【网络编程笔记四】《UNIX网络编程》学习笔记——socket API函数基本概念

1、关于connect函数
(1)激发TCP三次握手的是调用connect函数,该函数向对端发送一个SYN,从CLOSED状态转移到SYN_SENT状态,若函数执行成功再转移到ESTABLISHED状态。若函数执行失败,则该套接字不能再被使用,需要close当前的套接字并重新调用socket函数。
(2)connect函数返回失败的原因可能有以下情况:
发送SYN后没有得到响应,则重发SYN,超时未收到,则返回ETIMEOUT的错误。
硬错误:发送SYN后,收到的响应是RST。(ECONNREFUSED)
软错误:发出SYN后在中间的路由器找不到目的地,回发ICMP报文。

2、bind函数绑定套接字和ip地址和端口。其中ip地址和端口号分别可以为系统分配(通配)或者由进程(用户写代码中)自己分配。

3、listen函数的功能:
socket函数创建的套接字默认为主动套接字的状态,调用listen将其转换成被动套接字(监听套接字)。
listen函数的其中一个参数backlog的用途:指定某个套接字中,内核为其排队的【最大已完成连接数】。

4、内核为监听套接字维护了两个队列,分别是:
(1)未完成连接队列:当服务端收到客户端发送来的SYN,服务端在未完成连接队列中创建新项,并作出回应SYN+ACK,此时该连接处于SYN_RCVD状态。正常情况下需等待RTT,收到客户端发送来的ACK后,完成TCP连接三次握手,进入已完成连接队列队尾。
若服务端收到新客户端的SYN时,未完成队列已满,则忽视该分节,待正常的TCP重传机制使客户端重新发送SYN报文;不建议服务端发送RST,因为收到RST的客户端会给connect函数返回一个错误。另外,客户端无法区分:【响应SYN的RST】是意味着“端口没有服务器在监听”,还是“端口有服务器监听,但是它的队列满了”。
(2)已完成连接队列:存放完成TCP三次握手的连接,这次套接字处于ESTABLISHED状态,当进程执行accept函数时,会在此队列中取走头项。

5、每个文件或者套接字都拥有引用计数,表示该文件或套接字资源被引用的次数,引用的文件表项中维护着。例如:服务端的监听进程在监听客户端的连接,accept函数返回后就fock子进程来处理连接套接字,此时监听套接字listenfd和连接套接字connfd的引用计数都为2(父进程和子进程各占用1),要想关闭和客户端的连接结束TCP连接,则需要使connfd的引用计数为0,即父进程和子进程都要调用close函数关闭connfd。

6、服务器在调用bind函数时,捆绑通配地址的作用:
要是系统是多宿主主机,我们将接受目的地址为任何本地接口的连接。

7、从连接套接字读取数据时,需要循环多次调用read函数的原因:
当对端把数据写入连接套接字时,如果数据比较小,可以封装入单个TCP分节中传递到对端,则read函数可以一次读出对方发送过来的数据;如果对方要发送的数据大于MSS,则数据会被封装到多个TCP分节中再发送,所以要多次调用read函数读出所有数据。

8、关于read函数是否阻塞和返回之疑问的解释
(1)read函数只是一个通用的读文件设备的接口。是否阻塞需要由设备的属性和设定所决定。一般来说,读字符终端、网络的socket描述字,管道文件等,这些文件的缺省read都是阻塞的方式。如果是读磁盘上的文件,一般不会是阻塞方式的。但使用锁和fcntl设置取消文件O_NOBLOCK状态,也会产生阻塞的read效果。
(2)对于read套接字socket的时候,如果缓冲区没有数据,则read会阻塞,直到有数据才返回。返回值大于0有两种情况:一种是读取了函数参数所指定的值,另一种是读取到了数据的末尾。返回0时表示对端套接字已关闭。异常返回值小于0。
(3)非阻塞情况下,若要读取的文件没有数据或者要read的时候已经到达文件的末尾,也会导致read函数返回0。

