千家信息网

linux 网络编程 socket选项的实现

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,socket选项函数功能:用来读取和设置socket文件描述符属性的方法#include int getsockopt ( int sockfd, int level, int option_name
千家信息网最后更新 2026年10月31日linux 网络编程 socket选项的实现

socket选项函数

功能:用来读取和设置socket文件描述符属性的方法

#include int getsockopt ( int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len );int setsockopt ( int sockfd, int level, int option_name, const void* option_value, socklen_t option_len);

socket选项表如下:

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将自动继承这些选项。这些选项包括:SO_DEBUG、SO_DONTROUTE、SO_KEEPALIVE、SO_LINGER、SO_OOBINLINE、SO_RCVBUF、SO_RCVLOWAT、SO_SNDBUF、SO_SNDLOWAT、TCP_MAXSEG和TCP_NODELAY。

对于客户端而言,这些socket选项则应该在调用connect函数之前设置,因为connect调用成功返回之后,TCP三次握手已完成。

SO_REUSEADDR选项

前面讨论过TCP连接的TIME_WAIT状态,并提到服务器程序可以通过设置socket选项SO_REUSEADDR来强制使用被处于TIME_WAIT状态的连接占用的socket地址。

#include #include #include #include #include #include #include #include #include  int main( int argc, char* argv[] ){  if( argc <= 2 )  {    printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );    return 1;  }  const char* ip = argv[1];  int port = atoi( argv[2] );   int sock = socket( PF_INET, SOCK_STREAM, 0 );  assert( sock >= 0 );  int reuse = 1;  setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof( reuse ) );   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 );  int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );  assert( ret != -1 );   ret = listen( sock, 5 );  assert( ret != -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 remote[INET_ADDRSTRLEN ];    printf( "connected with ip: %s and port: %d\n",       inet_ntop( AF_INET, &client.sin_addr, remote, INET_ADDRSTRLEN ), ntohs( client.sin_port ) );    close( connfd );  }   close( sock );  return 0;}

经过setsocketopt的设置之后,即使sock处于TIME_WAIT状态,与之绑定的socket地址也可以立即被重用。此外,我们也可以通过修改内核参数/proc/sys/net/ipv4/tcp_tw_recycle 来快速回收被关闭的socket,从而使得TCP连接根本就不进入TIME_WAIT状态,进而允许应用程序立即重用本地的socket地址。

SO_RCVBUF和SO_SNDBUF选项

SO_RCVBUF和SO_SNDBUF选项分别表示TCP接收缓冲区和发送缓冲区的大小。不过,当我们用setsockopt来设置TCP的接收缓冲区和发送缓冲区的大小时,系统都会将其值加倍,并且不得小于其个最小值。TCP接收缓冲区的最小值是256字节,而发送缓冲区的最小值是2048字节(不过,不同的系统可能有不同的默认最小值)。此外,我们可以直接修改内核参数/proc/sys/net/ipv4/tcp_rmem和/proc/sys/net/ipv4/tcp_wmem来强制TCP接收缓冲区和发送缓冲区的大小没有最小值限制。

修改TCP发送缓冲区的客户端程序:

#include #include #include #include #include #include #include  #define BUFFER_SIZE 512 int main( int argc, char* argv[] ){  if( argc <= 3 )  {    printf( "usage: %s ip_address port_number send_bufer_size\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 );   int sock = socket( PF_INET, SOCK_STREAM, 0 );  assert( sock >= 0 );   int sendbuf = atoi( argv[3] );  int len = sizeof( sendbuf );  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;}

修改TCP接收缓冲区的服务器程序:

#include #include #include #include #include #include #include #include #include  #define BUFFER_SIZE 1024 int main( int argc, char* argv[] ){  if( argc <= 3 )  {    printf( "usage: %s ip_address port_number receive_buffer_size\n", basename( argv[0] ) );    return 1;  }  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 );   int sock = socket( PF_INET, SOCK_STREAM, 0 );  assert( sock >= 0 );  int recvbuf = atoi( argv[3] );  int len = sizeof( recvbuf );  setsockopt( sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof( recvbuf ) );  getsockopt( sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, ( socklen_t* )&len );  printf( "the receive buffer size after settting is %d\n", recvbuf );   int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );  assert( ret != -1 );   ret = listen( sock, 5 );  assert( ret != -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;}

