套接字可选项和I/O缓冲大小
套接字多种可选项
我们之前写的程序都是创建好套接字后(未经特别操作)直接使用的,此时通过默认的套接字特性进行数据通信,有时需要对套接字的特性进行更改。
套接字可选项是分层的,IPPROTO_IP层可选项是IP协议相关事项,IPPROTO_TCP层可选项是TCP协议相关的事项,SOL_SOCKET层是套接字相关的通用可选项。
可选项的读取和设置
通过getsockopt函数读取可选项
通过setsockopt函数设置可选项
示例
查看缓冲区的大小
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
int snd_buf, rcv_buf, state;
socklen_t len;
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);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
设置缓冲区的大小
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
int snd_buf=1024*3, rcv_buf=1024*3;
int state;
socklen_t len;
sock=socket(PF_INET, SOCK_STREAM, 0);
state=setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, sizeof(rcv_buf));
if(state)
error_handling("setsockopt() error!");
state=setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, sizeof(snd_buf));
if(state)
error_handling("setsockopt() error!");
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("Output buffer size: %d \n", snd_buf);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
输出结果和我们设置的不同,具体原因可以参考这篇文章Socket缓冲区大小修改与系统设置_mfence的博客-CSDN博客_socket缓冲区大小设置
SO_REUSEADDR
在之前的回声客户端与服务器端的程序中,客户端先请求断开连接,重新运行服务器端也不会有什么问题;但如果在客户端与服务器端已建立连接的状态下,向服务器端控制台输入(CTRL+C),即强制关闭服务器端,那么使用同一端口号重新运行服务器端时,将输出"bind() error"消息,无法再次运行,再过大约3分钟可重新运行服务器端。
Time-wait状态
套接字经过四次挥手过程后并非立即消除,而是要经过一段时间的Time-wait状态。只有先断开连接的(先发送FIN消息的)主机才经过Time-wait状态,套接字处在Time-wait过程时,相应端口是正在使用的状态。无需考虑客户端Time-wait状态,因为客户端套接字的端口号是任意指定的。
为何要有Time-wait状态
在图9-1中,如果主机A向主机B发送的ACK信息丢失,如果没有Time-wait状态,主机B将重传FIN信息,但主机B永远收不到回信。
地址再分配
四次挥手过程中,如果最后的数据丢失,主机B会认为主机A未能收到自己发送的FIN消息,因此重传。收到FIN消息的主机A将重启Time-wait计时器。如果网络状态不理想,Time-wait状态将持续。
SO_REUSEADDR状态:是否启用地址再分配,主要原理是操作关闭套接字的Time-wait时间等待的开启和关闭。SO_REUSEADDR的默认值为0(假),将这个值改成1(真),可将Time-wait状态下的套接字端口号重新分配给新的套接字。
TCP_NODELAY
Nagle算法
只有收到前一数据的ACK消息时,Nagle算法才发送下一数据。
未准确判断数据特性时不应禁用Nagle算法 。
禁用Nagle算法
将TCP_NODELAY改为1(真)即可禁用Nagle算法:
int opt_val=1;
setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(void *)&opt_val,sizeof(opt_val));
查看Nagle算法的设置状态:
int opt_val;
socklen_t opt_len;
opt_len=sizeof(opt_val);
getsockopt(sock,IPPROTO_TCP, TCP_NODELAY,(void*) &opt_val, &opt_len);