之前我们写的程序比较简单在创建好套接字后都直接使用,但有些时候需要更改一些默认设置,所以本节我们学习下部分套接字的可选项,下图列出了部分。
其中
名称 | 含义 |
---|---|
IPPROTO_IP | IP协议相关 |
IPPROTO_TCP | TCP协议相关 |
SOL_SOCKET | 套接字相关 |
getsockopt和setsockopt可对上表中所有的可选项进行读取和设置
#include
int getsockopt(int sock,int level,int optname,void *optval,socklen_t *optlen)
eg
int state;
optlen = sizeof(sock_type);
tcp_sock = socket(PF_INET, SOCK_STREAM, 0);
state = getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void *)&sock_type, &optlen);
最终sock_type的值为1表示TCP套接字,注意套接字类型只能创建时决定,以后不能更改。
#include
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
SO_SNDBUF是输入缓冲大小相关可选项,SO_RCVBUF是输出缓冲大小相关可选项。下面我们将读取当前I/O缓冲大小并进行修改。
①读当前I/O缓冲
#include
#include
#include
#include
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
#include
#include
#include
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);
}
统一设置为3字节,结果虽然减小了却和设置的不同,主要原因是TCP重传机制也需要缓存空间,所以不能随心所欲地设置缓冲大小。
服务器端与客户端建立连接的状态下,如果强行关闭服务器端,再立马运行服务器端,会出现bind error的提示,直到过三分钟后才能重新运行,这是为什么呢?
(注:我自己测试正常关闭进入time-wait状态,但强行关闭不会进入time-wait状态,而是进入fin-wait2状态且会自动消逝,原因未知,下面将按照书上所讲
)
原因在于强行断开的一端会进入time-wait的状态,time-wait状态这是为了保证连接正常中断以及让滞留在网络中的信息全部过期。而在这个状态中套接字并未消除占用着端口号,所以立马连接会出错。
强行关闭客户端,再立马运行客户端不会出现提示,难道客户端不会经过这一过程吗?
事实上不管是服务器端还是客户端只要是先断开连接的这一端都会经过这一过程,但是客户端套接字端口号是随机出来的,每次运行重新分配,不会出现占用的情况。仔细回想客户端没有bind的过程,在创建套接字后connect才进行随机分配,也验证了不会出现bind error。
如果系统发生故障必须要立即重启时,time-wait状态就减慢重启服务器的速度,所以SO_REUSEADDR应运而生。SO_REUSEADDR可以在time-wait状态下的套接字重新分配给新的套接字,SO_REUSEADDR默认是0,改成1即可。
optlen=sizeof(option);
option=TRUE;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &option, optlen);
为了防止数据包过载,Nagle算法发明并应用于传输层如下图所示,只有收到前一数据的ACK时,Nagle算法才会发送下一数据,并且最大限度的进行缓冲。而不使用Nagle算法时数据到达缓冲立即发送出去。那么传递一个字节消息也需要发送一个包,头部的大小都大于数据大小,效率明显偏低,为提高网络利用率必须使用Nagle算法。
但是Nagle算法并不是任何时候都适用,而在传输大文件且网络流量影响不大的情况下,Nagle算法需等待ACK才能继续发送速度远远慢于禁用Nagle算法。所以如果有必要,我们可以禁用Nagle算法。
TCP_NODELAY = 1表示禁用
int opt_val = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&opt_val,
查看是否开启
int opt_val;
socklen_t opt_len;
opt_len = sizeof(opt_val);
getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&opt_val, opt_len);