Windows Socket五种IO模型——代码全攻略

合集下载

WinSocket模型探讨——完成端口模型

WinSocket模型探讨——完成端口模型

WinSocket模型的探讨——完成端口模型众所皆知,完成端口是在WINDOWS平台下效率最高,扩展性最好的IO模型,特别针对于WINSOCK的海量连接时,更能显示出其威力。

其实建立一个完成端口的服务器也很简单,只要注意几个函数,了解一下关键的步骤也就行了。

这是篇完成端口入门级的文章,分为以下几步来说明完成端口:函数常见问题以及解答步骤例程1、函数:我们在完成端口模型下会使用到的最重要的两个函数是:CreateIoCompletionPort、GetQueuedCompletionStatusCreateIoCompletionPort 的作用是创建一个完成端口和把一个IO句柄和完成端口关联起来:// 创建完成端口HANDLE CompletionPort = CreateIoCompletionPort(INV ALID_HANDLE_V ALUE, NULL, 0, 0);// 把一个IO句柄和完成端口关联起来,这里的句柄是一个socket 句柄CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)PerHandleData, 0);其中第一个参数是句柄,可以是文件句柄、SOCKET句柄。

第二个就是我们上面创建出来的完成端口,这里就把两个东西关联在一起了。

第三个参数很关键,叫做PerHandleData,就是对应于每个句柄的数据块。

我们可以使用这个参数在后面取到与这个SOCKET对应的数据。

最后一个参数给0,意思就是根据CPU的个数,允许尽可能多的线程并发执行。

GetQueuedCompletionStatus 的作用就是取得完成端口的结果:// 从完成端口中取得结果GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE)第一个参数是完成端口第二个参数是表明这次的操作传递了多少个字节的数据第三个参数是OUT类型的参数,就是前面CreateIoCompletionPort传进去的单句柄数据,这里就是前面的SOCKET句柄以及与之相对应的数据,这里操作系统给我们返回,让我们不用自己去做列表查询等操作了。

Windows Socket五种IO模型——代码全攻略

Windows Socket五种IO模型——代码全攻略

Windows Socket五种I/O模型——代码全攻略(1)如果你想在Windows平台上构建服务器应用,那么I/O模型是你必须考虑的。

Windows操作系统提供了选择(Select)、异步选择(WSAAsyncSelect)、事件选择(WSAEventSelect)、重叠I/O(Overlapped I/O)和完成端口(Completion Port)共五种I/O模型。

每一种模型均适用于一种特定的应用场景。

程序员应该对自己的应用需求非常明确,而且综合考虑到程序的扩展性和可移植性等因素,作出自己的选择。

我会以一个回应反射式服务器(与《Windows网络编程》第八章一样)来介绍这五种I/O模型。

我们假设客户端的代码如下(为代码直观,省去所有错误检查,以下同):#include <WINSOCK2.H>#include <stdio.h>#define SERVER_ADDRESS "137.117.2.148"#define PORT 5150#define MSGSIZE 1024#pragma comment(lib, "ws2_32.lib")int main(){WSADATA wsaData;SOCKET sClient;SOCKADDR_IN server;char szMessage[MSGSIZE];int ret;// Initialize Windows socket libraryWSAStartup(0x0202, &wsaData);// Create client socketsClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// Connect to servermemset(&server, 0, sizeof(SOCKADDR_IN));server.sin_family = AF_INET;server.sin_addr.S_un.S_addr = inet_addr(SERVER_ADDRESS);server.sin_port = htons(PORT);connect(sClient, (struct sockaddr *)&server, sizeof(SOCKADDR_IN));while (TRUE){printf("Send:");gets(szMessage);// Send messagesend(sClient, szMessage, strlen(szMessage), 0);// Receive messageret = recv(sClient, szMessage, MSGSIZE, 0);szMessage[ret] = '\0';printf("Received [%d bytes]: '%s'\n", ret, szMessage);}// Clean upclosesocket(sClient);WSACleanup();return 0;}客户端所做的事情相当简单,创建套接字,连接服务器,然后不停的发送和接收数据。

