|
基于套接字函数的网络聊天系统
利用套接字函数设计一个网络聊天系统。网络聊天系统分为两部分,即客户端和服务器端。客户端用于发送和显示数据,服务器端则用于转发客户端的数据。下面分别介绍客户端和服务器端的设计过程。 1.客户端设计过程如下: (1)创建一个基于对话框的工程,工程名称为“Client”。设计对话框资源如图1所示。 ![]() 图1 对话框设计窗口 (2)在对话框类的头文件中引用“winsock2.h”头文件,并导入“ws2_32.lib”库文件。 #include "winsock2.h" //引用头文件 #pragma comment (lib,"ws2_32.lib") //链接库文件 (3)在应用程序的InitInstance方法中初始化套接字。 WSADATA wsd; //定义WSADATA对象 WSAStartup(MAKEWORD(2,2),&wsd); //初始化套接字 (4)改写应用程序的ExitInstance虚方法,在应用程序结束时释放套接字资源。 int CClientApp::ExitInstance() { WSACleanup(); //释放套接字资源 return CWinApp::ExitInstance(); } (5)在对话框类中添加如下成员变量。 SOCKET m_SockClient; //定义一个套接字 UINT m_Port; //定义端口 CString m_IP; //定义IP (6)在对话框初始化时创建套接字。 m_SockClient = socket(AF_INET,SOCK_STREAM,0); //创建套接字 (7)向对话框中添加ReceiveInfo方法,用于接收服务器端发来的数据。 void CClientDlg::ReceiveInfo() { char buffer[1024]; //定义一个数据缓冲区 int num = recv(m_SockClient,buffer,1024,0); //接收数据 buffer[num] = 0; //定义结束标记 m_MsgList.AddString(buffer); //读取数据到列表中 } (8)在对话框的消息映射部分添加ON_MESSAGE消息映射宏,将自定义的消息与ReceiveInfo方法关联。 ON_MESSAGE(CM_RECEIVE,ReceiveInfo) //添加消息映射宏 (9)处理“登录”按钮的单击事件,开始登录服务器。 void CClientDlg::OnLogin() { sockaddr_in serveraddr; //服务器端地址 CString strport; //定义一个字符串,记录端口 m_ServerPort.GetWindowText(strport); //获取端口字符串 m_ServerIP.GetWindowText(m_IP); //获取IP if (strport.IsEmpty() || m_IP.IsEmpty()) //判断端口和IP是否为空 { MessageBox("请设置服务器IP和端口号"); //弹出提示对话框 return; } m_Port = atoi(strport); //将端口字符串转换为整数 serveraddr.sin_family = AF_INET; //设置服务器地址家族 serveraddr.sin_port = htons(m_Port); //设置服务器端口号 serveraddr.sin_addr.S_un.S_addr = inet_addr(m_IP); //设置服务器IP //开始连接服务器 if (connect(m_SockClient,(sockaddr*)&serveraddr,sizeof(serveraddr))!=0) { MessageBox("连接失败"); //弹出提示对话框 return; } else MessageBox("连接成功"); //弹出提示对话框 WSAAsyncSelect(m_SockClient,m_hWnd,1000,FD_READ); //设置异步模型 CString strname,info ; //定义字符串变量 m_NickName.GetWindowText(strname); //获取昵称 info.Format("%s------>%s",strname,"进入聊天室"); //设置发送的信息 send(m_SockClient,info.GetBuffer(0),info.GetLength(),0); //向服务器发送数据 } (10)处理“发送”按钮的单击事件,向服务器发送数据,再由服务器转发数据。 void CClientDlg::OnSend() { CString strData,name,info ; //定义字符串变量 m_NickName.GetWindowText(name); //获取昵称 m_SendData.GetWindowText(strData); //获取发送的数据 if (!name.IsEmpty() && !strData.IsEmpty()) //判断字符串是否为空 { info.Format("%s说: %s",name,strData); //格式化发送的数据 send(m_SockClient,info.GetBuffer(0),info.GetLength(),0); //开始发送数据 m_MsgList.AddString(info); //向列表中添加数据 m_SendData.SetWindowText(""); //清空编辑框文本 } } (11)运行程序,效果如图2、图3所示。 ![]() 图2 客户端窗口1 ![]() 图3 客户端窗口2 2.服务器端程序设计步骤如下: (1)创建一个基于对话框的工程,工程名称为“Server”,设计对话框资源如图4所示。 ![]() 图4 对话框资源设计 (2)在对话框类的头文件中引用“winsock2.h”头文件,并导入“ws2_32.lib”库文件。 #include "winsock2.h" //引用头文件 #pragma comment (lib,"ws2_32.lib") //链接库文件 (3)在应用程序的InitInstance方法中初始化套接字。 WSADATA wsd; //定义WSADATA对象 WSAStartup(MAKEWORD(2,2),&wsd); //初始化套接字 (4)改写应用程序的ExitInstance虚方法,在应用程序结束时释放套接字资源。 int CServerApp::ExitInstance() { WSACleanup(); //释放套接字资源 return CWinApp::ExitInstance(); } (5)向对话框类中添加如下成员变量。 SOCKET m_SockServer,m_SockClient; //定义套接字 SOCKET m_Clients[MAXNUM]; //客户端套接字 int m_ConnectNum; //当前连接的客户数量 CString m_IP; //定义IP UINT m_Port; //定义端口 (6)在对话框初始化时创建套接字。 m_SockServer = socket(AF_INET,SOCK_STREAM,0); //创建套接字 //将网络中的事件关联到窗口的消息函数中 WSAAsyncSelect(m_SockServer,m_hWnd,WM_USER+1,FD_WRITE|FD_READ|FD_ACCEPT); m_ConnectNum = 0; //初始化客户端连接数量 for (int i = 0; i< MAXNUM;i++) //初始化客户端套接字 m_Clients[i]= 0; (7)向对话框中添加TranslateData方法,接收客户端的连接,并转发客户端发来数据。 void CServerDlg::TranslateData() { sockaddr_in serveraddr; //定义一个网络地址 char buffer[1024]; //定义一个缓冲区 int len =sizeof(serveraddr); //获取网络地址大小 int curlink = -1; //定义整型变量 int num = -1; //定义整型变量 for (int i = 0; i < MAXNUM; i++) //遍历客户端套接字 { num= recv(m_Clients[i],buffer,1024,0); //获取客户端接收的数据 if (num != -1) //判断哪个客户端向服务器发送数据 { curlink = i; //记录客户端索引 break; //终止循环 } } buffer[num]= 0; //设置数据结束标记 if (num == -1) //接受客户端的连接 { if (m_ConnectNum < MAXNUM) //判断当前客户端连接数量是否大于上限 { //接受客户端的连接 m_Clients[m_ConnectNum] = accept(m_SockServer,(struct sockaddr*) &serveraddr,&len); m_ConnectNum++; //将连接数量加1 } return; } for (int j = 0; j < m_ConnectNum; j++) //将接收的数据发送给客户端 if (j != curlink) //不向发送方本身发送数据 send(m_Clients[j],buffer,num,0); } (8)在对话框的消息映射部分添加ON_MESSAGE消息映射宏,将消息与TranslateData方法关联。 ON_MESSAGE(WM_USER+1,TranslateData) (9)处理“设置”按钮的单击事件,绑定套接字到指定的地址上,使套接字处于监听模式。 void CServerDlg::OnSetting() { m_ServerIP.GetWindowText(m_IP); CString strPort; m_ServerPort.GetWindowText(strPort); if (m_IP.IsEmpty() || strPort.IsEmpty()) { MessageBox("请设置服务器IP和端口号","提示"); return; } 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("绑定地址失败."); return; } listen(m_SockServer,20); } (10)运行程序,效果如图5所示。 ![]() 图5 服务器设置窗口 |