网络安全编程:非阻塞模式开发

安全
Winsock套接字的工作模式有两种,分别是阻塞模式(同步模式)和非阻塞模式(异步模式)。阻塞模式下的Winsock函数会将程序的某个线程处于“等待”状态。本文介绍异步模式的Winsock编程。

[[380553]]

 Winsock套接字的工作模式有两种,分别是阻塞模式(同步模式)和非阻塞模式(异步模式)。阻塞模式下的Winsock函数会将程序的某个线程(如果程序中只有一个主线程,那么会导致整个程序处于“等待”状态)处于“等待”状态。非阻塞模式的Winsock函数不会发生需要等待的情况。在异步模式下,当一个函数执行后会立刻返回,即使是操作没有完成也会返回;当函数执行完成时,会以某种方式通知应用程序。显然,异步模式更适合于Windows下的开发。本文介绍异步模式的Winsock编程。

当一个套接字通过socket()函数创建后,默认工作在阻塞模式下。为了使得套接字工作在非阻塞模式状态下,就需要对套接字进行设置,将其改编为非阻塞模式。改变套接字工作模式的方法有多种,为了基于Windows应用程序的消息驱动机制,这里只介绍常用的改变套接字的函数。该函数是WSAAsyncSelect()函数,其定义如下: 

  1. int WSAAsyncSelect(  
  2.  SOCKET s,  
  3.  HWND hWnd,  
  4.  unsigned int wMsg,
  5.  long lEvent  
  6. ); 

WSAAsyncSelect()函数会把套接字设置为非阻塞模式,该函数会绑定指定套接字到一个窗口。当该套接字有网络事件发生时,会向绑定窗口发送相应的消息。该函数的参数含义说明如下。

S:指定要改变工作模式为非阻塞模式的套接字。

hWnd:指定当发生网络事件时接收消息的窗口。

wMsg:指定当网络事件发生时向窗口发送的消息。该消息是一个自定义消息,定义自定义消息的方法是在 WM_USER 的基础上加一个数值,比如(WM_USER + 1)。

lEvent:指定应用程序感兴趣的通知码。它可以被指定为多个通知码的组合。常用的通知码有 FD_READ(套接字收到对端发来的数据包)、FD_ACCEPT(监听中的套接字有连接请求)、FD_CONNECT(套接字成功连接到对方)和 FD_CLOSE(套接字对应的连接被关闭)。在指定通知码时不需要全部将其指定。对于基于 TCP 协议的客户端来说,FD_ACCEPT 是没有意义的;对于基于 TCP 的服务端来说,FD_CONNECT 是没有意义的;对于基于 UDP 协议的客户端和服务器端来说,FD_ACCEPT、FD_CONNECT 和 FD_CLOSE 都是没有意义的。

在了解如何将套接字设置为非阻塞模式以后,这里完成一个简单的远程控制工具。这里要编写的远程控制工具是基于C/S模式的,即客户端/服务器端模式的架构。客户端通过发送控制命令,操作服务器端接收到控制命令后响应相应的事件,完成特定的功能。

这个远程控制的服务器端只简单实现以下几个功能。

  •  向客户端发送帮助信息。
  •  将服务器信息发送给客户端。
  •  交换鼠标的左右键和恢复鼠标的左右键。
  •  打开光驱和关闭光驱。

1. 远程控制软件框架设计

远程控制分为控制端和被控制端,控制端通常为客户端,而被控制端通常为服务器端。对于客户端来说,它需要3种通知码,即FD_CONNECT、FD_CLOSE和FD_READ。对于服务器端来说,它需要3种通知码,即FD_ACCEPT、FD_CLOSE和FD_READ,如图1所示。

图1  服务器端和客户端通信

