MFC 基于消息机机制的WSAAsyncSelect demo

1. 添加头文件

#include <winsock.h>

#pragma comment(lib, "ws2_32.lib")

 

服务端:

1. 定义变量

SOCKET sSocket;

2. 定义消息常量

#define MSG_SOCKET (WM_USER + 1000)

添加消息映射

BEGIN_MESSAGE_MAP(CXXXDlg, CDialog)
//{{AFX_MSG_MAP(CXXXDlg)
 //}}AFX_MSG_MAP
 ON_MESSAGE(MSG_SOCKET, OnSockEvent)
END_MESSAGE_MAP()

3. 初始化SOCKET

bool CXXXDlg::InitWSASocket()
{
   char szError[256] = {0};
   WORD wVersionRequested;
   WSADATA wsaData;                               
   wVersionRequested = MAKEWORD(2, 2);
   int ReturnInfo = WSAStartup(wVersionRequested, &wsaData);
  if (ReturnInfo != 0)
  {
    return false;
  }
  if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
  {
    WSACleanup();
    sprintf(szError, "初始化SOCKET错误,%d", WSAGetLastError());
    AfxMessageBox(szError);
    return false;
  }
   return true;
}

3. 初始化的时候调用InitWSASocket,如果初始化成功则开始监听。

BOOL CXXXDlg::OnInitDialog()
{
 CDialog::OnInitDialog();
 
 // Set the icon for this dialog.  The framework does this automatically
 //  when the application's main window is not a dialog
 SetIcon(m_hIcon, TRUE);   // Set big icon
 SetIcon(m_hIcon, FALSE);  // Set small icon
 
 // TODO: Add extra initialization here
 GetDlgItem(IDC_EDIT_PORT)->SetWindowText("8977");
 if (!InitWSASocket())
 {
  return FALSE;
 }
 if (!StartSocketListen())
 {
  OnCancel();
 }

 return TRUE;  // return TRUE  unless you set the focus to a control
}

 

4. 监听模块,注册事件

bool CXXXDlg::StartSocketListen()
{
 GetDlgItemText(IDC_EDIT_PORT, m_StrPort);
 if (m_StrPort.IsEmpty())
 {
  return false;
 }
 char szError[256] = {0};
 //创建套接字
 m_sSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 if(INVALID_SOCKET == m_sSocket)
 {
  sprintf(szError, "创建SOCKET错误,%d",  WSAGetLastError());
  AfxMessageBox(szError);
  return false;
 }
 //服务器地址信息
 SOCKADDR_IN addrSock;
 memset(&addrSock, 0x0, sizeof addrSock);
 addrSock.sin_addr.S_un.S_addr = inet_addr("127.0.0.1")/*htonl(INADDR_ANY)*/;
 addrSock.sin_family = AF_INET;
 addrSock.sin_port = htons(atoi(m_StrPort));
 //成功返回0 ,否则返回SOCKET_ERROR
 if (SOCKET_ERROR == ::bind(m_sSocket, (SOCKADDR*)&addrSock, sizeof(SOCKADDR)))
 {
  sprintf(szError, "绑定SOCKET错误,%d",  WSAGetLastError());
  AfxMessageBox(szError);
  return false;
 }
 //注册网络事件 
 if (SOCKET_ERROR == WSAAsyncSelect(m_sSocket, m_hWnd, MSG_SOCKET, FD_ACCEPT | FD_READ | FD_CLOSE))
 {
  ::closesocket(m_sSocket);
  sprintf(szError, "注册网络读取事件失败,%d",  WSAGetLastError());
  AfxMessageBox(szError);
  return false;
 }
 //成功返回0 ,否则返回SOCKET_ERROR
  if (SOCKET_ERROR == ::listen(m_sSocket, 100))
  {
   sprintf(szError, "监听SOCKET错误,%d",  WSAGetLastError());
   AfxMessageBox(szError);
   return false;
  }
 return true;
}

5. 当有客户端来连接的时候,系统发送消息触发以下事件。

说明:当客户端connect的时候触发服务端的FD_ACCEPT。客户端send数据的时候触发服务端的FD_READ

,客户端关闭的时候触发服务端的FD_CLOSE。