第4章 Windows套接字IO模型

第4章 Windows套接字IO模型

FIONREAD
SIOCATMARK
用于确定是否所有的 带外数据都已经被读 入,该命令仅适用于 流式套接字
TCP 首部格式
位0 8 源 端 口 序 号 TCP 首部 确 数据 偏移 保 留
U AP RSF R CS SY I G K H T NN
16
24 目 的 端 口
31

号 窗 口 紧 急 指 针
沈阳航空航天大学
简单的阻塞模式示例
SOCKET sock; char buff[256]; int done = 0; …… while(!done) { nBytes = recv(sock,buff,65,0); if (nBytes == SOCKET_ERROR) { printf(“recv failed with error %d\n”,WSAGetLastError()); return; } DoComputationData(buff); } ……
网络程序设计
第四章Windows套接字I/O模型
沈阳航空航天大学
设置套接字工作模式—ioctlsocket() 函数形式 :
int ioctlsocket ( SOCKET s, long cmd, u_long * argp );
功能说明: 套接字默认工作在阻塞模式,此函数设置套接字的工作模 式为非阻塞或阻塞模式。 返回值: 正确调用返回0,否则将返回SOCKET_ERROR ,应用程序
假如没有数据处于“待决”状态, 那么recv函数可能永远都无法返回。 只有从系统的输入缓冲区中读回点 什么东西,才允dows套接字I/O模型
沈阳航空航天大学
1.2 非阻塞模式
非阻塞模式的套接字在使用上稍显困难,但它在功能上 是非常强大的。除具备阻塞套接字已有的各项优点之外, 还进行了扩充,功能更强。 创建一个套接字,并将其置为非阻塞模式的程序示例:

windows—socket函数整理

windows—socket函数整理

Select模型Select模型是Windows sockets中最常见的IO模型。

它利用select函数实现IO 管理。

通过对select函数的调用,应用程序可以判断套接字是否存在数据、能否向该套接字写入数据。

如:在调用recv函数之前,先调用select函数,如果系统没有可读数据那么select函数就会阻塞在这里。

当系统存在可读或可写数据时,select函数返回,就可以调用recv函数接收数据了。

可以看出使用select模型,需要两次调用函数。

第一次调用select函数第二次socket API。

使用该模式的好处是:可以等待多个套接字。

select函数中需要三个fd_set结构:一:准备接收数据的套接字集合,即可读性集合。

二:准备发送数据的套接字集合,即可写性集合。

在select函数返回时,会在fd_set结构中,填入相应的套接字。

readfds数组将包括满足以下条件的套接字:1:有数据可读。

此时在此套接字上调用recv,立即收到对方的数据。

2:连接已经关闭、重设或终止。

3:正在请求建立连接的套接字。

此时调用accept函数会成功。

writefds数组包含满足下列条件的套接字:1:有数据可以发出。

此时在此套接字上调用send,可以向对方发送数据。

2:调用connect函数,并连接成功的套接字。

exceptfds数组将包括满足下列条件的套接字:1:调用connection函数,但连接失败的套接字。

2:有带外(out of band)数据可读。

select函数的使用:在调用select函数对套接字进行监视之前,必须将要监视的套接字分配给上述三个数组中的一个。

然后调用select函数,再次判断需要监视的套接字是否还在原来的集合中。

就可以知道该集合是否正在发生IO操作。

例如:应用程序想要判断某个套接字是否存在可读的数据,需要进行如下步骤:1:将该套接字加入到readfds集合。

2:以readfds作为第二个参数调用select函数。

Socket IO模型 六种讲解

Socket IO模型 六种讲解

本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教。

一:select模型二:WSAAsyncSelect模型三:WSAEventSelect模型四:Overlapped I/O 事件通知模型五:Overlapped I/O 完成例程模型六:IOCP模型老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系。

他们的信会被邮递员投递到他们的信箱里。