这里解释一下图1,并对它的框架设计进行补充。对于服务器端(Server端)来说,它需要处于监听状态等待客户端(Client端)发起的连接(FD_ACCEPT),在连接后会等待接收客户端发来的控制命令(FD_READ),当客户端断开连接后就可以结束此次通信了(FD_CLOSE)。对于客户端来说,它需要等待确认连接是否成功(FD_CONNET);当连接成功后就可以向服务器端发送控制命令,并等待接收命令响应结果(FD_READ);当服务器端被关闭后,通信则强制被结束了(FD_CLOSE)。因此,服务器端需要的通知码有FD_ACCEPT、FD_READ和FD_CLOSE,客户端需要的通知码有FD_CONNECT、FD_READ和FD_CLOSE。

客户端向服务器端发送的命令为“字符串”类型的数据。当服务器接收到客户端发来的命令后,需要判断命令,然后执行相应的功能。

服务器向客户端反馈的执行结果可能为字符串,也可能为其他的数据结构类型的内容。由于反馈数据的格式无法确定,那么对于服务器向客户端反馈的信息必须做特殊的标记,通过标记判断发送的数据格式。而客户端接收到服务器端发来的数据后,必须对格式进行解析,以便正确读取服务器端返回的命令反馈结果。服务器端的反馈数据协议格式如图2所示。

图2  服务器端反馈数据协议格式

从图2可以看出,服务器对于客户端的反馈数据协议格式有3部分内容,第1部分bType用于区分是文本数据和特定数据结构的数据,第2部分bClass用于区分不同的特定数据结构,第3部分szValue是真正的数据部分。对于服务器反馈的数据,如果是文本数据,那么客户端直接将szValue中的字符串显示输出;如果反馈的是特定的数据结构,则必须区分是何种数据结构,最后按照直接的数据结构解析szValue中的数据。将该协议格式定义为数据结构体,如下: 

  1. #define TEXTMSG 't' // 表示文本信息  
  2. #define BINARYMSG 'b' // 表示特定的数据结构  
  3. typedef struct _DATA_MSG  
  4.  
  5.   BYTE bType; // 数据的类型  
  6.   BYTE bClass; // 数据类型的补充  
  7.   char szValue[0x200]; // 数据的信息  
  8. }DATA_MSG, *PDATA_MSG; 

2. 远程控制软件代码要点

前面介绍了WSAAsyncSelect()函数原型和参数的含义,下面具体介绍WSAAsyncSelect()函数的使用。WSAAsyncSelect()函数在使用时会将指定的套接字、窗口句柄、自定义消息和通知码关联在一起,使用如下: 

  1. // 初始化 Winsock 库  
  2. WSADATA wsaData;  
  3. WSAStartup(MAKEWORD(2, 2), &wsaData);  
  4. // 创建套接字并将其设置为非阻塞模式  
  5. m_ListenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);  
  6. WSAAsyncSelect(m_ListenSock, GetSafeHwnd(), UM_SERVER, FD_ACCEPT); 

在代码的WSAAsyncSelect()函数中,第1个参数是新创建的用于监听的套接字m_ListenSock,第2个参数使用MFC的成员函数GetSafeHwnd()来得到当前窗体的句柄,第3个参数UM_SERVER是一个自定义的类型,最后一个参数FD_ACCEPT是该套接字要接收的通知码。函数中的第3个参数是一个自定义的消息。在服务器端,该消息的定义如下: 

  1. #define UM_SERVER (WM_USER + 200) 

当有客户端与服务器端连接时,系统会发送UM_SERVER消息到与监听套接字关联的句柄指定的窗口。当窗口收到该消息后,需要对该消息进行处理。该处理函数也需要手动进行添加,添加有3处地方。

第1处是在类定义中添加,代码如下: 

  1. // 生成的消息映射函数  
  2. //{{AFX_MSG(CServerDlg)  
  3. virtual BOOL OnInitDialog();  
  4. afx_msg void OnSysCommand(UINT nID, LPARAM lParam);  
  5. afx_msg void OnPaint(); 
  6. afx_msg HCURSOR OnQueryDragIcon();  
  7. afx_msg VOID OnSock(WPARAM wParam, LPARAM lParam);  
  8. afx_msg void OnClose();  
  9. //}}AFX_MSG  
  10. DECLARE_MESSAGE_MAP() 