运行结果:

root@iZbp1anc6yju2dks3nw5j0Z:~/test/socket# ./client 127.0.0.1 12345 2000
the tcp send buffer size after setting is 4608

root@iZbp1anc6yju2dks3nw5j0Z:~/test/socket# ./server 127.0.0.1 12345 50
the receive buffer size after settting is 2304

如上说明:当我们用setsockopt来设置TCP的接收缓冲区和发送缓冲区的大小时,系统都会将其值加倍,并且不得小于其个最小值。

SO_RCVLOWAT和SO_SNDLOWAT选项

  • SO_RCVLOWAT和SO_SNDLOWAT选项分别表示TCP接收缓冲区和发送缓冲区的低水位标记。它们一般被I/O复用系统调用,用来判断socket是否可读或可写。当TCP接收缓冲区中可读数据的总数大于其低水位标记时,I/O复用系统调用将通知应用程序可以从对应的socket上读取数据;当TCP发送缓冲区中的空闲空间(可以写入数据的空间)大于其低水位标记时,I/O复用系统调用将通知应用程序可以往对应的socket上写入数据。
  • 默认情况下,TCP接收缓冲区的低水位标记和TCP发送缓冲区的低水位标记均为1字节。

SO_LINGER选项

SO_LINGER选项用于控制close系统调用在关闭TCP连接时的行为。默认情况下,当我们使用close系统调用来关闭一个socket时,close将立即返回,TCP模块负责把该socket对应的TCP发送缓冲区中残留的数据发送给对方。

设置SO_LINGER选项的值时,我们需要给setsockopt(getsockopt)系统调用传递一个linger类型的结构体,其定义如下:

#include struct linger{  int l_onoff; //开启(非0)还是关闭(0)该选项  int l_linger; // 滞留时间};
  • 根据linger结构体中两个成员变量的不同值,close 系统调用可能产生如下3种行为之一:
  • l_onoff 等于0。此时SO_LINGER选项不起作用,close用默认行为关闭socket。
  • l_onoff 不为0,l_linger等于0. 此时close 系统调用立即返回,TCP模块将丢弃被关闭的socket对应的TCP发送缓冲区中残留的数据,同时给对方一个复位报文段。因此,这种情况给服务器提供了异常终止一个连接的方法。l_onoff不为0,l_linger大于0 。此时close的行为取决于两个条件:(1)被关闭的socket对应的TCP发送缓冲区中是否还有残留的数据;(2)该socket是阻塞的还是非阻塞的。 对于阻塞的socket,close将等待一段长为l_linger的时间,直到TCP模块发送完所有残留数据并得到对方的确认。如果这段之间内TCP模块没有发送完残留数据并得到对方的确认,那么close系统调用将返回-1并设置errno为EWOULDBLOCK。 如果socket是非阻塞的,close将立即返回,此时我们需要根据其返回值和errno来判断残留数据是否已经发送完毕。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

缓冲区 缓冲 系统 数据 最小 程序 残留 服务器 标记 水位 状态 服务 两个 大小 对方 情况 报文 模块 行为 监听 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网高科技 苏州找大状互联网科技有限公司 涨乐互联网科技 深圳互联网科技公司 互联网 科技创新 为想互联网科技 互联网科技股票 广州宏创互联网科技 互联网公司会议桌创意科技软装饰 互联网汽车科技小镇 宁波青枫互联网科技有限公司 翼展科技 首家互联网医院 威龙国际互联网科技公司 元气玛特互联网科技公司是外包吗 广州富喜互联网络科技 新氧科技和美国互联网有关系吗 昆明中奇联合互联网科技 陕西九四连强互联网科技有限公司 互联网金融运营模式中的金融科技 中国互联网公司的科技投入 亿人帮互联网科技有限公司 山东特亿宝互联网科技有限公司 圆梦互联网科技 中国互联网科技发展 九亚互联网科技 互联网科技网站 大白互联网科技 天津腾达互联网科技有限公司 保险科技服务互联网营销体系 普陀区互联网教育科技管理办法
0