这和Socket模型非常类似。

下面我就以老陈接收信件为例讲解Socket I/O模型~~~一:select模型老陈非常想看到女儿的信。

以至于他每隔10分钟就下楼检查信箱,看是否有女儿的信~~~~~在这种情况下,"下楼检查信箱"然后回到楼上耽误了老陈太多的时间,以至于老陈无法做其他工作。

select模型和老陈的这种情况非常相似:周而复始地去检查......如果有数据......接收/发送.......使用线程来select应该是通用的做法:procedure TListenThread.Execute;varaddr : TSockAddrIn;fd_read : TFDSet;timeout : TTimeV al;ASock,MainSock : TSocket;len, i : Integer;beginMainSock := socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );addr.sin_family := AF_INET;addr.sin_port := htons(5678);addr.sin_addr.S_addr := htonl(INADDR_ANY);bind( MainSock, @addr, sizeof(addr) );listen( MainSock, 5 );while (not Terminated) dobeginFD_ZERO( fd_read );FD_SET( MainSock, fd_read );_sec := 0;_usec := 500;if select( 0, @fd_read, nil, nil, @timeout ) > 0 then //至少有1个等待Accept的connection beginif FD_ISSET( MainSock, fd_read ) thenbeginfor i:=0 to fd_read.fd_count-1 do //注意,fd_count <= 64,也就是说select只能同时管理最多64个连接beginlen := sizeof(addr);ASock := accept( MainSock, addr, len );if ASock <> INV ALID_SOCKET then....//为ASock创建一个新的线程,在新的线程中再不停地selectend;end;end;end; //while (not self.Terminated)shutdown( MainSock, SD_BOTH );closesocket( MainSock );end;二:WSAAsyncSelect模型后来,老陈使用了微软公司的新式信箱。

Socket阻塞模式和非阻塞模式

Socket阻塞模式和非阻塞模式

Socket阻塞模式和⾮阻塞模式阻塞I/O模型:简介:进程会⼀直阻塞,直到数据拷贝完成应⽤程序调⽤⼀个IO函数,导致应⽤程序阻塞,等待数据准备好。

如果数据没有准备好,⼀直等待….数据准备好了,从内核拷贝到⽤户空间,IO函数返回成功指⽰。

阻塞I/O模型图:在调⽤recv()/recvfrom()函数时,发⽣在内核中等待数据和复制数据的过程。

当调⽤recv()函数时,系统⾸先查是否有准备好的数据。

如果数据没有准备好,那么系统就处于等待状态。

当数据准备好后,将数据从系统缓冲区复制到⽤户空间,然后该函数返回。

在套接应⽤程序中,当调⽤recv()函数时,未必⽤户空间就已经存在数据,那么此时recv()函数就会处于等待状态。

当使⽤socket()函数和WSASocket()函数创建套接字时,默认的套接字都是阻塞的。

这意味着当调⽤Windows Sockets API不能⽴即完成时,线程处于等待状态,直到操作完成。

并不是所有Windows Sockets API以阻塞套接字为参数调⽤都会发⽣阻塞。

例如,以阻塞模式的套接字为参数调⽤bind()、listen()函数时,函数会⽴即返回。

将可能阻塞套接字的Windows Sockets API调⽤分为以下四种:1.输⼊操作: recv()、recvfrom()、WSARecv()和WSARecvfrom()函数。

以阻塞套接字为参数调⽤该函数接收数据。

如果此时套接字缓冲区内没有数据可读,则调⽤线程在数据到来前⼀直睡眠。

2.输出操作: send()、sendto()、WSASend()和WSASendto()函数。

以阻塞套接字为参数调⽤该函数发送数据。

如果套接字缓冲区没有可⽤空间,线程会⼀直睡眠,直到有空间。

3.接受连接:accept()和WSAAcept()函数。

以阻塞套接字为参数调⽤该函数,等待接受对⽅的连接请求。

如果此时没有连接请求,线程就会进⼊睡眠状态。