在这里添加afx_msg VOID OnSock(WPARAM wParam, LPARAM lParam);

第2处在类实现中添加对应的函数实现代码,如下: 

  1. VOID CServerDlg::OnSock(WPARAM wParam, LPARAM lParam)  
  2.  

第3处是要添加消息映射,代码如下: 

  1. BEGIN_MESSAGE_MAP(CServerDlg, CDialog)  
  2. //{{AFX_MSG_MAP(CServerDlg)  
  3. ON_WM_SYSCOMMAND()  
  4. ON_WM_PAINT()  
  5. ON_WM_QUERYDRAGICON()  
  6. ON_MESSAGE(UM_SERVER, OnSock)  
  7. ON_WM_CLOSE()  
  8. //}}AFX_MSG_MAP  
  9. END_MESSAGE_MAP() 

在这里添加ON_MESSAGE(UM_SERVER, OnSock)。

通过以上3步,在程序中就可以接收并响应对UM_SERVER消息的处理。

3. 远程控制界面布局

首先来看远程控制客户端与服务器端的窗口界面,如图3所示。

图3  远程控制端与服务器端界面布局

在图3中,SERVER表示服务器端,Client表示客户端。服务器端(Server)运行在虚拟机中,客户端(Client)运行在物理机中。通过图3可以看出,物理机中客户端与服务器端是可以正常进行通信的。

服务器端的软件只有一个用于显示多行文本的编辑框。该界面比较简单。

客户端软件在IP地址后的编辑框中输入服务器端的IP地址,然后单击“连接”按钮,客户端会与远端的服务器进行连接。当连接成功后,输入IP地址的编辑框会处于只读状态,“连接”按钮变为“断开连接”按钮。对于发送命令后的编辑框变为可用状态,“发送”按钮也变为可用状态。

对于软件界面的布局,大家可以自行调整。

4. 服务器端代码的实现

当服务器启动时,需要创建套接字,并将套接字设置为异步模式,绑定IP地址和端口号并使其处于监听状态,代码如下: 

  1. BOOL CServerDlg::OnInitDialog()  
  2.  
  3.   ……  
  4.   // 添加其他初始化代码  
  5.   // 初始化 Winsock 库  
  6.   WSADATA wsaData;  
  7.   WSAStartup(MAKEWORD(2, 2), &wsaData);  
  8.   // 创建套接字并将其设置为非阻塞模式  
  9.   m_ListenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 
  10.   WSAAsyncSelect(m_ListenSock, GetSafeHwnd(), UM_SERVER, FD_ACCEPT);  
  11.   sockaddr_in addr;  
  12.   addr.sin_family = AF_INET 
  13.   addr.sin_addr.S_un.S_addr = ADDR_ANY 
  14.   addr.sin_port = htons(5555);  
  15.   // 绑定 IP 地址及 5555 端口,并处于监听状态  
  16.   bind(m_ListenSock, (SOCKADDR*)&addr, sizeof(addr));  
  17.   listen(m_ListenSock, 1);  
  18.   return TRUE; // return TRUE unless you set the focus to a control  

