用 WSAAsyncSelect 模型写一个局域网聊天室

其实网络编程一开始都搞过

只不过当时 精力都在CTF上面 一直想写一些小玩意===  但是都没有空--

所以现在打算抽两天空写一哈===

最后也算是写完了 

其实一开始打算用的是选择模型 最后感觉这个还是比较好玩的 就 打算用这个模型写一哈==

关于 各个模型的介绍

https://blog.csdn.net/qq_41071646/article/details/90414289

然后开始 ==

先说一下程序的大概流程

有一个服务端 来设置SOCKER 然后 进行信息的发送=

然后有客服端

进行信息的发送和接收

不过这里我倒是发现了一个bug   就是如果不主动发送信息的话 就一直接收不到信息 ==

等有空更新一哈==

服务端:

类的设计

class my_server
{
public:
	void init();
	void set();
	void S_accept();
	void recvdata();
	SOCKET m_SockServer, m_SockClient;
	SOCKET m_Clients[10];
	int    m_ConnectNum;
	char m_IP[100];
	UINT m_Port;
	HWND Hwnd;
};

这里其实都明了了=

下面是这些函数--

inline void my_server::init()
{
	m_SockServer = socket(AF_INET, SOCK_STREAM, 0);
	WSAAsyncSelect(m_SockServer, Hwnd, WM_SOCKET, FD_WRITE | FD_READ | FD_ACCEPT);
	m_ConnectNum = 0;
	for (int i = 0; i< 10; i++)
		m_Clients[i] = 0;
	
}

inline void my_server::set()
{
	char strport[100];
	GetDlgItemText(Hwnd, IDC_connect, m_IP, 98);
	OutputDebugStringA(m_IP);
	GetDlgItemText(Hwnd, IDC_Port, strport, 8);
	m_Port = atoi(strport);
	sockaddr_in serveraddr;
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.S_un.S_addr = inet_addr(m_IP);
	serveraddr.sin_port = htons(m_Port);
	if (bind(m_SockServer, (sockaddr*)&serveraddr, sizeof(serveraddr)))
	{
		MessageBox(NULL,"绑定地址失败.","警告",0);
		return;
	}
	else
	{
		MessageBox(NULL, "绑定地址成功.", "恭喜", 0);
	}
	listen(m_SockServer, 20);
}

inline void my_server::S_accept()
{
	SOCKADDR clientAddr;
	int clientAddr_size = sizeof(clientAddr);
	if (m_ConnectNum<10)
	{
		
		m_Clients[m_ConnectNum] = ::accept(m_SockClient, (SOCKADDR*)&clientAddr, &clientAddr_size);
		::WSAAsyncSelect(m_Clients[m_ConnectNum], Hwnd, WM_SOCKET, FD_READ | FD_WRITE | FD_CLOSE);
		m_ConnectNum++;
	}	
	else
	{
		SOCKET clientSock = accept(m_SockClient, (SOCKADDR*)&clientAddr, &clientAddr_size);
		char* str = "聊天室人数达到上线";
		send(clientSock, str, strlen(str) + sizeof(char), NULL);
		closesocket(clientSock);
	}
}

inline void my_server::recvdata()
{
	char buffer[1024];
	int num = -1;
	int curlink = -1;
	for (int i = 0; i < 10; i++)
	{
		num = recv(m_Clients[i], buffer, 1024, 0);
		if (num != -1)
		{
			curlink = i;
			break;
		}
	}
	buffer[num] = 0;
	for (int j = 0; j < m_ConnectNum; j++)
		if (j != curlink)
			send(m_Clients[j], buffer, num, 0);
	
	return;
}

然后在主函数里面

在程序初始化的时候我们把 WAS 类 初始化一下

