本节中以一个实例,说明MFC WinSocket使用实例。此实例包含两个程序,一个是服务器端,一个是客户端。一个服务器可以接受多个客户端的连接,并可以向客户端发送数据。下面是服务器端套接字的源代码。
- class CSocketServer : public CAsyncSocket // 服务器端SOCKET
- {
- public:
- CSocketServer(); // 声明构造函数
- virtual ~CSocketServer(); // 声明析构函数
- public:
- void DeleteRemoteSocket(CSocketClient*
pSock); // 删除客户端连接- POSITION GetRemoteSocketPos(CSocketClient* pSock);
- // 获取指定客户端在链表中的位置
- CSocketClient* GetRemoteSocket(int pSock);
- // 获取SOCKET对应的客户端变量
- CPtrList m_clientList;
// 客户端SOCKET链表- HWND m_hMsgWnd;
// 接收消息的窗口句柄对象- };
上面是服务器端SOCKET的头文件,其中定义了CSocketServer类,此类继承自CAsyncSocket类。下面是此类的实现代码。
- CSocketServer::CSocketServer()
// 定义服务器端SOCKET类的构造函数- {
- }
- CSocketServer::~CSocketServer() // 定义服务
器端SOCKET类的析构函数- {
- while (!m_clientList.IsEmpty()) // 如果客户端链表不为空
- {
- CSocketClient* client=(CSocketClient*)m_
clientList.Remove Head();
// 获取首个客户端对象- Client->Close(); // 关闭客户端
- delete client; // 删除客户端
- }
- m_clientList.RemoveAll(); // 移除所有客户端
- if (m_hSocket!=INVALID_SOCKET) Close();
- // 如果SOCKET句柄有效,关闭
- }
- void CSocketServer::OnAccept(int nErrorCode) // 接收客户端回调函数
- {
- char* pLog=new char[200]; // 定义消息日志
- if (nErrorCode) // 如果发生错误
- {
- if (nErrorCode==WSAENETDOWN) // 如果错误是网络故障
- sprintf(pLog, "网络故障!"); // 显示网络故障消息提示框
- else sprintf(pLog, "FD_ACCEPT未知错误");
- // 否则,显示其他错误提示
- return;// 函数返回
- }
- else // 如果没有发生错误
- {
- sockaddr address; // 定义地址变量
- CString IPaddr; // 定义IP地址字符串
- UINT port; // 定义端口号
- int address_len; // 定义地址长度
- address_len=sizeof(address); // 获取地址长度
- CSocketClient* pSocket=new CSocketClient();
- // 创建CSocketClient对象
- pSocket->m_hMsgWnd=this->m_hMsgWnd;
- // 为对象的消息接收句柄变量赋值
- if (this->Accept(*pSocket,&address,&address_len))
- // 接收客户端连接,如果成功则
- {
- pSocket->AsyncSelect(FD_WRITE|FD_READ|FD_CLOSE);
- // 设置读写关闭事件
- pSocket->GetPeerName(IPaddr,port);
- // 获取客户端IP地址和端口号
- pSocket->m_strIP=IPaddr; // 赋值SOCKETIP地址参数
- m_clientList.AddTail(pSocket); // 将客户端对象增加到链表中
- sprintf(pLog, "接收客户端连接。IP=%s;端口=%d", IPaddr, port);
- }
- else // 接收客户端连接失败
- {
- int Error=GetLastError(); // 获取错误代码
- if (Error==WSAECONNREFUSED) // 如果错误为网络错误
- sprintf(pLog, "拒绝连接"); // 显示错误消息框
- else // 如果错误为其他错误
- {
- wsprintf(pLog,"WSAAccept失败,错误代码: %d",Error);
- // 格式化消息串
- }
- delete pSocket; // 删除SOCKET对象
- return; // 函数返回
- }
- }
- CAsyncSocket::OnAccept(nErrorCode); // 调用基类的OnAccept()函数
- }
- void CSocketServer::OnClose(int nErrorCode)
// 关闭SOCKET的回调函数- {
- while (!m_clientList.IsEmpty()) // 如果客户端链表不为空
- {
- CSocketClient* client=(CSocketClient*)m_
clientList.RemoveHead(); // 获取首个客户端对象- Client->Close(); // 关闭客户端
- delete client; // 删除客户端
- }
- m_clientList.RemoveAll(); // 移除所有客户端
- if (m_hSocket!=INVALID_SOCKET) Close();
- // 如果SOCKET句柄有效,关闭
- CAsyncSocket::OnClose(nErrorCode);
// 调用基类的OnClose()函数- }
- void CSocketServer::DeleteRemoteSocket(CSocketClient* pSock)
- // 删除指定客户端连接对象
- {
- pSock->Close(); // 关闭SOCKEt连接
- POSITION pos=m_clientList.GetHeadPosition();
- // 获取客户端对象链表中的第一个对象位置
- POSITION temp; // 定义临时变量,用于存放位置
- while(pos!=NULL) // 如果首个客户端对象的位置有效
- {
- temp=pos; // 保存首个客户端对象的位置
- // 定义到下一个位置,并返回客户端对象
- CSocketClient* client= (CSocketClient*)m_
clientList.GetNext(pos);- if (client==pSock) // 如果返回的客户端对象与参数一致
- {
- m_clientList.RemoveAt(temp); // 移除客户端对象
- client->Close(); // 关闭SOCKEt连接
- delete client; // 删除客户端
- break; // 退出循环
- }
- }
- return ;// 处理完,函数返回
- }
上面代码是处理服务器端SOCKET的实现,主要处理ACCEPT事件,接收到OnAccept事件后,会接收客户端连接并存入链表。下面代码是客户端SOCKET的实现。
- CSocketClient::CSocketClient()
// 客户端SOCKET类的构造函数- {
- m_nLength=0;
// 数据长度初始化为0- memset(m_szReceBuf,0,sizeof(m_szReceBuf));
// 初始化接收缓冲区- memset(m_szSendBuf,0,sizeof(m_szSendBuf));
// 初始化发送缓冲区- m_bConnect=false;
// 初始化连接状态变量为false- m_hWnd=NULL;
// 初始化窗口句柄为NULL- m_strHost.Empty();
// 初始化主机字符串为空- m_strIP.Empty();
// 初始化IP字符串为空- }
- CSocketClient::~CSocketClient()
// 客户端SOCKET类的析构函数- {
- if (m_hSocket!=INVALID_SOCKET) Close();
// 如果SOCKET句柄有效,则关闭- }
- void CSocketClient::OnSend(int nErrorCode) // 发送数据
- {
- int nSendBytes = Send(m_szSendBuf,strlen(m_szSendBuf),0);
- // 发送发送缓冲区中的数据
- char* pLog=new char[200]; // 定义消息变量
- sprintf(pLog, "服务器发送%d个数据", nSendBytes);
// 格式化消息内容- if (m_hWnd!=NULL)
// 如果窗体不为空,发送日志显示消息- m_hWnd->SendMessage(WM_SOCKET_LOG,
(WPARAM)pLog, strlen(pLog));- memset(m_szSendBuf,0,sizeof(m_szSendBuf));
// 重新初始化发送缓冲区- AsyncSelect(FD_READ|FD_CLOSE); // 设置读和关闭事件
- }
- void CSocketClient::OnReceive(int nErrorCode)
// 接收数据- {
- m_nLength=Receive((void*)m_szReceBuf,MAXSOCKBUF,0); // 接收数据
- m_szReceBuf[m_nLength]=0;
// 设置接收缓冲区最后一位为0- char* recvBuf = new char[MAXSOCKBUF]; // 定义数组变量
- sprintf(recvBuf,(const char*)&m_szReceBuf, m_nLength);
- // 复制接收的数据
- if (m_hWnd!=NULL) // 如
果日志窗体不为空,发送日志显示消息- m_hWnd->SendMessage( WM_SOCKET_RECEIVE,
(WPARAM)recvBuf, strlen (recvBuf));- memset(m_szReceBuf,0,sizeof(m_szReceBuf));
// 重新初始化接收缓冲区- CAsyncSocket::OnReceive(nErrorCode); // 调用
基类的OnReceive()函数- }
- void CSocketClient::OnConnect(int nErrorCode) // 连接函数
- {
- char* pLog=new char[200]; // 定义消息变量
- if (nErrorCode == 0) // 如
果错误代码为0,即没发生错误- {
- sprintf(pLog, "连接服务器成功"); // 复制
消息提示道消息变量中- m_bConnect = true; // 设置
连接状态变量为true- }
- else sprintf(pLog, "连接服务器失败,错误代码=%d", nErrorCode);
- // 格式化错误提示信息
- if (m_hWnd!=NULL) // 如果日
志窗体不为空,发送日志显示消息- m_hWnd->SendMessage(WM_SOCKET_LOG,
(WPARAM)pLog, strlen(pLog));- }
- void CSocketClient::Init() // 客户端SOCKET初始化
- {
- memset(m_szReceBuf,0,sizeof(m_szReceBuf));
// 初始化接收缓冲区- m_nLength=MAXSOCKBUF; // 设
置每次接收的数据长度- }
上面代码分别处理了客户端SOCKET的函数,主要处理客户端SOCKET连接、发送数据、接收数据和关闭连接等操作。客户端程序、服务器程序的运行效果分别如图16-2、图16-3所示。
(点击查看大图)图16-2 客户端SOCKET运行效果图 |