当客户端与服务器端进行连接时,需要处理通知码FD_ACCEPT,并且创建与客户端进行通信的新的套接字。对于新的套接字也需要设置为异步模式,并且需要设置FD_READ和FD_CLOSE两个通知码。代码如下: 

  1. VOID CServerDlg::OnSock(WPARAM wParam, LPARAM lParam)  
  2.  
  3.   if ( WSAGETSELECTERROR(lParam) )  
  4.   {  
  5.     return ;  
  6.   }  
  7.   switch ( WSAGETSELECTEVENT(lParam))  
  8.   {  
  9.     // 处理 FD_ACCEPT  
  10.   case FD_ACCEPT:  
  11.     {  
  12.       sockaddr_in ClientAddr;  
  13.       int nSize = sizeof(ClientAddr);  
  14.       m_ClientSock = accept(m_ListenSock, (SOCKADDR*)&ClientAddr, &nSize);  
  15.       WSAAsyncSelect(m_ClientSock, GetSafeHwnd(), UM_SERVER, FD_READ | FD_CLOSE);  
  16.       m_StrMsg.Format("请求地址是%s:%d",  
  17.         inet_ntoa(ClientAddr.sin_addr), ntohs(ClientAddr.sin_port));  
  18.       DATA_MSG DataMsg;  
  19.       DataMsg.bType = TEXTMSG 
  20.       DataMsg.bClass = 0 
  21.       lstrcpy(DataMsg.szValue, HELPMSG);  
  22.       send(m_ClientSock, (const char *)&DataMsg, sizeof(DataMsg), 0);  
  23.       break;  
  24.     }  
  25.     // 处理 FD_READ  
  26.   case FD_READ:  
  27.     { 
  28.       char szBuf[MAXBYTE] = { 0 };  
  29.       recv(m_ClientSock, szBuf, MAXBYTE, 0);  
  30.       DispatchMsg(szBuf);  
  31.       m_StrMsg = "对方发来命令: " 
  32.       m_StrMsg += szBuf; 
  33.        break; 
  34.     }  
  35.     // 处理 FD_CLOSE  
  36.   case FD_CLOSE:  
  37.     {  
  38.       closesocket(m_ClientSock);  
  39.       m_StrMsg = "对方关闭连接" 
  40.       break;  
  41.     }  
  42.   }  
  43.   InsertMsg();  

在代码中,当响应FD_READ通知码时会接收客户端发来的命令,并通过DispatchMsg()函数处理客户端发来的命令。在OnSock()函数的最后有一个InsertMsg()函数,该函数用于将接收的命令显示到界面上对应的消息编辑框中。

DispatchMsg()函数用于处理客户端发来的命令,该代码如下: 

  1. VOID CServerDlg::DispatchMsg(char *szBuf)  
  2.  
  3.   DATA_MSG DataMsg;  
  4.   ZeroMemory((void*)&DataMsg, sizeof(DataMsg));  
  5.   if ( !strcmp(szBuf, "help") )  
  6.   {  
  7.     DataMsg.bType = TEXTMSG 
  8.     DataMsg.bClass = 0 
  9.     lstrcpy(DataMsg.szValue, HELPMSG);  
  10.   }  
  11.   else if ( !strcmp(szBuf, "getsysinfo"))  
  12.   {  
  13.     SYS_INFO SysInfo;  
  14.     GetSysInfo(&SysInfo);  
  15.     DataMsg.bType = BINARYMSG 
  16.     DataMsg.bClass = SYSINFO 
  17.     memcpy((void *)DataMsg.szValue, (const char *)&SysInfo, sizeof(DataMsg));  
  18.   }  
  19.   else if ( !strcmp(szBuf, "open") )  
  20.   {  
  21.     SetCdaudio(TRUE);  
  22.     DataMsg.bType = TEXTMSG 
  23.     DataMsg.bClass = 0 
  24.     lstrcpy(DataMsg.szValue, "open 命令执行完成");  
  25.   }  
  26.   else if ( !strcmp(szBuf, "close") )  
  27.   {  
  28.     SetCdaudio(FALSE);  
  29.     DataMsg.bType = TEXTMSG 
  30.     DataMsg.bClass = 0 
  31.     lstrcpy(DataMsg.szValue, "close 命令执行完成");  
  32.   }  
  33.   else if ( !strcmp(szBuf, "swap") )  
  34.   {  
  35.     SetMouseButton(TRUE);  
  36.     DataMsg.bType = TEXTMSG 
  37.     DataMsg.bClass = 0 
  38.     lstrcpy(DataMsg.szValue, "swap 命令执行完成");  
  39.   }  
  40.   else if ( !strcmp(szBuf, "restore") )  
  41.   {  
  42.     SetMouseButton(FALSE);  
  43.     DataMsg.bType = TEXTMSG 
  44.     DataMsg.bClass = 0 
  45.     lstrcpy(DataMsg.szValue, "restore 命令执行完成");  
  46.   }  
  47.   else 
  48.   {  
  49.     DataMsg.bType = TEXTMSG 
  50.     DataMsg.bClass = 0 
  51.     lstrcpy(DataMsg.szValue, "无效的指令");  
  52.   }  
  53.   // 发送命令执行情况给客户端  
  54.   send(m_ClientSock, (const char *)&DataMsg, sizeof(DataMsg), 0);  

