18.3.3 使用套接字函数设计网络聊天室
在18.3.2节中笔者介绍了常用的套接字函数,接下来将利用这些套接字函数设计一个网络聊天室程序。程序由两个实例组成,第1个实例为服务器端,负责接收用户的连接请求,并转发用户信息。第2个实例为客户端,负责连接服务器并发送信息。
服务器端程序设计步骤如下:
ch1806实例位置:mr\18\sl\06
(1)创建一个基于对话框的应用程序,设计对话框资源如图18.11所示。
(2)在对话框的头文件中引用“winsock2.h”头文件,并导入网络库文件。
#include "winsock2.h" #pragma comment (lib,"ws2_32.lib")
|
(3)在应用程序的InitInstance方法中初始化套接字。
WSADATA wsd; WSAStartup(MAKEWORD(2,2),&wsd);
|
(4)在对话框类中定义成员变量,记录服务器套接字和与之连接的客户端套接字信息。
SOCKET m_server,m_client; SOCKET m_Clients[MAXNUM]; //客户端套接字 int m_CurClient; //当前连接的客户数量
|
(5)在对话框类的OnInitDialog方法中创建套接字,并初始化数据。
//创建套接字 m_server = socket(AF_INET,SOCK_STREAM,0); //将网络中的事件关联到窗口的消息函数中 WSAAsyncSelect(m_server,m_hWnd,20000,FD_WRITE|FD_READ|FD_ACCEPT); m_client = 0; m_serverIP = ""; for (int i = 0; i< MAXNUM;i++) m_Clients[i]= 0; m_CurClient = 0;
|
(6)处理“监听”按钮的单击事件,将套接字绑定到本机地址,并开始监听套接字。
void CServerDlg::OnOK() { //服务器端地址 sockaddr_in serveraddr;serveraddr.sin_family = AF_INET; m_IP.GetWindowText(m_serverIP); //设置本机地址 serveraddr.sin_addr.S_un.S_addr = inet_addr(m_serverIP); UpdateData(TRUE); //设置端口号 serveraddr.sin_port = htons(m_port); //绑定地址 if (bind(m_server,(sockaddr*)&serveraddr,sizeof(serveraddr))) { MessageBox("绑定地址失败."); return; } //开始监听 listen(m_server,50); }
|
(7)向对话框中添加HandleData方法,用于接受客户端的连接,并获得客户端传来的数据,将其转发给其他客户端。
void CServerDlg::HandleData() { sockaddr_in serveraddr; char buffer[1024]; int len =sizeof(serveraddr);
//接收客户端的数据 int curlink = -1;int num = -1; for (int p = 0;p< MAXNUM; p++) { num= recv(m_Clients[p],buffer,1024,0); if (num != -1) { curlink = p; break; } } buffer[num]= 0; if (num==-1) //接受客户端的连接 { if (m_CurClient < MAXNUM) { m_Clients[m_CurClient] = accept(m_server,(struct sockaddr*) &serveraddr,&len); m_CurClient+=1; } return; } //将接收的数据发送给客户端 for (int j = 0;j< m_CurClient;j++) if (j != curlink) send(m_Clients[j],buffer,num,0); }
|
(8)改写对话框类的PreTranslateMessage方法,在服务器套接字中有事件触发时调用HandleData方法。
BOOL CServerDlg::PreTranslateMessage(MSG* pMsg) { if (pMsg->message==20000) { HandleData(); return TRUE; } else return CDialog::PreTranslateMessage(pMsg); }
|
客户端程序设计步骤如下。
ch1807实例位置:mr\18\sl\07
(1)创建一个基于对话框的工程,设置对话框资源如图18.12所示。
图18.12 客户端设计窗口
(2)在对话框类的头文件中引用“winsock2.h”头文件,并导入“ws2_32.lib”库文件。
#include "winsock2.h" #pragma comment (lib,"ws2_32.lib")
|
(3)在应用程序的InitInstance方法中初始化套接字。
WSADATA wsd; WSAStartup(MAKEWORD(2,2),&wsd);
|
(4)在对话框的OnInitDialog方法中创建套接字。
m_client = socket(AF_INET,SOCK_STREAM,0);
|
(5)处理“连接”按钮的单击事件,连接服务器,并设置套接字接收数据时触发的消息。
void CClientDlg::OnOK() { //服务器端地址 sockaddr_in serveraddr;UpdateData(TRUE); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(m_port); serveraddr.sin_addr.S_un.S_addr = inet_addr(m_IP); if (connect(m_client,(sockaddr*)&serveraddr,sizeof(serveraddr))!=0) { MessageBox("连接失败"); return; } else MessageBox("连接成功"); WSAAsyncSelect(m_client,m_hWnd,1000,FD_READ); CString str,info ; m_name.GetWindowText(str); info.Format("%s------>%s",str,"进入聊天室"); int i = send(m_client,info.GetBuffer(0),info.GetLength(),0); }
|
(6)处理“发送”按钮的单击事件,向服务器发送数据。
void CClientDlg::OnButton1() { CString str,name,info ; m_name.GetWindowText(name); m_info.GetWindowText(str); if (!name.IsEmpty()&&!str.IsEmpty()) { info.Format("%s说: %s",name,str); //开始发送数据 int i = send(m_client,info.GetBuffer(0),info.GetLength(),0); m_list.AddString(info); m_info.SetWindowText(""); } }
|
(7)向对话框类中添加ReceiveData方法,用于接收从服务器传来的数据。
void CClientDlg::ReceiveData(){ char buffer[1024]; //接收服务器端传来的数据, int num = recv(m_client,buffer,1024,0); buffer[num] = 0; //将接收的数据添加到列表框中 m_list.AddString(buffer); }
|
(8)改写对话框的PreTranslateMessage方法,截获对话框的消息,用于接收数据。
BOOL CClientDlg::PreTranslateMessage(MSG* pMsg) { if (pMsg->message==1000) { ReceiveData(); return TRUE; } else return CDialog::PreTranslateMessage(pMsg); }
|
运行程序,效果如图18.13、图18.14、图18.15所示。
图18.13 聊天室服务器
图18.14 客户端窗口1
图18.15 客户端窗口2
【责任编辑:
阚书 TEL:(010)68476606】