Unix网络编程学习笔记之第2章 TCP和UDP

TCP

1. TCP面向连接的协议,是一个字节流协议,没有任何记录边界。发送的是数据分组。

2. TCP提供了可靠性:确认重传和重组

(1) TCP每发送一份数据都会要求对端进行确认。如果超时,就会重传。TCP会估计往返时间RTT,以确定等待多长时间重传。

(2) 如果多次发送数据分组,TCP可以保证分组的按序达到。即会根据序列号进行重组。

3. TCP提供流量控制

TCP在任何时刻通知对端,它此时一次能够接受多少字节的数据,即通告窗口。该窗口指出接受缓冲区当前可用的空间。

4. 为何说TCP是面向连接的?

因为其开始阶段有一个三次握手的过程,且连接之后,服务器端和客户端都会维持一个相同的套接字对,而连接之后,双方就通过对这个套接字对进行通信。

UDP

提供无连接的不可靠的服务。发送的是数据报。

没有可靠性机制,没有流量控制。

TCP连接的三次握手

1. 首先服务端要socket,bind,listen,然后accept阻塞,等待客户端的连接。

2. 客户端socket,调用connect进行主动连接,发送SYN分组。占一个字节。

3. 服务器端接受到SYN分组后,进行ACK确认并捎带自己的SYN分组。占一个字节。

4. 当客户端接受到服务器端的确认ACK后,connect立即返回。然后向服务器端发送ACK确认。

5. 服务器端接受到这个ACK确认后,完成三次握手。

(1) 注意:这里为何服务器端在接受第三次ACK后,可能不立即返回accept?

因为内核为accept设置了两个缓存队列:未连接队列和已连接队列。

当一个SYN来到之后,首先放入未连接队列中,然后当其如果完成了三次握手(即最后一个ACK达到服务器端),则就放入已连接队列中,然后进程调用accept,

内核就从已完成队列队首项返回给进程,accept返回。

详见“4. 基于TCP套接字编程 ”中的“内核是如何进行连接排队的?”

(2) 注意这里SYN的作用是?

SYN的作用:连接的开始。通知对方自己的序列号,以便对以后的重传和重组提供序列号。

一般SYN还含有很多TCP选项:MSS—本端的最大分组大小;窗口规模(即窗口总大小);时间戳等。

TCP终止连接的四次分组

1. 客户端调用close关闭连接,则客户端发送FIN分组。注意:发送FIN分组的意思是本端不会再发送任何数据。

2. 服务端接受这个FIN,然后read函数返回0,并发送对端一个ACK确认。

注意:此时服务器端仍然客户端发送数据是可能的。此时称为半关闭状态。

3. 当服务器端在某个时刻判断read返回0,之后它主动调用close函数进行关闭连接。发送FIN。

注意:此时是程序自己调用close,会发送FIN。当Unix某个进程终止时,它就会关闭其打开的描述符,也会发送FIN。

4. 客户端接受这个FIN,然后发送ACK返回。

注意:可以看出无论是客户端还是服务器端都是可以主动首先关闭的。

TCP连接分组交换过程

这个图很有意思,仔细看

TCP中的TIME_WAIT状态

从上图可知,当客户端接受到服务器端的FIN之后,就会变成TIME_WAIT状态。

该端点停留在这个状态的持续时间是MSL(最长分组生命期)的两倍。有时称2MSL。

1. 为何该端点要维护这个状态?

(1) 可靠地实现TCP全双工连接的终止

上图中假如客户端的发送最终一个ACK丢失,则服务器端会重传上一个FIN,则客户端需要保留状态来处理这个重传的FIN。

(2) 允许老的重复分节在网络中消逝。

假设TCP不维护这个状态,则某个客户端发送最终一个ACK后即结束。如果此时又有重新启动相同IP和端口客户端,则这个新的客户端可能会收到来自服务器端的老的信息(如上述的服务器重传FIN)。造成错误。

而此时TCP维护了这个状态后,TCP就有了一个机制,不会为具有TIME_WAIT状态的连接重新发起新的连接。这就会让老的分组在网络中自动消逝,这也是为何端点维持此状态的时间为2MSL。

端口号

任何时候,TCP/UDP/SCTP都使用16位的整数的端口号。即2个字节的整数。一般是short类型。

端口号分为三类:

众所周知的端口号0~1023。这些端口号一般都有特殊的含义。例如一般80都是web端口。

已登记的端口号1024~49151.这些端口可供用户程序中使用。

动态或私用端口号49152~65535.这些端口也就是临时端口,即如果用户没有指定端口,内核自动给程序分配的临时端口号。

套接字对

一个TCP连接对应着一个套接字对:本地IP,本地端口号,对端IP,对端端口号。

一般服务器需要监听套接字对和已连接套接字对。

例如,某服务器bind的端口号为13,地址为INADDR_ANY,则监听套接字对为{*: 13 , * : *}。

当一个客户端connect之后,则监听套接字对中的本地IP就会被内核选取一个本地IP地址赋值,然后对端套接字就会从内核通过对方的连接信息得到。例如:

{192.2.3.2 : 13 ,  150.2.6.54 : 8265}

这时我们应该维护监听套接字对,以用来监听其他的连接。所以server一般需要两个套接字描述符。而client一般需要一个套接字描述符。

注意:当并行服务器时,TCP用套接字对来标识客户端来的信息应该分配为谁。

TCP输出

