网络编程基础知识

监听套接字与已连接套接字:

转载来自:https://blog.csdn.net/lihao21/article/details/64951446

了解网络编程的朋友们应该对套接字有所了解,本文首先介绍一下监听套接字与已连接套接字的区别。为了说明监听套接字与已连接套接字的区别,我们先来看一下套接字在连接中的含义。

从内核的角度来看,一个套接字就是通信的一个端点。一个连接由他两端套接字的地址唯一确定,这对套接字的地址叫做套接字对,由四个元素表示:

客户端IP地址,客户端端口,服务器IP地址,服务器端口。

如下图所示:

 在上面连接中,客户端是发起连接的主动实体,服务器是等待来自客户端连接请求的被动实体。我们知道,socket函数可以创建一个套接字。默认情况下,内核会认为socket函数创建的套接字是主动套接字(主动套接字用来发起连接)。在客户端会使用socket函数创建一个主动套接字clientfd用来发起连接。在服务器端也会用socket函数创建一个主动套接字listenfd,但是服务器程序调用listen函数时,该函数告诉内核,该套接字是被服务器使用而不是被客户端使用的,则listen函数将一个主动套接字listenfd转化为监听套接字。

监听套接字:用来接收三次握手数据,一旦三次握手完成了,就把客户端的套接字放到已连接队列中,accept函数就可以从监听队列中返回一个连接(已连接套接字)用来和客户端通信。但是如果考虑到多个客户端同时请求连接,监听套接字该如何处理?监听函数如下:


int listen(
  __in  SOCKET s,
  __in  int backlog
);

函数中一个套接字s,跟一个整形变量backlog。套接字s就不多说了,就是服务器端创建的监听套接字listenfd。变量backlog表示监听队列中能够接入的连接。

怎么回事呢?举个例子来讲,当我们调用函数listen(listenfd,3)。该函数表示监听队列的大小为3,也就是服务器能够接受的连接为3。就是如果有三个客户端向服务器发起连接,他们产生的连接会依次进入listen队列当中。

如果服务器端存在accept函数,当执行accept函数时服务器就会从监听队列中取走一个连接,这样监听队列的大小为3,被accept函数取走一个连接。现在监听队列中空出一个位置可以连入下一个客户端。

如果服务器端不存在accept函数,当已经有三个客户端发起连接之后,第四个客户端也发起连接,由于监听队列的长度为3,则第四个客户端就不能连上服务器。

accpet函数:

accept函数的调用方法如下:

sClientfd = accept(listenfd, (SOCKADDR *)&remoteAddr, &nAddrlen);

accept函数从监听队列中取走一个连接,返回一个已连接套接字用来和客户端进行通信。如果监听队列为空,则当程序执行到accept函数时无法返回程序则会阻塞在accept函数这里,直到有新的客户端连入accept函数有了返回值程序才会向下执行。然而当客户端发起连接,该链接会进入监听队列当中,如果accept函数没有从监听队列中取走这个连接,客户端的connect函数也会阻塞。直到accept函数返回一个已连接套接字之后,客户端的connect函数才会有返回值程序才能继续执行。

在此处简述一下客户端跟服务器的连接过程:

第一步在服务器中调用listen函数产生监听,之后调用accept函数,等待连接请求到达监听队列listenfd。第二步客户端调用connect函数,发送一个请求到listenfd。第三步accept函数返回一个新的已连接套接字sClientfd,在客户端与服务器端建立连接并使用sClientfd套接字与客户端进行通信。客户端也从connect函数返回。此时客户端跟服务就可以正常通信了。

AccepEx函数:

转载来自:https://blog.csdn.net/xl19900502/article/details/51456419

AcceptEx和传统的accept之间最大的区别,就是取消了阻塞方式的accept调用,也就是说,AcceptEx也是通过完成端口来异步完成的,所以就取消了专门用于accept连接的线程,用了完成端口来进行异步的AcceptEx调用。

首先要明白投递的AcceptEx请求去哪了?如果投递了AcceptEx请求但是没有客户端连入的话怎么办?程序会阻塞?先回答这两个问题,假设程序初始化时投递了10个AcceptEx请求,此时客户端没有连入也就是监听队列中为空。如果换做accept函数的话无法从监听队列中取走连接则无返回值程序会阻塞。但是AcceptEx则不会阻塞会立即返回,并且投递的操作由内核维护,假如一旦有客户端连入会有一个连接进入监听队列,此时内核会根据之前投递AcceptEx的请求调用AcceptEx函数从监听队列中取走连接。取走连接后此时内核还维护有9个AcceptEx请求。如果采用完成端口模型的话,内核会根据之前投递AcceptEx的请求调用AcceptEx函数从监听队列中取走连接。当连接完成以后,通知工作者线程投递一个AcceptEx请求。这样内核一直维护着10个AcceptEx请求。如果同时有两个客户端连入,内核会根据之前投递AcceptEx的请求调用AcceptEx函数从监听队列中依次取走连接。在完成连接之后依次通知两个工作者线程继续投递AcceptEx请求。

除此之外当大量客户端请求并发连接时,对于accept函数而言,accept取走连接后返回一个socket是临时建立的,由于建立socket需要耗费相当大的资源,所以客户端请求并发操作一旦多起来之后资源开销是非常大的。读者可能会问到需要准备哪些资源和呢?当然是准备Socket了……虽然我们创建Socket只用一行SOCKET s= socket(…) 这么一行的代码就OK了,但是系统内部建立一个Socket是相当耗费资源的,因为Winsock2是分层的机构体系,创建一个Socket需要到多个Provider之间进行处理,最终形成一个可用的套接字。总之,系统创建一个Socket的开销是相当高的,所以用accept的话,系统可能来不及为更多的并发客户端现场准备Socket了。

 而AcceptEx比Accept又强大在哪里呢?是有三点:

         (1) 这个好处是最关键的,是因为AcceptEx是在客户端连入之前,就把客户端的Socket建立好了,也就是说,AcceptEx是先建立的Socket,然后才发出的AcceptEx调用,也就是说,在进行客户端的通信之前,无论是否有客户端连入,Socket都是提前建立好了;而不需要像accept是在客户端连入了之后,再现场去花费时间建立Socket。如果各位不清楚是如何实现的,请看后面的实现部分。

         (2) 相比accept只能阻塞方式建立一个连入的入口,对于大量的并发客户端来讲,入口实在是有点挤;而AcceptEx可以同时在完成端口上投递多个请求,这样有客户端连入的时候,就非常优雅而且从容不迫的边喝茶边处理连入请求了。

         (3) AcceptEx还有一个非常体贴的优点,就是在投递AcceptEx的时候,我们还可以顺便在AcceptEx的同时,收取客户端发来的第一组数据,这个是同时进行的,也就是说,在我们收到AcceptEx完成的通知的时候,我们就已经把这第一组数据接完毕了;但是这也意味着,如果客户端只是连入但是不发送数据的话,我们就不会收到这个AcceptEx完成的通知……这个我们在后面的实现部分,也可以详细看到。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值