4.外出连接:connect()和WSAConnect()函数。

一文读懂网络编程中的5种IO模型

一文读懂网络编程中的5种IO模型

一文读懂网络编程中的5种IO模型关于IO模型,就必须先谈到几个日常接触的几个与IO相关名字:同步,异步,阻塞,非阻塞。

一、名词解释1、同步如果事件A需要等待事件B的完成才能完成,这种串行执行机制可以说是同步的,这是一种可靠的任务序列,要么都成功,要么都失败。

2、异步如果事件A的执行不需要依赖事件B的完成结果,这种并行的执行机制可以说是异步的。

事件A不确定事件B是否真正完成,所以是不可靠的任务序列。

同步异步可以理解为多个事件的执行方式和执行时机如何,是串行等待还是并行执行。

同步中依赖事件等待被依赖事件的完成,然后触发自身开始执行,异步中依赖事件不需要等待被依赖事件,可以和被依赖事件并行执行,被依赖事件执行完成后,可以通过回调、通知等方式告知依赖事件。

3、阻塞对于阻塞,如果一个事件在发起一个调用之后,在调用结果返回之前,该事件会被一直挂起,处于等待状态。

4、非阻塞对于非阻塞,如果一个事件在发起调用以后,无论该调用当前是否得到结果,都会立刻返回,不会阻塞当前事件。

阻塞与非阻塞可以理解为单个事件在发起其他调用以后,自身的状态如何,是苦苦等待还是继续干自己的事情。

非阻塞虽然能提高CPU利用率,但是也带来了系统线程切换的成本,需要在CPU执行时间和系统切换成本之间好好估量一下。

二、IO模型IO模型分为五种,阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动IO模型、异步IO模型、前4种为同步IO操作、只有异步IO模型是异步IO操作、请仔细阅读IO交互便于理解IO模型。

1、阻塞IO模型网络编程中,读取客户端的数据需要调用recvfrom。

在默认情况下,这个调用会一直阻塞直到数据接收完毕,就是一个同步阻塞的IO方式。

模拟举例:老李去火车站买票,排队三天买到一张退票。

耗费:在车站吃喝拉撒睡 3天,其他事一件没干。

2、非阻塞IO模型当用户进程发出read操作时、如果内核中的数据还没有准备好、那么它并不会阻塞用户进程、而是立刻返回一个error、从用户进程角度讲、它发起一个read操作后、并不需要等待、而是马上就得到了一个结果、用户进程判断结果是一个error时、它就知道数据还没有准备好、于是它可以再次发送read操作、一旦内核中的数据准备好了、并且又再次收到了用户进程的系统调用、那么它马上就将数据拷贝到了用户内存、然后返回。

Winsocket编程基础

Winsocket编程基础

一、网络编程基础1.1计算机网络概述计算机网络把分布在不同地点且具有独立功能的多个计算机系统通过通信设备和线路连接起来,在功能完善的软件和协议的管理下实现网络中资源共享。

在工控领域,现场数据的采集、传输及控制信息的发送都依赖于计算机网络来实现。

1.2、网络参考模型为什么要对网络进行分层设计?在计算机通信过程中需要通信协议,但因传输介质的不同、计算机本身的差异以及数据格式的不同等因素,致使网络通信相当复杂,为了降低复杂性,OSI提出了协议分层的参考模型,即OSI七层互联参考模型。

因为OSI的网络模型标准比较严格,另外推出的时间也相对较晚,所以目前还没有完全按照OSI模型实现的网络。

TCP/IP是目前实际应用最广泛的一种网络模型,在这个模型中,不只是划分了功能层,还有具体的实现技术,即协议。

TCP和IP就是这个模型中最重要的两个层次的代表协议。

