• 售前

  • 售后

热门帖子
入门百科

linux 网络编程 socket选项的实现

[复制链接]
我是一头牛2017 显示全部楼层 发表于 2021-10-26 13:32:09 |阅读模式 打印 上一主题 下一主题
socket选项函数
功能:用来读取和设置socket文件形貌符属性的方法
  1. #include <sys/scoket.h>
  2. int getsockopt ( int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len );
  3. 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地址。
  1. #include <sys/socket.h>
  2. #include <netinet/in.h>
  3. #include <arpa/inet.h>
  4. #include <assert.h>
  5. #include <stdio.h>
  6. #include <unistd.h>
  7. #include <stdlib.h>
  8. #include <errno.h>
  9. #include <string.h>
  10. int main( int argc, char* argv[] )
  11. {
  12.   if( argc <= 2 )
  13.   {
  14.     printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
  15.     return 1;
  16.   }
  17.   const char* ip = argv[1];
  18.   int port = atoi( argv[2] );
  19.   int sock = socket( PF_INET, SOCK_STREAM, 0 );
  20.   assert( sock >= 0 );
  21.   int reuse = 1;
  22.   setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof( reuse ) );
  23.   struct sockaddr_in address;
  24.   bzero( &address, sizeof( address ) );
  25.   address.sin_family = AF_INET;
  26.   inet_pton( AF_INET, ip, &address.sin_addr );
  27.   address.sin_port = htons( port );
  28.   int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
  29.   assert( ret != -1 );
  30.   ret = listen( sock, 5 );
  31.   assert( ret != -1 );
  32.   struct sockaddr_in client;
  33.   socklen_t client_addrlength = sizeof( client );
  34.   int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
  35.   if ( connfd < 0 )
  36.   {
  37.     printf( "errno is: %d\n", errno );
  38.   }
  39.   else
  40.   {
  41.     char remote[INET_ADDRSTRLEN ];
  42.     printf( "connected with ip: %s and port: %d\n",
  43.       inet_ntop( AF_INET, &client.sin_addr, remote, INET_ADDRSTRLEN ), ntohs( client.sin_port ) );
  44.     close( connfd );
  45.   }
  46.   close( sock );
  47.   return 0;
  48. }
复制代码
经过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发送缓冲区的客户端程序:
  1. #include <sys/socket.h>
  2. #include <arpa/inet.h>
  3. #include <assert.h>
  4. #include <stdio.h>
  5. #include <unistd.h>
  6. #include <string.h>
  7. #include <stdlib.h>
  8. #define BUFFER_SIZE 512
  9. int main( int argc, char* argv[] )
  10. {
  11.   if( argc <= 3 )
  12.   {
  13.     printf( "usage: %s ip_address port_number send_bufer_size\n", basename( argv[0] ) );
  14.     return 1;
  15.   }
  16.   const char* ip = argv[1];
  17.   int port = atoi( argv[2] );
  18.   struct sockaddr_in server_address;
  19.   bzero( &server_address, sizeof( server_address ) );
  20.   server_address.sin_family = AF_INET;
  21.   inet_pton( AF_INET, ip, &server_address.sin_addr );
  22.   server_address.sin_port = htons( port );
  23.   int sock = socket( PF_INET, SOCK_STREAM, 0 );
  24.   assert( sock >= 0 );
  25.   int sendbuf = atoi( argv[3] );
  26.   int len = sizeof( sendbuf );
  27.   setsockopt( sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, sizeof( sendbuf ) );
  28.   getsockopt( sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, ( socklen_t* )&len );
  29.   printf( "the tcp send buffer size after setting is %d\n", sendbuf );
  30.   if ( connect( sock, ( struct sockaddr* )&server_address, sizeof( server_address ) ) != -1 )
  31.   {
  32.     char buffer[ BUFFER_SIZE ];
  33.     memset( buffer, 'a', BUFFER_SIZE );
  34.     send( sock, buffer, BUFFER_SIZE, 0 );
  35.   }
  36.   close( sock );
  37.   return 0;
  38. }
复制代码
修改TCP接收缓冲区的服务器程序:
  1. #include <sys/socket.h>
  2. #include <netinet/in.h>
  3. #include <arpa/inet.h>
  4. #include <assert.h>
  5. #include <stdio.h>
  6. #include <unistd.h>
  7. #include <stdlib.h>
  8. #include <errno.h>
  9. #include <string.h>
  10. #define BUFFER_SIZE 1024
  11. int main( int argc, char* argv[] )
  12. {
  13.   if( argc <= 3 )
  14.   {
  15.     printf( "usage: %s ip_address port_number receive_buffer_size\n", basename( argv[0] ) );
  16.     return 1;
  17.   }
  18.   const char* ip = argv[1];
  19.   int port = atoi( argv[2] );
  20.   struct sockaddr_in address;
  21.   bzero( &address, sizeof( address ) );
  22.   address.sin_family = AF_INET;
  23.   inet_pton( AF_INET, ip, &address.sin_addr );
  24.   address.sin_port = htons( port );
  25.   int sock = socket( PF_INET, SOCK_STREAM, 0 );
  26.   assert( sock >= 0 );
  27.   int recvbuf = atoi( argv[3] );
  28.   int len = sizeof( recvbuf );
  29.   setsockopt( sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof( recvbuf ) );
  30.   getsockopt( sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, ( socklen_t* )&len );
  31.   printf( "the receive buffer size after settting is %d\n", recvbuf );
  32.   int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
  33.   assert( ret != -1 );
  34.   ret = listen( sock, 5 );
  35.   assert( ret != -1 );
  36.   struct sockaddr_in client;
  37.   socklen_t client_addrlength = sizeof( client );
  38.   int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
  39.   if ( connfd < 0 )
  40.   {
  41.     printf( "errno is: %d\n", errno );
  42.   }
  43.   else
  44.   {
  45.     char buffer[ BUFFER_SIZE ];
  46.     memset( buffer, '\0', BUFFER_SIZE );
  47.     while( recv( connfd, buffer, BUFFER_SIZE-1, 0 ) > 0 ){}
  48.     close( connfd );
  49.   }
  50.   close( sock );
  51.   return 0;
  52. }
复制代码
运行结果:
  1. root@iZbp1anc6yju2dks3nw5j0Z:~/test/socket# ./client 127.0.0.1 12345 2000
  2. the tcp send buffer size after setting is 4608
复制代码
  1. root@iZbp1anc6yju2dks3nw5j0Z:~/test/socket# ./server 127.0.0.1 12345 50
  2. 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范例的结构体,其界说如下:
  1. #include <sys/socket.h>
  2. struct linger
  3. {
  4.   int l_onoff; //开启(非0)还是关闭(0)该选项
  5.   int l_linger; // 滞留时间
  6. };
复制代码
      
  • 根据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来判定残留数据是否已经发送完毕。
以上就是本文的全部内容,盼望对各人的学习有所资助,也盼望各人多多支持草根技术分享。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作