Unix 网络编程 读书笔记1
第一章:
C/C++语言提供两种不同的编程模式:IPL32和PL64。
► IPL32
● 表示integer/pointer/long三种数据类型是32位(4个字节),在这种模式下,提供32
位的地址空间,理论的内存使用限制为4G。
► PL64
● 表示pointer/long两种数据类型是64位(8个字节),提供64位地址空间,使用内存超
过4G(达2^60bytes=1EB)。
more infoabout IPL32 and PL64, please refer to http://www-31.ibm.com/cn/downloadfiles/tsc/faq/cyberclass/aixmig/aixmig.pdf
第二章:
TCP 连接建立过程:
TCP 连接解除过程:
TCP 状态和socket 函数调用对应关系:
深入理解 listen 和accept 函数
1 #include<sys/socket.h> 2 int listen(int sockfd, int backlog);
listen函数仅由TCP服务器调用,它做两件事情:
1、当socket函数创建一个套接口时,它被假设为一个主动套装口,也就是说,它是一个将调用connet发起连接的客户套接口。listen函数把一个未连接的套接口转换成一个被动套接口,指示内核应接受指向该套接口的连接请求。根据TCP状态转换图,调用listen导致套接口从CLOSED状态转换到LISTEN状态。
2、本函数的第二个参数规定了内核应该为相应套接口排队的最大连接个数。
为了更好的理解backlog参数,我们必须认识到内核为任何一个给定的监听套接口维护两个队列:
1、未完成连接队列(incomplete connection queue),每个这样的SYN分节对应其中一项:已由某个客户发出并到达服务器,而服务器正在等待完成相应的TCP三路握手过程。这些套接口处于SYN_RCVD状态。
2、已完成连接队列(completed connection queue),每个已完成TCP三路握手过程的客户对应其中一项。这些套接口处于ESTABLISHED状态。
当来自客户的SYN到达时,TCP在未完成连接队列中创建一个新项,然后响应以三路握手的第二个分节:服务器的SYN响应,其中稍带对客户SYN的ACK(即SYN+ACK)。这一项一直保留在未完成连接队列中,直到三路握手的第三个分节(客户对服务器SYN的ACK)到达或者该项超时为止(曾经源自Berkeley的实现为这些未完成连接的项设置的超时值为75秒)。如果三路握手正常完成,该项就从未完成连接队列移到已完成连接队列的队尾。当进程调用accept时,已完成连接队列中的队头项将返回给进程,或者如果该队列为空,那么进程将被投入睡眠,直到TCP在该队列中放入一项才唤醒它。
3次握手发生在客户端 connect 时 而服务器accept 只是从内核取出可以已完成握手的队列中取出一个。
man listen
[root@localhost httpSever]# man listen
LISTEN(2) Linux Programmer’s Manual LISTEN(2)
NAME
listen - listen for connections on a socket
SYNOPSIS
#include <sys/socket.h>
int listen(int sockfd, int backlog);
DESCRIPTION
To accept connections, a socket is first created with socket(2), a willingness to accept incoming connections
and a queue limit for incoming connections are specified with listen(), and then the connections are accepted
with accept(2). The listen() call applies only to sockets of type SOCK_STREAM or SOCK_SEQPACKET.
The backlog parameter defines the maximum length the queue of pending connections may grow to. If a connection
request arrives with the queue full the client may receive an error with an indication of ECONNREFUSED or, if
the underlying protocol supports retransmission, the request may be ignored so that retries succeed.
NOTES
The behaviour of the backlog parameter on TCP sockets changed with Linux 2.2. Now it specifies the queue
length for completely established sockets waiting to be accepted, instead of the number of incomplete connec-
tion requests. The maximum length of the queue for incomplete sockets can be set using the tcp_max_syn_backlog
sysctl. When syncookies are enabled there is no logical maximum length and this sysctl setting is ignored.
See tcp(7) for more information.
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
ERRORS
EADDRINUSE
Another socket is already listening on the same port.
EBADF The argument sockfd is not a valid descriptor.
ENOTSOCK
The argument sockfd is not a socket.
EOPNOTSUPP
The socket is not of a type that supports the listen() operation.
CONFORMING TO
4.4BSD, POSIX.1-2001. The listen() function call first appeared in 4.2BSD.
BUGS
If the socket is of type AF_INET, and the backlog argument is greater than the constant SOMAXCONN (128 in Linux
2.0 & 2.2), it is silently truncated to SOMAXCONN.
SEE ALSO
accept(2), bind(2), connect(2), socket(2)
BSD Man Page 1993-07-23 LISTEN(2)
转一篇文章http://blog.163.com/xychenbaihu@yeah/blog/static/13222965520118139252103/
在我们学习网络基础时,传输层的协议有TCP和UDP;
在Linux网络编程中,我们使用socket API,实现网络通信。
那么:
socket API 和 TCP 协议中各个状态是如何对应的呢?我们可以通过下图来看:
在socket系统调用中,如何完成三次握手和四次挥手:
SOCK_DGRAM,即UDP中的connect操作知识在内核中注册对方机器的IP和PORT信息,并没有建立链接的过程,即没有发包,close也不发包)。
而SOCK_STREAM对应如下:
connect会完成TCP的三次握手,客户端调用connect后,由内核中的TCP协议完成TCP的三次握手;
close操作会完成四次挥手。
三次握手对应的Berkeley Socket API:
从图中,可以看出和连接建立相关的API有:connect, listen, accept 3个,connect用在客户端,另外2个用在服务端。
对于TCP/IP protocol stack来说,TCP层的tcp_in&tcp_out也参与这个过程。我们这里只讨论这3个应用层的API干了什么事情。
(1) connect
发送了一个SYN,收到Server的SYN+ACK后,代表连接完成。发送最后一个ACK是protocol stack,tcp_out完成的。
(2)listen
在server这端,准备了一个未完成的连接队列,保存只收到SYN_C的socket结构;
还准备了已完成的连接队列,即保存了收到了最后一个ACK的socket结构。
(3)accept
应用进程调用accept的时候,就是去检查上面说的已完成的连接队列,如果队列里有连接,就返回这个连接;
如果没有,即空的,blocking方试调用,就睡眠等待;
nonblocking方式调用,就直接返回,一般一"EWOULDBLOCK“ errno告诉调用者,连接队列是空的。
注意:
在上面的socket API和TCP STATE的对应关系中,TCP协议中,客户端收到Server响应时,可能会有会延迟确认。
即客户端收到数据后,会阻塞给Server端确认。
可以在每次收到数据后:
调用setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, (int[]){1}, sizeof(int)); 快速给Server端确认。
我们如何判断有一个建立链接请求或一个关闭链接请求:
建立链接请求:
1、connect将完成三次握手,accept所监听的fd上,产生读事件,表示有新的链接请求;
关闭链接请求:
1、close将完成四次挥手,如果有一方关闭sockfd,对方将感知到有读事件,
如果read读取数据时,返回0,即读取到0个数据,表示有断开链接请求。(在操作系统中已经这么定义)
关闭链接过程中的TCP状态和SOCKET处理,及可能出现的问题:
1. TIME_WAIT
TIME_WAIT 是主动关闭 TCP 连接的那一方出现的状态,系统会在 TIME_WAIT 状态下等待 2MSL(maximum segment lifetime )后才能释放连接(端口)。通常约合 4 分钟以内。
进入 TIME_WAIT 状态等待 2MSL 的目的:
1、确保连接可靠地关闭; 即防止最后一个ACK丢失。
2、避免产生套接字混淆(同一个端口对应多个套接字)。
(
意思是,一方close发送了关闭链接请求,对方的应答迟迟到不了(例如网络原因),导致TIME_WAIT超时,此时这个端口又可用了,
我们在这个端口上又建立了另外一个socket链接。
如果此时对方的应答到了,怎么处理呢?
其实这个在TCP层已经处理了,由于有TCP序列号,所以内核TCP层,就会将包丢掉,并给对方发包,让对方将sockfd关闭。
所以应用层是没有关系的。即我们用socket API编写程序,就不用处理。
)
注意::
TIME_WAIT是指操作系统的定时器会等2MSL,而主动关闭sockfd的一方,并不会阻塞。(即应用程序在close时,并不会阻塞)。
当主动方关闭sockfd后,对方可能不知道这个事件。那么当对方(被动方)写数据,即send时,将会产生错误,即errno为: ECONNRESET。
服务器产生大量 TIME_WAIT 的原因:(一般我们不这样开发Server)
服务器存在大量的主动关闭操作,需关注程序何时会执行主动关闭(如批量清理长期空闲的套接字等操作)。
一般我们自己写的服务器进行主动断开连接的不多,除非做了空闲超时之类的管理。(TCP短链接是指,客户端发送请求给服务器,客户端收到服务器端的响应后,关闭链接)。
2. CLOSE_WAIT
CLOSE_WAIT 是被动关闭 TCP 连接时产生的,
如果收到另一端关闭连接的请求后,本地(Server端)不关闭相应套接字就会导致本地套接字进入这一状态。
(如果对方关闭了,没有收到关闭链接请求,就是下面的不正常情况)
按状态机,我方收到FIN,则由TCP实现发送ACK,因此进入CLOSE_WAIT状态。但如果我方不执行close(),就不能由CLOSE_WAIT迁移到LAST_ACK,则系统中会存在很多CLOSE_WAIT状态的连接。
如果存在大量的 CLOSE_WAIT,则说明客户端并发量大,且服务器未能正常感知客户端的退出,也并未及时 close 这些套接字。(如果不及时处理,将会出现没有可用的socket描述符的问题,产生问题的原因,没有及时close)。
正常情况下,一方关闭sockfd,另外一方将会有读事件产生, 当recv数据时,如果返回值为0,表示对端已经关闭。此时我们应该调用close,将对应的sockfd也关闭掉。
不正常情况下,一方关闭sockfd,另外一方并不知道,(比如在close时,自己断网了,对方就收不到发送的数据包)。此时,如果另外一方再向对应的sockfd上写send或读recv数据。
recv时,将会返回0,表示链接已经断开。
send时, 将会产生错误,errno为ECONNRESET。
TIME_WAIT状态存在的理由
----------------------------
TCP/IP协议就是这样设计的,是不可避免的。主要有两个原因:
1)可靠地实现TCP全双工连接的终止
TCP协议在关闭连接的四次握手过程中,最终的ACK是由主动关闭连接的一端(后面统称A端)发出的,如果这个ACK丢失,对方(后面统称B端)将重发出最终的FIN,因此A端必须维护状态信息(TIME_WAIT)允许它重发最终的ACK。如果A端不维持TIME_WAIT状态,而是处于CLOSED 状态,那么A端将响应RST分节,B端收到后将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。
因而,要实现TCP全双工连接的正常终止,必须处理终止过程中四个分节任何一个分节的丢失情况,主动关闭连接的A端必须维持TIME_WAIT状态 。
2)允许老的重复分节在网络中消逝
TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个迟到的迷途分节到达时可能会引起问题。在关闭“前一个连接”之后,马上又重新建立起一个相同的IP和端口之间的“新连接”,“前一个连接”的迷途重复分组在“前一个连接”终止后到达,而被“新连接”收到了。为了避免这个情况,TCP协议不允许处于TIME_WAIT状态的连接启动一个新的可用连接,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个新TCP连接的时候,来自旧连接重复分组已经在网络中消逝。
转 http://blog.csdn.net/whuslei/article/details/6667471
建立TCP需要三次握手才能建立,而断开连接则需要四次握手。整个过程如下图所示:
先来看看如何建立连接的。
首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源。Client端接收到ACK报文后也向Server段发生ACK报文,并分配资源,这样TCP连接就建立了。
那如何断开连接呢?简单的过程如下:
【注意】中断连接端可以是Client端,也可以是Server端。
假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,"告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息"。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,"告诉Client端,好了,我这边数据发完了,准备好关闭连接了"。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,"就知道可以断开连接了"。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!
整个过程Client端所经历的状态如下:
而Server端所经历的过程如下:转载请注明:blog.csdn.net/whuslei
【注意】 在TIME_WAIT状态中,如果TCP client端最后一次发送的ACK丢失了,它将重新发送。TIME_WAIT状态中所需要的时间是依赖于实现方法的。典型的值为30秒、1分钟和2分钟。等待之后连接正式关闭,并且所有的资源(包括端口号)都被释放。
【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。
很多人会问,为什么建链接要3次握手,断链接需要4次挥手?
- 对于建链接的3次握手,主要是要初始化Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的Sequence Number(缩写为ISN:Inital Sequence Number)——所以叫SYN,全称Synchronize Sequence Numbers。也就上图中的 x 和 y。这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP会用这个序号来拼接数据)。
- 对于4次挥手,其实你仔细看是2次,因为TCP是全双工的,所以,发送方和接收方都需要Fin和Ack。只不过,有一方是被动的,所以看上去就成了所谓的4次挥手。如果两边同时断连接,那就会就进入到CLOSING状态,然后到达TIME_WAIT状态。下图是双方同时断连接的示意图(你同样可以对照着TCP状态机看):