在DispatchMsg()函数中,通过if()…else if()…else()比较客户端发来的命令执行相应的功能,并将执行的结果发送给客户端。

命令功能的实现函数如下: 

  1. VOID CServerDlg::GetSysInfo(PSYS_INFO SysInfo)  
  2.  
  3.   unsigned long nSize = 0 
  4.   SysInfo->OsVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);  
  5.   GetVersionEx(&SysInfo->OsVer);  
  6.   nSize = NAME_LEN 
  7.   GetComputerName(SysInfo->szComputerName, &nSize);  
  8.   nSize = NAME_LEN 
  9.   GetUserName(SysInfo->szUserName, &nSize);  
  10.  
  11. VOID CServerDlg::SetCdaudio(BOOL bOpen)  
  12.  
  13.   if ( bOpen )  
  14.   {  
  15.     // 打开光驱  
  16.     mciSendString("set cdaudio door open", NULL, NULL, NULL);  
  17.   }  
  18.   else  
  19.   {  
  20.     // 关闭光驱  
  21.     mciSendString("set cdaudio door closed", NULL, NULL, NULL);  
  22.   }  
  23.  
  24. VOID CServerDlg::SetMouseButton(BOOL bSwap)  
  25.  
  26.   if ( bSwap)  
  27.   {  
  28.     // 交换  
  29.     SwapMouseButton(TRUE);  
  30.   }  
  31.   else  
  32.   {  
  33.   // 恢复  
  34.   SwapMouseButton(FALSE);  
  35.   }  

这里面对于getsysinfo命令,需要定义一个结构体,具体如下: 

  1. #define HELPMSG "帮助信息: \r\n" \  
  2.  "\t help : 显示帮助菜单 \r\n" \  
  3.  "\t getsysinfo : 获得对方主机信息\r\n" \ 
  4.  "\t open : 打开光驱 \r\n" \  
  5.  "\t close : 关闭光驱 \r\n" \  
  6.  "\t swap : 交换鼠标左右键 \r\n" \  
  7.  "\t restore : 恢复鼠标左右键" \  
  8. #define NAME_LEN 20  
  9. typedef struct _SYS_INFO  
  10.  
  11.   OSVERSIONINFO OsVer; // 保存操作系统信息  
  12.   char szComputerName[NAME_LEN]; // 保存计算机名  
  13.   char szUserName[NAME_LEN]; // 保存当前登录名  
  14. }SYS_INFO, *PSYS_INFO; 

该结构体不是文本类型的数据,需要在反馈协议中填充bClass字段。对于getsysinfo命令,该bClass字段填充的内容为“SYSINFO”。SYSINFO的定义如下: 

  1. #define SYSINFO 0x01L 

调用mciSendString()函数需要添加头文件和库文件,具体如下: 

  1. #include <mmsystem.h>  
  2. #pragma comment (lib, "Winmm") 

至此,服务器端的主要功能就介绍完了,最后还有两个函数没有列出,分别是InsertMsg()函数和释放Winsock库的部分,代码如下: 

  1. void CServerDlg::OnClose()  
  2.  
  3.   // 添加处理程序代码或调用默认方法  
  4.   // 关闭监听套接字,并释放 Winsock 库  
  5.   closesocket(m_ClientSock);  
  6.   closesocket(m_ListenSock);  
  7.   WSACleanup();  
  8.   CDialog::OnClose();  
  9.  
  10. VOID CServerDlg::InsertMsg()  
  11.  
  12.   CString strMsg;  
  13.   GetDlgItemText(IDC_MSG, strMsg);  
  14.   m_StrMsg += "\r\n"; 
  15.   m_StrMsg += "----------------------------------------\r\n";  
  16.   m_StrMsg += strMsg;  
  17.   SetDlgItemText(IDC_MSG, m_StrMsg);  
  18.   m_StrMsg = "" 

