Windows提供了两种方式“套接字模式”和“套接字I/O模型”,可对一个套接字上的I/O行为加以控制。套接字模式用于决定在随一个套接字调用时,那些 Winsock函数的行为。其中的模型包括括select(选择)、WSAAsyncSelect(异步选择)、WSAEventSelect(事件选择)、OverlappedI/O(重叠式I/O)以及Completionport(完成端口)等等。

  所有Windows平台都支持套接字以锁定非锁定方式工作。在锁定模式下,在I/O操作完成前,执行操作的Winsock函数(比如send和recv)会一直等候下去,不会立即返回程序(将控制权交还给程序)。而在非锁定模式下,Winsock函数无论如何都会立即返回。

  锁定模式使用线程同步机制完成数据的I/O,扩展性不好,尤其是有多个套接字时。

  非锁定模式的使用需要函数 ioctlsocket(),使用如下:

SOCKET s;
unsigned long ub =1;
int nRet;
s = socketAF_INET,SOCK_STREAM,0);
nRet = ioctlsocket(s,FIOBIO,(unsigned long *)&ub);//设置非锁定模式
if(nRet == SOCKET_ERROR)
{
    //进入非锁定模式失败
}

套接字I/O模型

1、select模型

//函数原型
int select(
    int nfds,    //与早期程序兼容,可忽略
    fd_set FAR * readfds,// 可读性
    fd_set FAR * writefds,// 可写性
    fd_set FAR * exceptfds,// 例外数据
    const struct timeval FAR * timeout//超时时间
    );

参数readfds 表示以下几种情况

        有数据可读入、连接已经关闭(重设或者终止)、如果已经listen,并且正在建立连接,那么accept函数会返回成功。

参数writefds表示以下几种情况

        有数据可发出、如果已完成对一个非锁定连接调用的处理,连接就会成功。

参数exceptfds表示如下

        如果已完成对一个非锁定连接调用的处理,连接尝试就会失败。有带外(Out-of-band,OOB)数据可供读取。

其中fd_set结构如下:

typedef struct fd_set {
        u_int fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

超时时间

struct timeval {
        long    tv_sec;         /* seconds */long    tv_usec;        /* and microseconds */毫秒
};

  用select对套接字进行监视之前,在自己的应用程序中,必须将套接字句柄分配给一个集合,设置好一个或全部读、写以及例外fd_set结构。将一个套接字分配给任何一个集合后,再来调用select,便可知道一个套接字上是否正在发生上述的I/O活动。

下面是对fd_set进行操作的一些宏

  FD_CLR(s,*set):从set中删除套接字s。

  FD_ISSET(s,*set):检查s是否set集合的一名成员;如答案是肯定的是,则返回TRUE。

  FD_SET(s,*set):将套接字s加入集合set。

  FD_ZERO(*set):将set初始化成空集合。

用select操作一个或多个套接字句柄的全过程:

  1)使用FD_ZERO宏,初始化自己感兴趣的每一个fd_set。

  2)使用FD_SET宏,将套接字句柄分配给自己感兴趣的每个fd_set。

  3)调用select函数,然后等待在指定的fd_set集合中,I/O活动设置好一个或多个套接字句柄。select完成后,会返回在所有fd_set集合中设置的套接字句柄总数,并对每个集合进行相应的更新。

  4)根据select的返回值,我们的应用程序便可判断出哪些套接字存在着尚未完成(待决)的I/O操作—具体的方法是使用FD_ISSET宏,对每个fd_set集合进行检查。

  5)知道了每个集合中“待决”的I/O操作之后,对I/O进行处理,然后返回步骤1),继续进行select处理。

简单过程如下

   SOCKET s;
    fd_set fdread;
    int ret;
    //创建
    //bind()
    //accept()
    //开始
    while (1)
    {
        FD_ZERO(&fdread);//初始化
        FD_SET(s,&fdread);//添加
        if ((ret = select(0,&fdread,NULL,NULL,NULL)) == SOCKET_ERROR)
        {
            //添加失败
        }
        if (ret > 0)
        {
            if (FD_ISSET(s,&fdread))
            {
                //已经是集合的一部分,正在读取数据
            }        
        }
        //其他操作
    }

2、WSAAsyncSelect模型

WSAAsyncSelect 的使用必须要在窗口应用程序中使用,在回调函数中实现处理。

int WSAAsyncSelect(
  _In_  SOCKET s,//关心的socket
  _In_  HWND hWnd,//窗口句柄
  _In_  unsigned int wMsg,//消息
  _In_  long lEvent//事件类型
);

事件类型很多,如图

