用 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網絡與通信程序設計》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章