Socket学习之accept函数

标题accept()函数是一个阻塞函数,那什么是阻塞函数呢?

阻塞函数就是一旦调用了这个函数,但是不满足条件的话这个函数是没有返回值的。
同时,connect也是一个阻塞函数。假如客户端连接服务器连不上,客户端一直在等待连接并处于阻塞阶段,如果一直连不上会有一个连接超时的机制(Linux好像是120s)。为什么连不上 ,参考网络故障排查的方法,以及Socket程序连接失败调试方法。

Server创建一个ListenSocket,它仅仅用在客户端连接使用的,它不会用作数据的通信使用。
accept成功后会返回一个新的文件描述符,用新的文件描述符与客户端通信。并且每一个文件描述符都有一个Txbuf和Rxbuf。
在这里插入图片描述

同时我们也要了解,对于linux内核而言,每一个socket在内核里面都有Txbuf与Rxbuf,往socket里面写内容并不是一下子就通过网卡发送给机器(如客户端给服务器端通过socket连接进行通信,client通过socket给server写信息,信息的内容并不是一下子就通过网卡发送给机器的),当我们调write系统调用的时候,首先是把所要发送的内容写到Txbuf中去,但是此时也不是立马就发送出去,而是需要等一段时间。因为没写一个字节内容就发送一次的话效率太低了,但是需要等多少时间是由操作系统调度决定的。(比如调用write()系统调用write(fd, “Hello”)将“Hello”这5个字节写进去,立马再调用一个write(fd, “world”)又发了5个字节写到Txbuf中去,此时有可能hello还没有发送过去,又写了一个world,这时候有可能会把hello与world打包成一个数据报文发给对方去,这种情况叫TCP粘包。)
同理,假设client中发送的数据中第一个数据hello到了,第二个数据world也到了,这两个数据也并不是立刻给程序,而是放在Rxbuf中,而且不仅发送发会发生粘包,接收方也会发生粘包。
所以说,socket在工作的时候,每一个socket都有一个Txbuf和Rxbuf。客户端发送信息到了服务器端的Rxbuf时,服务器端的socket在合适的时候调用read()系统调用。而read()系统调用也是阻塞的,当调用read()socket的时候,如果Rxbuf没有内容,它就一直等着,直到客户端发了内容过来,read()系统调用才会返回。此外,客户端发了多少个字节的数据给服务器端我们也是需要知道的,所以read()函数的第一个参数是从哪里调用,所以是socketfd,第二个参数是我们要将数据读走,放到哪里去,所以是buf,第三个参数是,读取数据的buf是多大(即装读取数据的盒子有多大),所以是sizeof(buf),read的返回值是实际读到的数据的字节数。
在这里插入图片描述

read()调用并不是从write里面调用,而是从服务器端的Rxbuf(前提是:数据是从客户端发的)。如果read返回值表示出错了,如网线断了,数据都失效了。如果返回值是0,表示断开,大于0表示的是实际读到的数据字节数。

标题accept函数的三次握手

socket的三路握手是发生在客户端pc开始调用connect()函数的时候来发起的,并且不是在调用accept之后结束的。事实上socket的三路握手并不是accept完成的,而是由Linux操作系统内核完成的, 完成之后操作系统会将所有的连接请求都丢到一个队列中去。accept只是从这个队列中抽取第一个,这时候服务器的socket就可以和客户端进行通信,而原来的ListenSocket对于被抽取的客户端来说是失效的。
在这里插入图片描述

结合上图与文字一起理解
服务器端首先要调用socket()创建一个listenfd,然后再调用bind(将服务器端的IP和Port绑定起来),这时候的socket还处于close的状态的,因为还没有建立连接。接下里调用listen,这个时候socket处于listen状态,此时,客户端调用accept(),并阻塞在这里,因为三路握手是需要时间的。这时候客户端调用socket(), 创建一个文件描述符fd,然后调用connect()开始连接服务器,但是需要注意的是连接的时候并不是一下子就连接成功,需要建立三路握手,又因为三路握手需要时间。三路握手首先在调用connect()函数的时候操作系统会发起一个SYNC的一个连接请求,服务器端接收到连接请求后需要对客户端进行确认SYN+ACK,此时的ACK是对客户端的SYNC的确认。但是服务器端不确定自己的数据报文会不会到客户端所以它还会捎带一个sync,客户端收到了服务器端的ACK和SYNC之后再给服务器端发送一个ACK数据报文,此时服务器端就建立了请求。重要的是connect是在完成三路握手并在accept之后connect才能返回,否则connect一直阻塞。

半握手状态指当多个客户端要连接服务器端但是还没有连接上的状态,而连接上的客户端会被放在一个队列中去,然后由accept()按照顺序抽取,实现通信。
所以说三路握手不是由accept()完成的,而是由操作系统提前完成的。accept()只是从已经建立起三路握手的客户端中的第一个。

对于客户端而言,客户端请求连接服务器端,应用程序是不知道客户端的IP和Port,因为这些信息都是服务器端(操作系统内核)处理的。如果想知道客户端的IP和Port,我们可以调用accept()来获取服务器端的连接请求,此时此刻也可以通过accept的第二个和第三个参数来获取客户端的信息,因为已经建立好连接请求的这些socket的信息Linux操作系统都已经保存了。accept 从一个参数中(listenfd)中获取一个已经连接的请求,并返回一个新的文件描述符,第2个和第3个参数是让内核把这一个客户端的地址信息发给应用程序空间。同时第三个参数是一个值,意思就说我们既要通过addrlen给Linux内核传递一个消息,同时Linux内核还会改变addrlen的结果并返回出去。(这些内容可以用 man accept进行查看)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值