Windows网络编程笔记6 --- WinSock I/O 控制方法

 WSAAsyncSelect模式实现,这个实现起来挺简单的

  1 // WSAAsyncSelect模式实现,这个实现起来挺简单的。
  2 #include <winsock2.h>  
  3 #include <tchar.h>  
  4 #define PORT      7890  
  5 #define MSGSIZE   1024  
  6 #define WM_SOCKET WM_USER+1  
  7 #pragma comment(lib, "ws2_32.lib")  
  8 
  9 
 10 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
 11 int WINAPI WinMain(    HINSTANCE hInstance, 
 12                      HINSTANCE hPrevInstance, 
 13                      LPSTR lpCmdLine, 
 14                      int nShowCmd 
 15     )
 16 {
 17 
 18 
 19     static TCHAR szAppName[] = TEXT ("WSAAsyncSelect Test") ;
 20     HWND         hwnd ;
 21     MSG          msg ;
 22     WNDCLASS     wndclass ;
 23 
 24     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
 25     wndclass.lpfnWndProc   = WndProc;
 26     wndclass.cbClsExtra    = 0 ;
 27     wndclass.cbWndExtra    = 0 ;
 28     wndclass.hInstance     = hInstance ;
 29     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
 30     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
 31     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
 32     wndclass.lpszMenuName  = NULL ;
 33     wndclass.lpszClassName = szAppName ;
 34 
 35     if (!RegisterClass (&wndclass))
 36     {
 37         MessageBox (NULL, TEXT ("Program requires Windows NT!"), 
 38             szAppName, MB_ICONERROR) ;
 39         return 0 ;
 40     }
 41 
 42     hwnd = CreateWindow (szAppName, TEXT ("WSAAsyncSelect"),
 43         WS_OVERLAPPEDWINDOW,
 44         CW_USEDEFAULT, CW_USEDEFAULT,
 45         CW_USEDEFAULT, CW_USEDEFAULT,
 46         NULL, NULL, hInstance, NULL) ;
 47 
 48     ShowWindow (hwnd, nShowCmd) ;
 49     UpdateWindow (hwnd) ;
 50 
 51     while (GetMessage (&msg, NULL, 0, 0))
 52     {
 53         TranslateMessage (&msg) ;
 54         DispatchMessage (&msg) ;
 55     }
 56     return msg.wParam ;
 57 }
 58 
 59 
 60 
 61 LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  
 62 {  
 63     WSADATA       wsd;  
 64     static SOCKET sListen;  
 65     SOCKET        sClient;  
 66     SOCKADDR_IN   local, client;  
 67     int           ret, iAddrSize = sizeof(client);  
 68     char          szMessage[MSGSIZE] ;  
 69     switch (message)  
 70     {  
 71     case WM_CREATE:  
 72         // 初始化
 73         WSAStartup(0x0202, &wsd);  
 74 
 75         // 创建socket 
 76         sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
 77 
 78         // 绑定  
 79         local.sin_addr.S_un.S_addr = inet_addr("192.168.0.87");
 80         local.sin_family = AF_INET;  
 81         local.sin_port = htons(PORT);  
 82         bind(sListen, (struct sockaddr *)&local, sizeof(local));  
 83 
 84         // 监听 
 85         listen(sListen, 3);  
 86         // 设置WSAAsyncSelect模式  
 87         WSAAsyncSelect(sListen, hwnd, WM_SOCKET, FD_ACCEPT);  
 88         return 0;  
 89     case WM_DESTROY:  
 90         closesocket(sListen);  
 91         WSACleanup();  
 92         PostQuitMessage(0);  
 93         return 0;  
 94 
 95     case WM_SOCKET:  
 96         if (WSAGETSELECTERROR(lParam))//使用宏 WSAGETSELECTERROR 判断lParam高字节是否有错误
 97         {  
 98             closesocket(wParam);  
 99             break;  
100         }  
101 
102         switch (WSAGETSELECTEVENT(lParam))//使用宏WSAGETSELECTEVENT判断lParam低字节是什么操作
103         {  
104         case FD_ACCEPT:  
105             // 从客户端接收连接  
106             sClient = accept(wParam, (struct sockaddr *)&client, &iAddrSize);  
107 
108             // 设置事件监听
109             WSAAsyncSelect(sClient, hwnd, WM_SOCKET, FD_READ | FD_WRITE | FD_CLOSE);  
110             break;  
111         case FD_READ:  //
112             ret = recv(wParam, szMessage, MSGSIZE, 0);  
113             if (ret == 0 || ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)  
114             {  
115                 closesocket(wParam);  
116             }  
117             else  
118             {  
119                 szMessage[ret] = '\0';  
120                 MessageBox(hwnd,"接收成功",NULL,MB_OK);
121                 send(wParam, szMessage, strlen(szMessage), 0);  
122             }  
123             break;  
124         case FD_WRITE://
125             send(wParam, szMessage, strlen(szMessage), 0);  
126             break;
127         case FD_CLOSE:  //关闭
128             closesocket(wParam);        
129             break;  
130         }  
131         return 0;  
132     }  
133      
134     return DefWindowProc(hwnd, message, wParam, lParam);  
135 }  
View Code

相关文章:

  • 2022-12-23
  • 2021-07-03
  • 2021-06-01
  • 2021-11-04
  • 2021-09-26
  • 2021-04-30
  • 2022-12-23
  • 2021-09-15
猜你喜欢
  • 2022-12-23
  • 2021-10-16
  • 2021-05-14
  • 2021-11-22
  • 2021-06-07
  • 2021-08-01
  • 2022-03-02
相关资源
相似解决方案