5. 客户端代码的实现

客户端的代码基本与服务端的代码类似,这里就不再说明。

连接远程服务器的代码如下: 

  1. void CClientDlg::OnBtnConnect()  
  2.  
  3.   // 添加处理程序代码  
  4.   char szBtnName[10] = { 0 };  
  5.   GetDlgItemText(IDC_BTN_CONNECT, szBtnName, 10);  
  6.   // 断开连接  
  7.   if ( !lstrcmp(szBtnName, "断开连接") )  
  8.   {  
  9.     SetDlgItemText(IDC_BTN_CONNECT, "连接");  
  10.     (GetDlgItem(IDC_SZCMD))->EnableWindow(FALSE);  
  11.     (GetDlgItem(IDC_BTN_SEND))->EnableWindow(FALSE);  
  12.     (GetDlgItem(IDC_IPADDR))->EnableWindow(TRUE);  
  13.     closesocket(m_Socket);  
  14.     m_StrMsg = "主动断开连接" 
  15.     InsertMsg();  
  16.     return ;  
  17.   }  
  18.   // 连接远程服务器端  
  19.   char szIpAddr[MAXBYTE] = { 0 };  
  20.   GetDlgItemText(IDC_IPADDR, szIpAddr, MAXBYTE);  
  21.   m_Socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);  
  22.   WSAAsyncSelect(m_Socket,GetSafeHwnd(),UM_CLIENT, FD_READ | FD_CONNECT | FD_CLOSE);  
  23.   sockaddr_in ServerAddr;  
  24.   ServerAddr.sin_family = AF_INET 
  25.   ServerAddr.sin_addr.S_un.S_addr = inet_addr(szIpAddr);  
  26.   ServerAddr.sin_port = htons(5555);  
  27.   connect(m_Socket, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr));  
  28. }  

响应通知码的函数如下: 

  1. VOID CClientDlg::OnSock(WPARAM wParam, LPARAM lParam)  
  2.  
  3.   if ( WSAGETSELECTERROR(lParam) )  
  4.   {  
  5.     return ;  
  6.   }  
  7.   switch ( WSAGETSELECTEVENT(lParam)) 
  8.   {  
  9.     // 处理 FD_ACCEPT  
  10.   case FD_CONNECT:  
  11.     {  
  12.       (GetDlgItem(IDC_SZCMD))->EnableWindow(TRUE);  
  13.       (GetDlgItem(IDC_BTN_SEND))->EnableWindow(TRUE);  
  14.       (GetDlgItem(IDC_IPADDR))->EnableWindow(FALSE);  
  15.       SetDlgItemText(IDC_BTN_CONNECT, "断开连接");  
  16.       m_StrMsg = "连接成功" 
  17.       break;  
  18.     }  
  19.     // 处理 FD_READ  
  20.   case FD_READ:  
  21.     {  
  22.       DATA_MSG DataMsg;  
  23.       recv(m_Socket, (char *)&DataMsg, sizeof(DataMsg), 0);  
  24.       DispatchMsg((char *)&DataMsg);  
  25.       break;  
  26.     }  
  27.     // 处理 FD_CLOSE  
  28.   case FD_CLOSE:  
  29.     {  
  30.       (GetDlgItem(IDC_SZCMD))->EnableWindow(FALSE);  
  31.       (GetDlgItem(IDC_BTN_SEND))->EnableWindow(FALSE);  
  32.       (GetDlgItem(IDC_IPADDR))->EnableWindow(TRUE);  
  33.       closesocket(m_Socket);  
  34.       m_StrMsg = "对方关闭连接" 
  35.       break;  
  36.     }  
  37.   }  
  38.   InsertMsg();  
  39. }  

