套接字的多种可选项

套接字可选项和I/O缓冲大小

套接字的多种可选项

套接字可选项分为 IPPROTO_IPIPPROTO_TCPSOL_SOCKET 三层,各层的含义为:
IPPROTO_IP:IP 协议相关事项;
IPPROTO_TCP:TCP 协议相关事项;
SOL_SOCKET:套接字相关的通用可选项。
下表列出了其中部分可选项,这些可选项无需立即掌握,用到什么学什么即可。
套接字的多种可选项_第1张图片
套接字的多种可选项_第2张图片

函数 - getsockopt & setsockopt

可选项的读取和设置由如下两个函数来是实现:
getsockopt & setsockopt

#include 
int getsockopt(int sock, int level, int optname, void* optval, socklen_t* optlen);
// 功能:读取套接字可选项
// 参数:sock:套接字文件描述符;level:可选项所属协议层;optname:要查看的可选项名称;
// optval:用于保存查看结果的缓冲地址;optlen:调用函数后,optlen 会保存 optval 返回的可选信息项的字节数
// 返回值:成功时返回 0,失败时返回 -1。
int setsockopt(int sock, int level, int optname, void* optval, socklen_t optlen);
// 功能:更改套接字可选项
// 参数:sock:套接字文件描述符;level:可选项所属协议层;optname:要更改的可选项名称;
// optval:保存要更改的选项信息的缓冲地址;optlen:指明参数 optval 所指对象的大小
// 返回值:成功时返回 0,失败时返回 -1。

示例:

#include 
#include 
#include 
#include 

void error_handling(char *message);

int main(int argc, char *argv[]) 
{
	WSADATA wsaData;
	int tcp_sock, udp_sock;
	int sock_type;
	int optlen;
	int state;
	
	if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0)
		error_handling("WSAStartup() error!"); 
	
	optlen=sizeof(sock_type);
	tcp_sock=socket(PF_INET, SOCK_STREAM, 0);
	udp_sock=socket(PF_INET, SOCK_DGRAM, 0);	
	printf("SOCK_STREAM: %d \n", SOCK_STREAM);
	printf("SOCK_DGRAM: %d \n", SOCK_DGRAM);
	
	state=getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
	if(state)
		error_handling("getsockopt() error!");
	printf("Socket type one: %d \n", sock_type);
	
	state=getsockopt(udp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
	if(state)
		error_handling("getsockopt() error!");
	printf("Socket type two: %d \n", sock_type);
	
	WSACleanup();
	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

选项 - SO_SNDBUF & SO_RCVBUF

创建套接字同时将生成 I/O 缓冲
SO_SNDBUF 可选项表示输出缓冲大小相关信息,SO_RCVBUF 可选项表示输入缓冲大小相关信息。
这两个选项都是可读可写的。默认的输入输出缓冲大小可能在几万字节(几十)左右。
可以修改缓冲区大小,但是系统并不一定会完全按照我们的要求进行修改,修改结果可能会有所出入。

示例:

#include 
#include 
#include 
#include 

void error_handling(char *message);

int main(int argc, char *argv[])
{
	WSADATA wsaData;
	int sock;  
	int snd_buf, rcv_buf, state;
	int len;
	
	if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0)
		error_handling("WSAStartup() error!"); 
	
	sock=socket(PF_INET, SOCK_STREAM, 0);	
	len=sizeof(snd_buf);
	state=getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, &len);
	if(state)
		error_handling("getsockopt() error");
	
	len=sizeof(rcv_buf);
	state=getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, &len);
	if(state)
		error_handling("getsockopt() error");
	
	printf("Input buffer size: %d \n", rcv_buf);
	printf("Outupt buffer size: %d \n", snd_buf);
	
	WSACleanup();
	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

选项 - SO_REUSEADDR

time-wait状态

主动结束连接的一方(先发送 FIN 消息的)会经历 time-wait 状态。

地址分配错误(bind() error)

当使用 Ctrl+C 终止服务器程序时,服务器程序成为主动终止连接的一方,会经历 time-wait 状态。这时服务器之前所用的套接字是无法立即使用的(如果立即执行会发生 bind() error),只能等几分钟再执行或修改端口号(即修改套接字)。
客户端的 time-wait 状态无需关心,因为它的端口号是动态分配的。

地址再分配

有时需要立即重启服务器程序,这可以通过更改可选项 SO_REUSEADDR 的状态来实现。
SO_REUSEADDR 的默认值为 0,将其修改为 1 即可将 time-wait 状态下的套接字端口号重新分配给新的套接字。

使用方式:

int option = 1;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&option, sizeof(option));

选项 - TCP_NODELAY

Nagle 算法是应用于 TCP 层的一个简单算法:只有收到前一数据的 ACK 消息时,Nagle 算法才会发送下一数据。
TCP 默认使用 Nagle 算法,因此会最大限度地进行缓冲,直到收到 ACK 才将数据发送出去。
套接字的多种可选项_第3张图片
Nagle 算法的优点:可以避免产生大量网络流量。如果不使用 Nagle 算法,数据到达输出缓冲后立即发送出去,会产生多个体积很小的包(如上图所示),增加网络负载。
Nagle 算法的缺点:很多时候会降低传输速度。不使用 Nagle 算法时,数据无需等待 ACK 报文就可以发送出去,没有等待时间。在发送大文件数据时尤其明显。因为传输大文件数据无论是否使用 Nagle 算法都不会产生大量的小数据包,而不使用 Nagle 算法则不用等待 ACK 报文,速度更快。
应根据情况选择是否禁用 Nagle 算法。

禁用Nagle算法

可选项 TCP_NODELAY 默认为 0,表示开启 Nagle 算法,将其修改为 1 即可禁用 Nagle 算法。
禁用 Nagle 算法的方式:

int opt_val = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&opt_val, sizeof(opt_val));

可以通过 TCP_NODELAY 的值查看 Nagle 算法的设置状态。

int opt_val = 1;
int opt_len = sizeof(opt_val);
getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&opt_val, &opt_len);

如果正在使用 Nagle 算法,opt_val 变量中会保存 0;如果已禁用 Nagle 算法,则保存 1。

你可能感兴趣的:(网络,c++)