TCP套接字的创建 socket() 创建的是监听套接字: 例如绑定:IPv4、TCP协议
AF_INET 表示使用IPv4地址
SOCK_STREAM 表示使用TCP协议
0 表示使用默认的协议(即TCP)
在创建套接字之后,可以使用setsockopt函数设置一些套接字选项,例如:
SO_REUSEADDR: 允许在服务器退出后立即重用本地地址和端口。
TCP_NODELAY: 禁用Nagle算法,立即发送小数据包
准备地址结构:
sin_family 指定地址族为 AF_INET。
sin_addr.s_addr 指定绑定的IP地址。INADDR_ANY 表示绑定到所有可用的网络接口。
sin_port 指定绑定的端口号,使用 htons 将主机字节序转换为网络字节序
此时套接字只是一个文件描述符,还没有进行网络相关的绑定
bind() 将服务器IP与端口与套接字关联
listen()将套接字转换为监听状态,BACKLOG 参数指定最大连接队列长度
客户端的创建套接字,类似于服务器端,创建一个新的套接字
准备服务器地址结构
sin_port 指定服务器的端口号
inet_pton 函数将字符串形式的IP地址转换为网络字节序
connect 函数尝试连接到服务器。操作系统会自动分配一个本地端口,并发起三次握手
客户端发起连接
客户端发送一个SYN同步序列号包,请求建立连接,包含的信息:seq=x,表示客户端初始序列号
服务器响应
服务器收到SYN包后,发送一个SYN-ACK(同步确认序列号)包,表示接受连接请求。
包含的信息:seq=y, ack=x+1,表示服务器的初始序列号和对客户端SYN包的确认
客户端确认
客户端收到SYN-ACK包后,发送一个ACK(确认)包,表示已接受服务器的连接请求。
包含的信息:ack=y+1,表示对服务器SYN包的确认
三次握手的建立
客户端 服务器
| |
|------- SYN (seq=x) --------------> | # 客户端发起连接
| |
|<----- SYN-ACK (seq=y,ack=x+1) ---- | # 服务器确认并响应
| |
|------- ACK (ack=y+1) -------------> | # 客户端确认
| |
连接建立 连接建立
服務端通過accept接受連接,accept返回新的通信套接字
操作系统会为每个连接维护信息 ,信息如下:
本地IP和端口
远端IP和端口
连接状态
序列号
确认号
窗口大小
缓冲区信息
定时器
服务器发送数据,写入数据到通信套接字,详细过程:
数据写入发送缓冲区
TCP协议将数据分段
添加TCP头部(源端口、目标端口、序列号等)
添加IP头部(源IP、目标IP)
发送数据包
客户端接收数据,从套接字读取数据,详细过程:
操作系统接收数据包
检查IP头部和TCP头部
根据四元组找到对应套接字
数据放入接收缓冲区
发送确认包(ACK)
应用程序读取数据
疑难解答:
客户端在没有调用bind()情况下,connect()如何工作
当客户端创建一个套接字并直接调用 connect() 时 操作系统会自动为该套接字分配一个本地IP地址和一个临时的的端口号 这个本地IP地址通常是用于到达服务器IP的网络接口的IP 端口号则从可用的临时端口范围内分配 这样 客户端套接字就绑定了自己的本地IP和端口 并连接到服务器的IP和指定端口 因此 即使客户端没有显式调用 bind() 在调用 connect() 后 套接字实际上已经被系统绑定了本地的IP和端口
服务器的监听套接字和通信套接字有什么区别
监听套接字:这是服务器最初创建、绑定了特定IP和端口、并调用了 listen() 的套接字。它的主要作用是监听新的连接请求,不用于实际的数据传输。
通信套接字:这是 accept() 返回的新套接字 每当有一个新的客户端连接 accept() 都会返回一个新的通信套接字 这个套接字专用于与特定客户端的数据通信
服务器 listen() 后是否暂停运行?还是暂停在 accept()
服务器调用 listen() 后并不会暂停运行 listen() 只是将套接字设置为监听状态 真正使服务器阻塞等待的是 accept() 函数 accept() 会阻塞直到有新的连接请求到来
1693

被折叠的 条评论
为什么被折叠?