每一个TCP套接字都有一个发送缓冲区,应用程序调用write函数时,内核就把应用程序的缓冲区数据全部复制到发送缓冲区。如果成功,write立即返回。如果套接字发送缓冲区容不下应用程序的缓冲区数据(或者应用程序缓冲区过大,或者套接字发送缓冲区已有其他数据),则write函数会被阻塞(如果该套接字是阻塞的)。

直到应用程序的缓冲区的全部数据复制到套接字发送缓冲区才被唤醒。

注意:

1. write函数也可能会被阻塞

2. write函数成功返回,只是代表数据被复制到发送缓冲区,并不代表对端已经接收到。

3. 内核会把套接字发送缓冲区的数据发送给对端,这时并不会删除缓冲区的数据(以便重传),只有对端的ACK来到之后,缓冲区才会把已确认的数据丢掉。

时间: 2024-08-11 20:15:42

Unix网络编程学习笔记之第2章 TCP和UDP的相关文章

Unix网络编程学习笔记之第5章 TCP客户端/服务器程序示例

一. 一个简单TCP回射服务端程序 #include "unp.h" #define MAXLINE 1024 #define PORT 13 #define CONMAX 5 void err_sys(const char* s) { fprintf(stderr, "%s\n",s); exit(1); } void str_echo(int connfd) { int nbyte; char buff[MAXLINE+1]; again: while(nbyt

Unix网络编程学习笔记之第7章 套接字选项

一.获取/设置套接字选项的方法 一个套接字描述符相关联的套接字选项很多.获取/设置套接字选项的方法: 1.  getsockopt和setsockopt函数 2. fcntl函数 3. ioctl函数 二. getsockopt和setsockopt函数 int getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen); int setsockopt(int sockfd, int level,

Unix网络编程学习笔记之第6章 I/O复用:select和poll函数

一.I/O复用应用场合 1. 当客户处理多个描述符(既有标准输入,又有网络套接字)时,必须使用IO复用. 2. 一个客户同时处理多个套接字是可能的. 3. 如果一个服务器既要处理监听套接字,又要处理已连接套接字,一般就要使用I/O复用. 4. 如果一个服务器既要处理TCP,又要处理UDP,一般就要I/O复用. 5. 如果一个服务器要处理多个服务或协议,就要用到I/O复用. 其实IO复用就是一个进程/线程处理多个套接字描述符. 二. I/O模型 Unix提供了5种I/O模型: 1. 阻塞式I/O模

Unix网络编程学习笔记之第8章 基于UDP套接字编程

一. UDP C/S的典型函数调用 UDP没有像TCP那样的连接,客户端直接sendto向某服务器发送数据,服务器端一直recvfrom阻塞,以接收任何客户端发送的数据. 二. sendto和recvfrom函数 int sendto(int sockfd, const void* buff, size_t nbytes, int flag, const struct sockaddr* to, socklen_taddrlen); int recvfrom(int sockfd, void*

Unix网络编程学习笔记之第11章 名字与地址转换

一. 域名系统(DNS) 1. 简介 DNS主要用于主机名和IP地址之间的映射. 主机名可以是简单的名字ljm,也可以是全限定域名ljm.localdomainbaidu.com等. 2.资源记录 DNS中的条目称为资源记录(RR).我们感兴趣的RR类型只有几个: A             A记录把一个主机名映射为一个32位的IPv4地址. AAAA    4A记录把一个主机名映射为一个128位的IPv6地址. 例如: ljm               IN      A    127.0.

Unix网络编程学习笔记之第1章 简介

一.一个简单的时间获取客户端 #include <sys/socket.h> #define MAXCON 50 #define MAXLINE 1024 #define PORT 13 void err_sys(const char* s) { fprintf(stderr, "%s\n",s); exit(1); } int main(int argc, char** argv) { int sockfd; structsockaddr_in servaddr; cha

Unix网络编程学习笔记之第12章 IPv4与IPv6的互操作性

一. 简介 假设我们本章讨论的主机都是支持双栈的,即支持IPv4地址,也支持Ipv6地址. 我们本次讨论的点:客户端与服务器端使用的是不同类型的地址.因为相同类型的地址没什么可讲的. 二. IPv4客户端与IPv6服务器 即,客户端使用IPv4地址套接字来通信,服务器端使用IPv6地址套接字通信. 原理: 0. 首先IPv6服务器主机保证既有IPv4地址,又有IPv6地址. 1. IPv4客户端通过getaddrinfo函数,找到服务器端的IPv4地址,然后进行连接. 2. 来自客户端的IPv4

Unix网络编程学习笔记之第4章 基于TCP套接字编程

1. socket函数 int socket(int family, int type,int protocol) 成返回一个套接字描述符.错误返回-1 其中family指定协议族,一般IPv4为AF_INET, IPv6为AF_INET6. 其中type指定套接字类型,字节流:SOCK_STREAM.   数据报:SOCK_DGRAM. 一般情况下通过family和type的组合都可以唯一确定一个套接字类型.所以一般我们就把protocol设为0就可以了. 有时在某些特殊情况下,family和

UNIX网络编程:socket套接字(TCP与UDP)

套接字简介: 套接字是网络编程中的一种通信机制,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程.应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题.凭借这种机制,客户/服务器系统的开发工作既可以在本地单机上进行,也可以跨网络进行,Linux所提供的功能(如打印服务,ftp等)通常都是通过套接字来进行通信的,套接字的创建和使用与管道是有区别的,