前言
socket选项
正文
一, sockopt参数
设置socket的参数的函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
1,sockopt的选项
level | option name | 数据类型 | 说明 |
SOL_SOCKET(通用socket选项, 与协议无关) | SO_DEBUG | int | 打开调试信息 |
SO_REUSEADDR | int | 重用本地地址 | |
SO_TYPE | int | 获取socket类型 | |
SO_ERROR | int | 获取并清除socket错误状态 | |
SO_DONTROUTE | int | 不查看路由表,直接将数据发送给本地局域网内的主机。含义和send系统调用的MSG_DONTROUTE标志类似 | |
SO_RCVBUF | int | TCP接受缓冲区大小 | |
SO_SNDBUF | int | TCP发送缓冲区大小 | |
SO_KEEPALIVE | int | 发送周期性保活报文以维持连接 | |
SO_OOBINLINE | int | 接收到的带外数据将保留在普通数据的输入队列中(在线保留) | |
SO_LINGER | linger | 若有数据待发送, 则延迟关闭 | |
SO_RCVLOWAT | int | TCP接收缓冲区低水位标记 | |
SO_SNDLOWAT | int | TCP发送缓冲区低水位标记 | |
SO_RCVTIMEO | itmeval | recv,recvmsg,accept [返回-1, 设置errno为EAGAIN或EWOULDBLOCK] | |
SO_SNDTIMEO | timeval | send, sendmsg [返回-1, 设置errno为EAGAIN或EWOULDBLOCK], connect [[返回-1, 设置errno为EWOULDBLOCK]] | |
IPPROTO_IPv4(IPV4选项) | IP_TOS | int | 服务类型 |
IP_TTL | int | 保活时间 | |
IPPROTO_IPv6(IPv6选项) | IPV6_NEXTHOP | sockaddr_in6 | 下一跳IP地址 |
IPV6_RECVPKTINFO | int | 接收分组信息 | |
IPV6_DONTFRAG | int | 禁止分片 | |
IPV6_RECVTCLASS | int | 接收通讯类型 | |
IPPROTO_TCP(TCP选项) | TCP_MAXSNG | int | TCP最大报文段大小 |
TCP_NODELAY | int | 禁止Nagle算法 | |
getsockopt和setsockopt这两个函数成功时返回0,失败时返回-1并且设置errno
对于服务器而言, 有部分socket选项只能在调用listen系统调用前针对监听socket设置的才有效。这是因为连接socket只能由accept调用返回,而accept从listen监听队列中接受的连接至少已经完成了TCP三次握手的前两个步骤(因为listen监听队列中的连接至少已进入SYN_RCVD状态),这说明服务器已经往被接受连接上发送出了TCP同步报文段。但有的socket选项却应该在TCP同步报文段中设置,比如TCP最大报文段选项。对这种情况,
linux给开放人员提供的解决方案是:
对于监听socket设置这些socket选项, 那么accept返回的连接socket将自动继承这些选项。 这些socket选项包含:
- SO_DEBUG
- SO_DONTROUTE
- SO_KEEPALIVE
- SO_LINGER
- SO_OOBINLINE
- SO_RCVBUF
- SO_RCVLOWAT
- SO_SNDBUF
- SO_SNDLOWAT
- TCP_MAXSEG
- TCP_NODELAY
client 而言 这些socket选项应该在connect函数之前设置, 因为connect调用成功返回之后, TCP三次握手已经完成了
二, SO_SNDBUF 和 SO_RECVBUF 选项
SO_SNDBUF 和 SO_RECVBUF 选项分别表示TCP发送缓冲区和接受缓冲区的大小。 不过
发送缓冲区和接受缓冲区的修改配置文件
/proc/sys/net/ipv4/tcp_wmem
/proc/sys/net/ipv4/tcp_rmem
三个参数分别指定是缓冲区
- 最小值
- 默认值
- 最大值
测试程序
client
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#define BUFFER_SIZE 512
int main(int argc, char *argv[])
{
if (argc <= 2)
{
printf("usage: %s ip_address port_number send_bufer_seize\n", basename(argv[0]));
return 1;
}
const char * ip = argv[1];
int port = atoi(argv[2]);
struct sockaddr_in server_address;
bzero(&server_address, sizeof(server_address));
server_address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &server_address.sin_addr);
server_address.sin_port = htons(port);
// create socket
int sock = socket(PF_INET, SOCK_STREAM, 0);
if(sock <= 0)
{
close(sock);
printf("create socket error = %d\n", errno);
return 0;
}
int sendbuf = atoi(argv[3]);
int len = sizeof(sendbuf);
// setting TCP SEDBUFF size read
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, sizeof(sendbuf));
getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, (socklen_t *)&len);
printf("the tcp send buffer size after setting is %d\n", sendbuf);
if (connect(sock, (struct sockaddr *)&server_address, sizeof(server_address)) != -1)
{
char buffer[BUFFER_SIZE];
memset(buffer, 'a', BUFFER_SIZE);
send(sock, buffer, BUFFER_SIZE, 0);
}
close(sock);
return 0;
}
server
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include <cerrno>
#define BUFFER_SIZE 1024
int main(int argc, char *argv[])
{
if (argc <= 2)
{
printf("usage: %s ip_address port_number recv_buffer_seize\n", basename(argv[0]));
return 1;
}
int ret = 0;
const char * ip = argv[1];
int port = atoi(argv[2]);
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &address.sin_addr);
address.sin_port = htons(port);
// create socket
int sock = socket(PF_INET, SOCK_STREAM, 0);
if(sock <= 0)
{
close(sock);
printf("create socket error = %d\n", errno);
return 0;
}
int recvbuf = atoi(argv[3]);
int len = sizeof(recvbuf);
// setting TCP SEDBUFF size read
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &recvbuf, sizeof(recvbuf));
getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &recvbuf, (socklen_t *)&len);
printf("the tcp recv buffer size after setting is %d\n", recvbuf);
ret = bind(sock, (struct sockaddr *)&address, sizeof(address));
if (ret == -1)
{
close(sock);
printf("bind socket error = %d\n", errno);
return 1;
}
ret = listen(sock, 5);
if (ret == -1)
{
close(sock);
printf("listen socket error = %d\n", errno);
return 1;
}
struct sockaddr_in client;
socklen_t client_addrlength = sizeof(client);
int connfd = accept(sock, (struct sockaddr*) &client, &client_addrlength);
if (connfd < 0)
{
printf("errno is : %d\n", errno);
}
else
{
char buffer[BUFFER_SIZE];
memset(buffer, '\0', BUFFER_SIZE);
while(recv(connfd, buffer, BUFFER_SIZE - 1, 0) > 0) {}
close(connfd);
}
close(sock);
return 0;
}
开放端口
/sbin/iptables -I INPUT -p tcp --dport 9992 -j ACCEPT
tcpdump 抓包
tcpdump -nt -i ens33 port 9992
效果图片