void CXXXDlg::OnSockEvent(WPARAM wParam,LPARAM lParam)
{
    SOCKET sSock= static_cast<SOCKET>(wParam);
 if(WSAGETSELECTERROR(lParam)) 
    { 
        AfxMessageBox("OnSockEvent()接收到错误消息"); 
        ::closesocket(wParam); 
        return; 
    } 
 switch(WSAGETSELECTEVENT(lParam))
 {
 case FD_ACCEPT:
  {
   SOCKADDR_IN addrSock;
   memset(&addrSock, 0x0, sizeof addrSock);
   int Len = sizeof(addrSock);
   //当客户端connect的时候全局的m_sSocket和wParam传进来的SOCKET是相同的,
   //当服务端调用accept去获取客户端的SOCKET传入下一次的OnSockEvent中wParam参数中
   m_cSocket = ::accept(m_sSocket, (SOCKADDR*)&addrSock, &Len);
   if (INVALID_SOCKET == m_cSocket)
   {
    char szError[256] = {0};
    sprintf(szError, "接收SOCKET错误,%d",  WSAGetLastError());
    AfxMessageBox(szError);
   }
  }
  break;
 case FD_READ:
  {
   char buff[1024] = {0};
   SOCKADDR_IN addrSock;
   memset(&addrSock, 0x0, sizeof addrSock);
   int Len = sizeof(addrSock);
   //recv不等待是因为你使用的是非阻塞socket,换而你使用阻塞socket一样需要等待。
   //recv的recvfrom是可以替换使用的,只是recvfrom多了两个参数,可以用来接收对端的地址信息,
   //这个对于udp这种无连接的,可以很方便地进行回复。
   //而换过来如果你在udp当中也使用recv,那么就不知道该回复给谁了,如果你不需要回复的话,也是可以使用的。
   //另外就是对于tcp是已经知道对端的,就没必要每次接收还多收一个地址,
   //没有意义,要取地址信息,在accept当中取得就可以加以记录了。
   ::recv(sSock, buff, 1024, 0);
//    ::recvfrom(m_cSocket, buff, 1024, 0, (SOCKADDR*)&addrSock, &Len);
   static_cast<CListBox *>(GetDlgItem(IDC_LIST_LOG))->AddString(buff);
   ::send(sSock/*m_cSocket*/, buff, 1024, 0);
  }     
  break;
 case FD_CLOSE:
  {
   ::shutdown(sSock, 2);
   ::closesocket(sSock);
  }
  break;
 }
}

5. 关闭程序的时候

CXXXDlg::~CXXXDlg()
{
 WSACleanup();
 ::shutdown(m_sSocket, 2);
 ::closesocket(m_sSocket);
}

 

客户端:

与服务端一样。定义SOCKET变量,添加消息常量,添加消息映射。初始化SOCKET,关闭的时候清理SOCKET(WSACleanup)

void CXXXDlg::OnButtonSend()
{
 char szError[256] = {0};
 SOCKET m_Socket;
 //创建套接字
 if (m_IsTCP)
 {
  m_Socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 }
 else
 {
  m_Socket = ::socket(AF_INET, SOCK_STREAM, 0);
 }
 if(INVALID_SOCKET == m_Socket)
 {
  sprintf(szError, "创建SOCKET错误,%d",  WSAGetLastError());
  AfxMessageBox(szError);
  return;
 }
 //服务器地址信息
 SOCKADDR_IN addrSock;
 addrSock.sin_addr.S_un.S_addr = inet_addr(m_StrIP)/*htonl(INADDR_ANY)*/;
 addrSock.sin_family = AF_INET;
 addrSock.sin_port = htons(atoi(m_StrPort));
 //连接服务器
 //成功返回0 ,否则返回SOCKET_ERROR
 if(SOCKET_ERROR == ::connect(m_Socket, (SOCKADDR*)&addrSock, sizeof(SOCKADDR)))
 {
  ::closesocket(m_Socket);
  sprintf(szError, "连接服务端错误,%d",  WSAGetLastError());
  AfxMessageBox(szError);
  return;
 }
 else
 {
  CString SendBuff;
  char RecvBuff[1024] = {0};
  GetDlgItemText(IDC_EDIT_MSG, SendBuff);
  ::send(m_Socket, SendBuff, SendBuff.GetLength(), 0);
  ::recv(m_Socket, RecvBuff, 1024, 0);
  static_cast<CListBox *>(GetDlgItem(IDC_LIST_TEXT))->AddString(RecvBuff);
 }
 closesocket(m_Socket);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值