1.2.1、OSI和TCP/IP参考模型在网络的不同分层中有不同的协议,计算机只能在同一层次上进行通信,如下图:虽然TCP/IP不是完全符合OSI的参考模型,但在层次上也存在着对应关系,如下图:1.2.2、数据流向在网络的层次模型中,每一层与相邻层之间都留有接口,较低层通过接口为上一层提供服务,中间层就像个翻译一样,如下图为经典的中德教师的对话过程:在TCP/IP网络模型中,数据在从应用层向网络接口层(链路层)传递的过程中,每经过一层都要加入该层的相应的协议内容,这样数据在链路层形成了完整的数据包,该数据包到达接收方后,数据包从链路层到应用层进行逐层解析,在接收方应用层解析得到的数据就是发送方在应用层发送的数据,数据进行逐层封装和解析的过程如下图:1.2.3、IP分类IP地址在网络层中定义,长度为32个二进制位,分为4段,每段8位,用于主机在网络中的标识,IP地址有两部分组成,一部分为网络地址,另一部分为主机地址。

IP地址分为A、B、C、D、E 共5类,具体如下:A 类:|0| + 网络号7位+主机号24位0.0.0.0 ------127.255.255.255B 类:|1 0|+网络号14位+主机号16位128.0.0.0 ------191.255.255.255C 类:|1 1 0|+网络号21位+主机号8位192.0.0.0 ------223.255.255.255D 类:|1 1 1 0|+网络号28位多播组号224.0.0.0 ------239.255.255.255E 类:|1 1 1 1 0|+27位留待后用在使用IP地址进行通信时,可分为单播、组播和广播三种通信方式。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Windows Socket五种I/O模型——代码全攻略Winsock 的I/O操作:1、两种I/O模式阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序。

套接字默认为阻塞模式。

可以通过多线程技术进行处理。

非阻塞模式:执行I/O操作时,Winsock函数会返回并交出控制权。

这种模式使用起来比较复杂,因为函数在没有运行完成就进行返回,会不断地返回WSAEWOULDBLOCK错误。

但功能强大。

为了解决这个问题,提出了进行I/O操作的一些I/O模型,下面介绍最常见的三种:Windows Socket五种I/O模型——代码全攻略如果你想在Windows平台上构建服务器应用,那么I/O模型是你必须考虑的。

Windows操作系统提供了选择(Select)、异步选择(WSAAsyncSelect)、事件选择(WSAEventSelect)、重叠I/O(Overlapped I/O)和完成端口(Completion Port)共五种I/O模型。

每一种模型均适用于一种特定的应用场景。

程序员应该对自己的应用需求非常明确,而且综合考虑到程序的扩展性和可移植性等因素,作出自己的选择。

我会以一个回应反射式服务器(与《Windows网络编程》第八章一样)来介绍这五种I/O模型。

我们假设客户端的代码如下(为代码直观,省去所有错误检查,以下同):#include <WINSOCK2.H>#include <stdio.h>#define SERVER_ADDRESS "137.117.2.148"#define PORT 5150#define MSGSIZE 1024#pragma comment(lib, "ws2_32.lib")int main(){WSADA TA wsaData;SOCKET sClient;SOCKADDR_IN server;char szMessage[MSGSIZE];int ret;// Initialize Windows socket libraryWSAStartup(0x0202, &wsaData);// Create client socketsClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// Connect to servermemset(&server, 0, sizeof(SOCKADDR_IN));server.sin_family = AF_INET;server.sin_addr.S_un.S_addr = inet_addr(SERVER_ADDRESS);server.sin_port = htons(PORT);connect(sClient, (struct sockaddr *)&server, sizeof(SOCKADDR_IN));while (TRUE){printf("Send:");gets(szMessage);// Send messagesend(sClient, szMessage, strlen(szMessage), 0);// Receive messageret = recv(sClient, szMessage, MSGSIZE, 0);szMessage[ret] = '\0';printf("Received [%d bytes]: '%s'\n", ret, szMessage);}// Clean upclosesocket(sClient);WSACleanup();return 0;}客户端所做的事情相当简单,创建套接字,连接服务器,然后不停的发送和接收数据。