9、服务端并发连接客户端的基本状态(以linux下运行服务端客户端为例)
该例子为客户端连接服务端后,服务端生成一个子进程来处理客户端请求,客户端循环调用fgets函数获取屏幕输入的值,并发送给服务端,服务端循环read,并回发相同的信息给客户端,客户端通过fput函数讲信息显示在屏幕。
(1)正常启动:
正常连接后,在linux下输入命令netstat -a | grep 端口号可以查看端口号占用情况;(这里服务端代码中采用端口是8889,34326是内核为客户端使用的端口)
在这里插入图片描述
通过ps -a -o pid,ppid,tty,stat,args,wchan查看进程使用情况。
在这里插入图片描述
-o是自定义输出选项,stat那列为S表示进程在为等待某些资源而睡眠。wchan那列为状态的相应条件,如linux在进程阻塞于accept或connect,或者进程阻塞于套接字的输入或输出。
(2)正常终止
control键+D,使客户端的程序中循环调用的fgets函数返回NULL,客户端通过exit函数终止,并给服务端发送FIN。服务器回应ACK后,服务端套接字为CLOSE_WAIT状态,客户端为FIN_WAIT_2状态,服务端阻塞的read函数返回0,然后调用exit函数结束进程后,就给客户端发送FIN,客户端回应ACK后,客户端进入TIME_WAIT状态。
在这里插入图片描述
(此时如果没有处理信号的话,服务端中处理客户端的子进程会进入僵死状态,进程状态为Z+表明僵死进程)
在这里插入图片描述
这是因为在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程,而系统默认将忽略此信号;由于子进程处于僵死进程,所以父进程可以来获取子进程的信息。为了不占用资源,我们需要处理僵死进程,可移植方法是:捕获SIGCHLD,并调用wait或waitpid。通过Signal(int signo,函数指针)函数可以:捕获指定信号并执行相应的处理函数。

10、重启被中断的系统调用:
accept函数属于慢系统调用(如read、write、select这样会阻塞的函数),当进程阻塞于某个慢系统调用的时候捕获到某个信号且该信号的处理函数返回时,该阻塞中的函数就会返回一个EINTR错误。有的内核会重启某些被中断的函数(系统调用),有些不会。所以我们要在慢系统调用时,处理这种EINTR中断返回,从而避免错误(例如为了防止accept结束阻塞监听,我们要在调用后accept函数判断返回值是否为EINTR,是的话重新执行accept函数)。

11、UNIX系统的信号一般是默认不排队的:
在一个信号处理函数期间,正在被递交的信号是阻塞着的,如果一个信号在阻塞期间产生了一次或多次,那该信号被解阻塞之后通常只递交一次。例如:五个SIGCHLD信号同时产生,则该信号的处理函数只执行一次,函数中只调用一次wait函数,那么结果就是剩下另外4个进程还是处理僵死状态。
此外,一旦安装了信号处理函数,它就一直安装着。
解决方法:while((pid = waitpid(-1,&stat,WNOHANG))>0);//在一次信号处理函数中循环处理所有的信号。参数-1表示等待第一个终止的程序,参数WNOHANG表示告知内核没有已终止子进程时不要阻塞。

12、服务器进程中止的情况
(1)当服务器进程中止时,会主动向连接着的客户端发送FIN,客户端回应ACK。如果客户端程序单纯的阻塞某一个特定源的输入上(如fget输入上),会导致FIN的接收没有告知客户【TCP服务器进程已经终止】。客户端发送给服务端的数据会使服务端回应RST;收到RST后,客户端若再次发送数据的话,内核会向进程发送一个SIGPIPE信号,该信号的默认操作是终止客户端的进程。read阻塞遇到RST则会返回ECONNRESET错误。
解决方法是:要解决【客户端程序单纯阻塞在某个特定源的输入上】的问题,可以采用IO复用技术的轮询函数,如select,poll。这样就不能因为客户端的程序阻塞,导致【客户端TCP无法及时检测到服务端发送的FIN并及时回发自己的FIN分节】的问题。
(2)向一个已接收FIN的套接字写入数据不会导致错误,但是向一个已接收RST的套接字写入数据则是一个错误。

13、服务器主机崩溃的情况
当服务器主机崩溃时,客户端TCP会持续重传数据分节,试图想接收到ACK。TCP的一个典型的重传模式是:“源自Berkeley的实现重传该数据分节12次,共等待约9分钟才放弃重传。”
(1)如果服务器主机已崩溃,则对客户端的分节没有响应,客户的阻塞调用将返回ETIMEOUT的错误。
(2)如果中间的某个路由器发现服务器主机不可达,则会响应一个“destination unreachable”的ICMP消息,返回EHOSTUNREACH或ENETUNREACH的错误。
(3)一般客户端等待ACK经过超时会发现对端主机崩溃或者不可达,我们也可以不发送数据就得知,方法是设置SO_KEEPALIVE心跳包。
(4)服务器主机崩溃后,会失去其之前连接的客户的信息。即使重启了服务器,收到客户端发送的数据后,也会因为找不到该连接而回应RST。

14、服务器主机主动关机
Unix系统关机时,init进程通常会给所有进程发送SIGTERM信号(可捕获),SIGTERM信号的默认处理行为是终止进程,如果我捕获了它并且没有终止进程,则服务器将由SIGKILL信号终止。当服务器的和客户端连接着的子进程终止时,所有打开的套接字描述符都将被关闭,即服务端TCP发送FIN分节(回到第12点的情况)。

15、不同主机下的各种数据结构类型所用的位数以及机器的对齐限制不一定都相同(位数、大小端字节序),所以穿越套接字传送二进制结构的编程可能会出现两端数据的展现形式不一样。一种解决方法是:把所有的数值数据作为文本来传递。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值