回调函数里面的主要代码

	if (UMsg == WM_INITDIALOG)
	{
		WSADATA wsd;
		WSAStartup(MAKEWORD(2, 2), &wsd);
		Server.Hwnd = hwndDlg;
		Server.m_ConnectNum = 0;
		Server.init();
		
	}
	if (UMsg==WM_SOCKET)
	{
		Server.m_SockClient = wParam;
		// 查看是否出错
		if (WSAGETSELECTERROR(lParam))
		{
			::closesocket(Server.m_SockClient);
			return 0;
		}
		// 处理发生的事件
		switch (WSAGETSELECTEVENT(lParam))
		{
		case FD_ACCEPT:		// 监听中的套接字检测到有连接进入
		{

			Server.S_accept();

		}
		break;
		case FD_WRITE:
		{
		}
		break;
		case FD_READ:
		{
			Server.recvdata();
		}
		break;
		case FD_CLOSE:
		{
			::closesocket(Server.m_SockClient);
		}
		break;
		}
	}

	else if (WM_INPUT == UMsg)
	{

	}
	if (UMsg == WM_CLOSE)
	{
		WSACleanup();
		EndDialog(hwndDlg, NULL);
	}
	if (UMsg == WM_COMMAND)
	{
		if (wParam == IDSet)//点击的设置按钮
		{
			Server.set();
		    
		}
	}

 然后就是 客服端

类的设计

class client
{
public:
	void login();
	void Onsend();
	void recvdata();
	char  m_IP[100];
	UINT m_Port;
	HWND Hwnd;
	SOCKET m_SockClient;
	char name[100];
	HWND L_hwnd;
};

主要函数

inline void client::login()
{
	sockaddr_in serveraddr;
	char strport[100];
	GetDlgItemText(Hwnd, IDC_IP, m_IP, 98);
	OutputDebugStringA(m_IP);
	OutputDebugString("\n");
	GetDlgItemText(Hwnd, IDC_Port, strport, 8);
	m_Port = atoi(strport);
	OutputDebugStringA(strport);
	OutputDebugString("\n");
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.S_un.S_addr = inet_addr(m_IP);
	serveraddr.sin_port = htons(m_Port);
	if (connect(m_SockClient, (sockaddr*)&serveraddr, sizeof(serveraddr)) != 0)
	{
		MessageBox(NULL, "登陆失败.", "警告", 0);
		return;
	}
	else
	{
		MessageBox(NULL, "登陆成功.", "恭喜", 0);
	}
	::WSAAsyncSelect(m_SockClient, Hwnd, WM_SOCKET, FD_READ);
	char info[100];
	GetDlgItemText(Hwnd, IDC_Name, name, 98);
	wsprintf(info, "%s------>%s", name, "进入聊天室");
	send(m_SockClient, info, strlen(info) + sizeof(char), 0);/**/
}

inline void client::Onsend()
{
	char sendlist[100],info[100];
	GetDlgItemText(Hwnd, IDC_eGo, sendlist, 98);
	wsprintf(info, "%s说: %s", name, sendlist);
	send(m_SockClient, info, strlen(info) + sizeof(char), 0);
	L_hwnd = GetDlgItem(Hwnd, IDC_list);
	SendMessage(L_hwnd, LB_ADDSTRING, NULL, (LPARAM)info);
}

inline void client::recvdata()
{
	char buffer[1024];
	int num = recv(m_SockClient, buffer, 1024, 0);
	buffer[num] = 0;
	SendMessage(L_hwnd, LB_ADDSTRING, NULL, (LPARAM)buffer);
	
}

 回调函数里面的主要代码

	if (UMsg == WM_INITDIALOG)
	{
		WSADATA wsd;
		WSAStartup(MAKEWORD(2, 2), &wsd);
		Client.m_SockClient = socket(AF_INET, SOCK_STREAM, 0);
		Client.Hwnd = hwndDlg;
	}
	
	if (UMsg == WM_CLOSE)
	{
		WSACleanup();
		EndDialog(hwndDlg, NULL);
	}
	if (UMsg == WM_COMMAND)
	{
		if (wParam == IDC_Login)//点击的设置按钮
		{
			Client.login();
			
		}
		if (wParam == IDC_Go)
		{
			Client.Onsend();
		}
	}
	if (UMsg == WM_SOCKET)
	{
		
		switch (WSAGETSELECTEVENT(lParam))
		{
		case FD_ACCEPT:		// 监听中的套接字检测到有连接进入
		{

		

		}
		break;
		case FD_WRITE:
		{
		}
		break;
		case FD_READ:
		{
			Client.recvdata();
		}
		break;
		case FD_CLOSE:
		{
			
		}
		break;
		}
	}

 总体来说 代码很简单

也算是更加清晰的认清了 这个模型  感觉挺好玩的=== 能和消息机制结合到一块

参考资料

《Visual C++ 从入门到精通》

《Windows网络与通信程序设计》

发布了313 篇原创文章 · 获赞 44 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_41071646/article/details/101112873