发送命令到远程服务器端的代码如下: 

  1. void CClientDlg::OnBtnSend()  
  2.  
  3.   // 添加处理程序代码  
  4.   char szBuf[MAXBYTE] = { 0 };  
  5.   GetDlgItemText(IDC_SZCMD, szBuf, MAXBYTE);  
  6.   send(m_Socket, szBuf, MAXBYTE, 0);  
  7. }  

处理服务器端反馈结果的代码如下: 

  1. VOID CClientDlg::DispatchMsg(char *szBuf)  
  2.  
  3.   DATA_MSG DataMsg;  
  4.   memcpy((void*)&DataMsg, (const void *)szBuf, sizeof(DATA_MSG));  
  5.   if ( DataMsg.bType == TEXTMSG ) 
  6.   {  
  7.     m_StrMsg = DataMsg.szValue;  
  8.   }  
  9.   else  
  10.   {  
  11.     if ( DataMsg.bClass == SYSTEMINFO )  
  12.     {  
  13.       ParseSysInfo((PSYS_INFO)&DataMsg.szValue);  
  14.     }  
  15.   }  
  16. }  

解析服务器端信息的代码如下: 

  1. VOID CClientDlg::ParseSysInfo(PSYS_INFO SysInfo)  
  2.  
  3.   if ( SysInfo->OsVer.dwPlatformId == VER_PLATFORM_WIN32_NT )  
  4.   {  
  5.     if ( SysInfo->OsVer.dwMajorVersion == 5 && SysInfo->OsVer.dwMinorVersion == 1 )  
  6.     {  
  7.       m_StrMsg.Format("对方系统信息:\r\n\t Windows XP %s", SysInfo->OsVer. szCSDVersion);  
  8.     } 
  9.      else if ( SysInfo->OsVer.dwMajorVersion == 5 && SysInfo->OsVer.dwMinorVersion== 0)  
  10.     {  
  11.       m_StrMsg.Format("对方系统信息:\r\n\t Windows 2K");  
  12.     }  
  13.   }  
  14.   else  
  15.   { 
  16.      m_StrMsg.Format("对方系统信息:\r\n\t Other System \r\n");  
  17.   }  
  18.   m_StrMsg += "\r\n";  
  19.   m_StrMsg += "\t Computer Name is ";  
  20.   m_StrMsg += SysInfo->szComputerName;  
  21.   m_StrMsg += "\r\n";  
  22.   m_StrMsg += "\t User Name is";  
  23.   m_StrMsg += SysInfo->szUserName;  
  24. }  

到这里,远程控制的代码就完成了。如果要实现更多的功能,可能该框架无法进行更好的扩充。该实例主要为了演示非阻塞模式的Winsock应用的开发。如果该实例中的套接字使用阻塞模式的话,那么就必须配合多线程来完成,将接收的部分单独放在一个线程中,否则接收数据的函数recv()在等待接收数据的到来时会将整个程序“卡死”。 

 

责任编辑:庞桂玉 来源: 计算机与网络安全
相关推荐

2020-05-08 10:34:30

Spring非阻塞编程

2021-03-03 12:20:42

网络安全DLL编程

2021-06-11 13:40:17

网络安全专杀工具病毒

2021-03-05 13:46:56

网络安全远程线程

2021-01-26 13:45:03

网络安全Winsock编程

2021-02-23 10:20:07

网络安全进程代码

2023-09-06 14:05:14

2021-02-21 18:19:43

网络安全网络安全编程创建进程

2016-10-10 00:18:27

2011-12-07 17:17:02

JavaNIO

2021-06-18 09:55:09

网络安全目录监控

2021-05-12 14:57:13

网络安全密码代码

2021-04-19 10:26:41

网络安全PE文件

2021-05-24 11:55:55

网络安全Windows钩子函数

2021-06-24 08:37:34

网络安全内核代码

2021-06-15 11:16:24

网络安全U盘软件

2021-03-01 11:20:13

网络安全多线程代码

2012-05-21 10:15:48

2012-05-17 16:20:24

2011-03-17 13:32:45

点赞
收藏

51CTO技术栈公众号