比较容易想到的一种服务器模型就是采用一个主线程,负责监听客户端的连接请求,当接收到某个客户端的连接请求后,创建一个专门用于和该客户端通信的套接字和一个辅助线程。

以后该客户端和服务器的交互都在这个辅助线程内完成。

这种方法比较直观,程序非常简单而且可移植性好,但是不能利用平台相关的特性。

例如,如果连接数增多的时候(成千上万的连接),那么线程数成倍增长,操作系统忙于频繁的线程间切换,而且大部分线程在其生命周期内都是处于非活动状态的,这大大浪费了系统的资源。

所以,如果你已经知道你的代码只会运行在Windows平台上,建议采用Winsock I/O模型。

一.选择模型Select(选择)模型是Winsock中最常见的I/O模型。

之所以称其为“Select模型”,是由于它的“中心思想”便是利用select函数,实现对I/O的管理。

最初设计该模型时,主要面向的是某些使用UNIX操作系统的计算机,它们采用的是Berkeley套接字方案。

Select模型已集成到Winsock 1.1中,它使那些想避免在套接字调用过程中被无辜“锁定”的应用程序,采取一种有序的方式,同时进行对多个套接字的管理。

由于Winsock 1.1向后兼容于Berkeley 套接字实施方案,所以假如有一个Berkeley套接字应用使用了select函数,那么从理论角度讲,毋需对其进行任何修改,便可正常运行。

(节选自《Windows网络编程》第八章)下面的这段程序就是利用选择模型实现的Echo服务器的代码(已经不能再精简了):#include <winsock.h>#include <stdio.h>#define PORT 5150#define MSGSIZE 1024#pragma comment(lib, "ws2_32.lib")int g_iTotalConn = 0;SOCKET g_CliSocketArr[FD_SETSIZE];DWORD WINAPI WorkerThread(LPVOID lpParameter);int main(){WSADA TA wsaData;SOCKET sListen, sClient;SOCKADDR_IN local, client;int iaddrSize = sizeof(SOCKADDR_IN);DWORD dwThreadId;// Initialize Windows socket libraryWSAStartup(0x0202, &wsaData);// Create listening socketsListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// Bindlocal.sin_addr.S_un.S_addr = htonl(INADDR_ANY);local.sin_family = AF_INET;local.sin_port = htons(PORT);bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));// Listenlisten(sListen, 3);// Create worker threadCreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);while (TRUE){// Accept a connectionsClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));// Add socket to g_CliSocketArrg_CliSocketArr[g_iTotalConn++] = sClient;}return 0;}DWORD WINAPI WorkerThread(LPVOID lpParam){int i;fd_set fdread;int ret;struct timeval tv = {1, 0};char szMessage[MSGSIZE];while (TRUE){FD_ZERO(&fdread);for (i = 0; i < g_iTotalConn; i++){FD_SET(g_CliSocketArr, &fdread);}// We only care read eventret = select(0, &fdread, NULL, NULL, &tv);if (ret == 0){// Time expiredcontinue;}for (i = 0; i < g_iTotalConn; i++){if (FD_ISSET(g_CliSocketArr, &fdread)){// A read event happened on g_CliSocketArrret = recv(g_CliSocketArr, szMessage, MSGSIZE, 0);if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)) {// Client socket closedprintf("Client socket %d closed.\n", g_CliSocketArr);closesocket(g_CliSocketArr);if (i < g_iTotalConn - 1){g_CliSocketArr[i--] = g_CliSocketArr[--g_iTotalConn];}}else{// We received a message from clientszMessage[ret] = '\0';send(g_CliSocketArr, szMessage, strlen(szMessage), 0);}}}}return 0;}服务器的几个主要动作如下:1.创建监听套接字,绑定,监听;2.创建工作者线程;3.创建一个套接字数组,用来存放当前所有活动的客户端套接字,每accept一个连接就更新一次数组;4.接受客户端的连接。

这里有一点需要注意的,就是我没有重新定义FD_SETSIZE宏,所以服务器最多支持的并发连接数为64。

相关文档
最新文档