UNIX-LINUX套接字描述符
- 套接字描述符是一个整数值,用于唯一标识进程中的一个开放的网络连接或者套接字
- 在
UNIX
和Linux
系统中,套接字描述符是一种特殊类型的文件描述符,可以用于表示打开的文件、管道、设备或者网络连接
- 在
- 套接字描述符唯一标识了进程中的一个网络连接
- 在创建新的套接字(例如通过
socket
函数)时,将返回一个新的描述符
- 在创建新的套接字(例如通过
- 可以使用像
read
、write
、send
、recv
等函数来通过套接字描述符读取或写入数据。这些函数的工作方式与文件描述符非常相似 - 可以使用如
setsockopt
和getsockopt
等函数来查询或修改与套接字描述符相关联的各种选项和参数 - 可以使用像
select
和poll
等函数,通过套接字描述符来同时监视多个网络连接的状态 - 当连接不再需要时,可以使用
close
函数来关闭套接字描述符,从而终止网络连接
WINDOWS套接字描述符
- 在
Windows
中,套接字由一个称为“句柄”的特殊对象表示,而不是一个整数- 句柄用于引用系统内核中的对象,包括文件、线程、进程和窗口,以及套接字
- 套接字句柄与文件句柄不共享同一命名空间,并且不能用标准文件操作函数进行操作
Windows
中的套接字操作使用特定于Windows
的Winsock API
进行- 该
API
提供了与BSD
套接字相似的接口,但是使用了特定于Windows
的数据类型和函数
- 该
- 区别于
Linux
:- 在
Windows
中创建套接字并返回句柄,而在Linux
中创建套接字并返回一个整数描述符 - 在
Windows
中使用Winsock API
的函数(如send
和recv
)进行网络I/O
,而在Linux
中可以使用通用的文件I/O
函数(如read
和write
) - 在
Windows
中关闭套接字使用closesocket
函数,而在Linux
中使用通用的close
函数
- 在
socket
概述
- 用于创建一个新的套接字,并返回一个套接字描述符
- 套接字是进行网络通信的端点,可以用于创建
TCP
或UDP
连接
- 套接字是进行网络通信的端点,可以用于创建
原型
1 |
int socket(int domain, int type, int protocol); |
参数
- 套接字所使用的地址域(或协议族)。常见的值有:
AF_INET
:IPv4
协议族AF_INET6
:IPv6
协议族AF_UNIX
:本地套接字(用于进程间通信)
- 套接字的类型。主要选项有:
SOCK_STREAM
:流式套接字,用于TCP
连接。SOCK_DGRAM
:数据报套接字,用于UDP
连接。- 其他一些特殊类型,如
SOCK_RAW
用于创建原始套接字
- 特定协议的编号,通常与
type
参数相对应。常见的值有:- 如果
type
是SOCK_STREAM
,protocol
通常设置为IPPROTO_TCP
- 如果
type
是SOCK_DGRAM
,protocol
通常设置为IPPROTO_UDP
- 在许多情况下,可以简单地将
protocol
设置为0
,系统将自动选择与type
相匹配的协议
- 如果
返回值
- 成功时,
socket
函数返回一个非负整数,代表新创建的套接字描述符 - 失败时,返回
-1
,并设置相应的错误代码
示例
1 2 3 4 5 |
int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } |
注意事项
- 创建的套接字描述符最初是“未绑定”的,即没有分配本地地址
- 通常,接下来需要使用
bind
函数将套接字绑定到一个特定的地址和端口
- 通常,接下来需要使用
- 对于
TCP
套接字,还需要调用listen
和accept
函数来接受新的连接,或使用connect
函数主动连接到远程端点 - 创建套接字后,还可以使用各种套接字选项来进一步自定义其行为,例如通过
setsockopt
函数
bind
概述
- 用于将套接字与特定的
IP
地址和端口号绑定 - 这个操作在创建服务器套接字并开始侦听客户端连接之前是必要的
原型
1 |
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
参数
- 套接字描述符,由
socket
函数返回 - 指向结构体
sockaddr
的指针,包含要绑定的IP
地址和端口号 addr
指向的地址结构体的大小
返回值
- 成功时,返回
0
- 失败时,返回
-1
,并设置errno
以指示发生了什么错误
示例
- 这个示例中,使用
INADDR_ANY
绑定到服务器上所有可用的接口- 如果你想绑定到特定的
IP
地址,你可以使用诸如inet_pton
之类的函数来转换它
- 如果你想绑定到特定的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <sys/socket.h> #include <netinet/in.h> int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { // 处理错误 } struct sockaddr_in my_addr; my_addr.sin_family = AF_INET; my_addr.sin_port = htons(8080); // 端口号,使用htons进行字节序转换 my_addr.sin_addr.s_addr = INADDR_ANY; // 绑定到所有可用的接口 if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) { // 处理错误 } |
注意事项
bind
函数只能在套接字上调用一次- 如果端口号已被其他进程使用,
bind
调用将失败 - 在服务器套接字上,
bind
通常后跟listen
调用,以便开始接受连接
listen
概述
- 用于将未连接的套接字转换为被动套接字
- 表示内核应接受到此套接字的传入连接请求
- 通常用于服务器编程,用于准备接受客户端连接
原型
1 |
int listen(int sockfd, int backlog); |
参数
- 已绑定(通过
bind
函数)未连接的套接字描述符 - 未完成连接队列的最大长度
- 这指定了内核应该为特定套接字排队的最大挂起连接数
返回值
- 成功时,返回
0
- 失败时,返回
-1
,并设置errno
以指示发生了什么错误
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <sys/socket.h> #include <netinet/in.h> int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { // 处理错误 } struct sockaddr_in my_addr; my_addr.sin_family = AF_INET; my_addr.sin_port = htons(8080); my_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) { // 处理错误 } if (listen(sockfd, 5) < 0) { // 最多允许5个挂起的连接 // 处理错误 } // 服务器现在准备好接受连接 |
注意事项
listen
函数不会返回新连接;它只准备套接字以接受它们- 使用
accept
函数来实际接受连接
- 使用
backlog
参数是指定系统允许排队的最大挂起连接数- 这不包括已经接受但尚未由进程获取的连接。如果连接到达并且挂起队列已满,则客户端可能会收到错误
- 一旦调用了
listen
,套接字就不能用于发起连接,只能用来接受它们
accept
概述
- 用于接受一个到达的客户端连接请求
- 在服务器端的编程中,
accept
通常与bind
、listen
一起使用,以便设置服务器并等待并处理客户端的连接请求
原型
1 |
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); |
参数
- 一个已经绑定并处于监听状态的套接字描述符
- 一个结构,用于存储被接受的连接的客户端地址信息
- 一个指向整数的指针,该整数包含客户端地址结构的大小
返回值
- 成功时,返回一个新的套接字描述符,该描述符代表与客户端的新连接
- 所有的通信都应该通过这个新的描述符进行
- 失败时,返回
-1
,并设置errno
以指示发生了什么错误
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <sys/socket.h> #include <netinet/in.h> int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { // 处理错误 } struct sockaddr_in my_addr; my_addr.sin_family = AF_INET; my_addr.sin_port = htons(8080); my_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) { // 处理错误 } if (listen(sockfd, 5) < 0) { // 处理错误 } struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); int newsockfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addr_len); if (newsockfd < 0) { // 处理错误 } // 使用newsockfd与客户端通信 |
注意事项
accept
函数可能会被阻塞,如果没有连接请求到达- 可以通过将套接字设置为非阻塞或使用诸如
select
、poll
之类的函数来避免这种情况
- 可以通过将套接字设置为非阻塞或使用诸如
- 返回的新套接字描述符与原始监听套接字描述符分开,有其自己的缓冲区、协议状态等
- 新描述符用于与特定客户端通信,而监听描述符继续用于接受新的连接请求
- 通常,每个连接请求都会生成一个新的套接字描述符
- 因此,服务器可能会同时管理多个连接
- 你可以通过
addr
参数获取到连接客户端的地址信息,例如IP
地址和端口号
recv
概述
- 用于从已连接的套接字接收数据
- 该函数通常在
TCP
客户端和服务器编程中使用
- 该函数通常在
原型
1 |
ssize_t recv(int sockfd, void *buf, size_t len, int flags); |
参数
- 已连接的套接字描述符
- 缓冲区,用于存储接收到的数据
buf
缓冲区的大小(字节数)- 接收操作的选项标志
- 例如
MSG_PEEK
、MSG_OOB
等。通常可以设置为0
- 例如
返回值
- 成功时,返回接收到的字节数。如果连接已关闭,则返回
0
- 失败时,返回
-1
,并设置errno
以指示发生了什么错误
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 省略绑定和监听代码 struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); int newsockfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addr_len); if (newsockfd < 0) { // 处理错误 } char buffer[1024]; ssize_t n = recv(newsockfd, buffer, sizeof(buffer), 0); if (n > 0) { buffer[n] = '\0'; printf("Received: %s\n", buffer); } else if (n == 0) { printf("Connection closed\n"); } else { // 处理错误 } close(newsockfd); close(sockfd); return 0; } |
注意事项
recv
函数可能会被阻塞,如果没有数据可读- 可以通过将套接字设置为非阻塞或使用诸如
select
、poll
之类的函数来避免这种情况
- 可以通过将套接字设置为非阻塞或使用诸如
- 如果缓冲区小于接收的数据大小,则多余的数据会被丢弃
- 如果设置了
MSG_PEEK
标志,那么数据会被复制到缓冲区,但不会从输入队列中移除,所以下一次调用recv
时还可以读到同样的数据
recv与接收缓冲区
recv
用于从套接字中接收数据,其工作方式与底层的接收缓冲区(Receive Buffer
)紧密相关- 接收缓冲区是
TCP/IP
协议栈中的一个关键组成部分
- 接收缓冲区是
send
概述
- 用于通过已连接的套接字发送数据
- 它通常用于 TCP `客户端和服务器编程
原型
1 |
ssize_t send(int sockfd, const void *buf, size_t len, int flags); |
参数
- 已连接的套接字描述符
- 包含要发送数据的缓冲区
- 要发送的字节数
- 发送操作的选项标志
- 例如
MSG_NOSIGNAL
、MSG_OOB
等。通常可以设置为0
- 例如
返回值
- 成功时,返回实际发送的字节数
- 失败时,返回
-1
,并设置errno
以指示发生了什么错误
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); // 省略设置服务器IP地址的代码 connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); const char *message = "Hello, Server!"; ssize_t n = send(sockfd, message, strlen(message), 0); if (n < 0) { // 处理错误 } else { printf("Sent %zd bytes\n", n); } close(sockfd); return 0; } |
注意事项
send
函数可能会被阻塞- 如果套接字的发送缓冲区满了。可以通过将套接字设置为非阻塞或使用诸如
select
、poll
之类的函数来避免这种情况
- 如果套接字的发送缓冲区满了。可以通过将套接字设置为非阻塞或使用诸如
- 返回的字节数可能小于要发送的字节数,特别是在非阻塞模式下
- 在这种情况下,应用程序应该再次尝试发送剩余的数据
- 如果设置了
MSG_OOB
标志,则发送的数据会被视为"带外"数据- 带外数据是一种紧急机制,允许在正常数据流之外发送额外的信息
- 发送数据的成功并不意味着接收方已经收到了数据
TCP
提供了可靠传输,但确认接收是在较低的协议级别处理的
send与发送缓冲区
send
函数用于将数据发送到套接字连接的另一端,其工作方式与发送缓冲区(Send Buffer
)紧密相关- 发送缓冲区在
TCP/IP
协议栈中起着重要的作用
- 发送缓冲区在
connect
概述
- 这个函数是在客户端用来与服务器建立连接的
- 它尝试建立与指定的远程主机和端口的连接
原型
1 |
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
参数
- 套接字描述符,由
socket
函数返回 - 指向一个
sockaddr
结构的指针,该结构包含了你想连接的远程地址和端口 addr
所指向的结构的大小,通常设置为sizeof(struct sockaddr)
返回值
- 成功时返回
0
- 失败时返回
-1
,并设置errno
来表示错误类型
详细
- 当你调用
connect
时,系统会执行TCP
的三次握手过程,并将套接字从CLOSED
状态移动到ESTABLISHED
状态(如果是TCP
套接字)- 一旦连接建立成功,你就可以使用
send
和recv
函数来发送和接收数据了
- 一旦连接建立成功,你就可以使用
connect
也可以用于UDP
,尽管UDP
是无连接的- 在
UDP
的情况下,connect
不会尝试建立连接,但会将套接字与特定的远程地址和端口关联起来 - 可以使用
send
和recv
(而不是sendto
和recvfrom
)与那个特定的远程主机通信 - 当使用
connect
关联了远程地址和端口之后,你就不需要再使用sendto
和recvfrom
来指定这些细节了
- 在
- 在非阻塞模式下,
connect
也可以异步工作- 它可能会立即返回,在连接尚未建立时你可以继续执行其他任务
- 你可以使用
select
,poll
或其他机制来检测连接何时完成
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket"); return -1; } struct sockaddr_in serv_addr; serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(80); inet_pton(AF_INET, "192.168.1.1", &serv_addr.sin_addr); if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { perror("connect"); return -1; } // Connection is established, data can be sent/received return 0; } |
注意事项
- 对于无连接的协议(例如
UDP
),connect
也有用处 - 虽然
UDP
本身不需要连接,但你可以通过调用connect
为UDP
套接字指定默认的目的地址- 这样,在以后的
send
和recv
调用中就不需要重复指定目的地址
- 这样,在以后的
sendto
概述
- 用于无连接的数据报协议(例如
UDP
),用于向指定的目的地址发送数据 - 与
send
函数不同,sendto
允许你每次发送时指定不同的目的地址
原型
1 2 3 4 5 6 |
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); |
参数
- 套接字描述符,表示你想通过哪个套接字发送数据
- 指向你想发送的数据的指针
- 你想发送的字节数
- 可选的发送标志,通常可以设置为
0
- 指向一个
sockaddr
结构的指针,其中包含你想发送数据的目的地址和端口 dest_addr
所指向的结构的大小,通常设置为sizeof(struct sockaddr)
返回值
- 返回值是实际发送的字节数
- 如果出现错误则返回
-1
,可以通过errno
来检查具体错误
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket"); return -1; } struct sockaddr_in dest_addr; dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(8000); inet_pton(AF_INET, "192.168.1.1", &dest_addr.sin_addr); const char *message = "Hello, World!"; ssize_t sent = sendto(sockfd, message, strlen(message), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); if (sent < 0) { perror("sendto"); return -1; } printf("Sent %zd bytes\n", sent); return 0; } |
注意事项
sendto
函数使得你可以轻松地向不同的目的地发送数据报,无需每次都更改套接字的“连接”状态或使用不同的套接字- 这对于实现各种无连接的网络协议非常有用
recvfrom
概述
- 用于在无连接的数据报协议(例如
UDP
)上接收数据- 该函数接收数据报并捕获数据报的来源地址
原型
1 2 3 4 5 6 |
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); |
参数
- 套接字描述符,标识你希望从哪个套接字接收数据
- 指向一个缓冲区的指针,其中将存储接收到的数据
- 缓冲区的大小,即你想接收的最大字节数
- 接收标志,通常可以设置为
0
- 指向一个
sockaddr
结构的指针,用于保存发送方的地址信息 - 输入/输出参数
- 调用
recvfrom
时,你必须设置它为src_addr
所指向的结构的大小 recvfrom
返回时,该值将被设置为实际地址的大小
- 调用
返回值
- 返回值是实际接收的字节数
- 如果出现错误则返回
-1
,并可以通过errno
来检查具体错误
示例
- 创建了一个
UDP
套接字,并绑定到端口8000
上- 然后,它使用
recvfrom
函数接收一个数据报,并打印接收到的内容和来源地址
- 然后,它使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket"); return -1; } struct sockaddr_in servaddr; servaddr.sin_family = AF_INET; servaddr.sin_port = htons(8000); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { perror("bind"); return -1; } char buffer[1024]; struct sockaddr_in src_addr; socklen_t addrlen = sizeof(src_addr); ssize_t received = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&src_addr, &addrlen); if (received < 0) { perror("recvfrom"); return -1; } buffer[received] = '\0'; // null-terminate the received string printf("Received %zd bytes from %s: %s\n", received, inet_ntoa(src_addr.sin_addr), buffer); return 0; } |
接收缓冲区
定义
- 接收缓冲区是一块内核分配的内存区域,用于暂存从网络接收但尚未被应用程序读取的数据
大小
- 缓冲区的大小对性能有重要影响,太小可能导致阻塞,太大可能浪费内存
- 许多操作系统允许动态调整缓冲区大小,并可能具有自动调整机制
数据流入
- 当
TCP
数据包到达,并且通过TCP
的各种检查后(例如序列号检查、校验和验证等),数据会被复制到接收缓冲区 - 如果缓冲区已满,新的数据包可能会被丢弃,
TCP
的流量控制机制会通知发送方减慢发送速率
应用程序读取
- 当应用程序调用
recv
函数来读取数据时,数据会从接收缓冲区复制到应用程序指定的内存位置 - 如果缓冲区为空,
recv
的行为取决于套接字是否设置为阻塞模式- 在阻塞模式下,
recv
会等待数据到来 - 在非阻塞模式下,
recv
会立即返回一个错误
- 在阻塞模式下,
流量控制
- 接收缓冲区的大小和使用情况是
TCP
流量控制的关键因素之一 TCP
窗口大小通常与接收缓冲区的剩余空间有关- 如果接收缓冲区满了,窗口大小可能会缩小,从而通知发送方减慢发送速率
缓冲区调整
- 操作系统通常允许通过套接字选项(例如
SO_RCVBUF
)调整接收缓冲区的大小- 在高性能的网络应用中,合理调整缓冲区大小可能有助于优化性能
与阻塞和超时的交互
recv
函数的行为还可能受到与套接字关联的阻塞模式和超时设置的影响- 例如,可以设置
recv
的超时,以避免长时间等待缓冲区中的数据
- 例如,可以设置
与其他接收选项的交互
recv
函数还支持一些标志- 例如
MSG_PEEK
,允许应用程序在不实际删除数据的情况下检查接收缓冲区的内容
- 例如
发送缓冲区
定义
- 发送缓冲区是一块内核分配的内存区域,用于暂存由应用程序发送但尚未被
TCP
协议栈发送到网络的数据
大小
- 缓冲区的大小对性能和资源使用有关键影响,通常可以通过套接字选项(例如
SO_SNDBUF
)进行调整
数据流出
- 当应用程序调用
send
函数发送数据时,数据会被复制到发送缓冲区 - 如果缓冲区已满,
send
的行为取决于套接字是否设置为阻塞模式- 在阻塞模式下,
send
会等待缓冲区有空间 - 在非阻塞模式下,
send
会立即返回一个错误
- 在阻塞模式下,
TCP协议的交互
TCP
协议栈会逐渐将数据从发送缓冲区发送到网络- 发送过程受到
TCP
的流量控制、拥塞控制和可靠传输机制的影响- 例如,如果接收端的接收缓冲区满了,发送方可能会暂停发送
确认与重传
- 在
TCP
连接中,发送的数据需要得到接收端的确认。 - 确认的数据会从发送缓冲区中移除
- 如果某些数据未得到确认,可能会触发
TCP
的重传机制,数据将从发送缓冲区重新发送
- 如果某些数据未得到确认,可能会触发
缓冲区调整与优化
- 在某些情况下,可能需要调整发送缓冲区的大小以适应特定的网络条件或应用需求
- 合理的缓冲区大小可以帮助确保高效的网络利用,而不是浪费带宽或内存
与其他发送选项的交互
send
函数支持一些标志- 例如
MSG_DONTROUTE
,这些标志可能会影响发送缓冲区如何与底层的TCP
协议栈交互
- 例如
与应用程序的交互
- 发送缓冲区的状态可能会影响应用程序的性能和行为
- 例如,一个满的发送缓冲区可能会导致应用程序阻塞
- 而一个空的发送缓冲区可能会触发应用程序继续发送更多数据
函数理解解析
套接字队列
- 对于面向连接的套接字(例如,
TCP
套接字),操作系统通常为每个套接字(socket
)维护了两个队列:- 已完成连接的队列
- 未完成连接的队列
已完成连接的队列
- 当三次握手完成并成功建立连接后,这些连接会被放在已完成连接的队列中
- 它们已经完成了
TCP
三次握手并成功地建立了一个连接
- 它们已经完成了
未完成连接的队列
- 当一个客户端尝试连接到服务器,但是三次握手还没有完成,这些连接会被放在未完成连接的队列中
- 例如,如果已经发送了
SYN
包,但还没有收到客户端的ACK
包,这种连接就会在这个队列中
- 例如,如果已经发送了
listen的backlog
- 这个参数定义了操作系统应该为特定套接字队列的最大长度
- 具体来说,它指的是已完成连接的队列的最大长度
- 换句话就是,这个参数指定了操作系统为这个套接字维护的所谓已完成的连接的队列的最大数量
- 而未完成连接队列(也叫半连接队列),它的大小通常与这个参数无关,它仅用于存放处于三次握手过程中的连接请求
- 注意:
- 根据操作系统的实现,实际的队列长度可能会受到系统级别限制,或者可能考虑到未完成连接的队列
- 也就是说,
backlog
参数只是一个请求值,操作系统可能会选择使用这个值,也可能会对其进行调整
例如,有些系统可能会将实际的队列长度设置为backlog
参数的两倍
- 当有客户端尝试连接并且已完成连接的队列已满时,新的连接请求可能会被拒绝
- 同样,如果未完成连接的队列已满,后续的SYN请求也可能被丢弃
- 当服务器调用
accept
函数时,它会从已完成连接的队列中取出一个连接- 如果这个队列为空,根据套接字的阻塞模式,
accept
要么阻塞,要么立即返回一个错误
- 如果这个队列为空,根据套接字的阻塞模式,
发送(接收)缓冲区是为每个套接字维护了一个么
- 操作系统为每个套接字维护了一个发送缓冲区和一个接收缓冲区
- 这两个缓冲区存在于内核
- 应用程序调用
send
时,数据会从应用程序的用户空间复制到内核空间的发送缓冲区
然后,操作系统的网络协议栈会从发送缓冲区获取数据并将其发送到网络 - 当数据从网络到达时,它会首先被放入内核空间的接收缓冲区
然后,应用程序通过调用recv
等函数从接收缓冲区中读取数据,此时数据会被复制到用户空间
- 应用程序调用
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Linux 高性能服务器编程:网络基础编程一11/27
- ♥ 51CTO:C++编程技巧与规范08/01
- ♥ Windows进程通信相关03/10
- ♥ Soui六06/01
- ♥ Linux 高性能服务器编程:高性能服务器架构一12/05
- ♥ C++标准库 _string04/16