Linux C语言 50-套接字参数设置

Linux C语言 50-套接字参数设置

本节关键字:Linux、C语言、套接字、参数设置
相关C库函数:setsockopt、getsockopt、printf、connect、send、select、assert

参数设置接口

int setsockopt(int sock, int level, int optname, const void *optval, socklent_t optlen);
/*
@param sock:将要被设置或选取选项的套接字
@param level:选项所在的协议层,SOL_OCKET:通用套接字,IPPROTO_IP:IP选项,IPPROTO_TCP:TCP选项
@param optname:需要访问的选项名,
@param optval:指向包含新选项值的缓冲
@param optlen:长度
@return 成功返回0,失败返回-1,errno被设为以下某一值
	EBADF:sock不是有效的文件描述词
	EFAULT:optval指向的内存并非有效的进程空间
	EINVAL:在调用setsockopt()时,optlen无效
	ENOPROTOOPT:指定的协议层不能识别选项
	ENOTSOCK:sock描述的不是套接字

level协议层

对应宏名称 说明
SOL_OCKET 通用套接字
IPPROTO_IP IP选项
IPPROTO_TCP TCP选项

optname选项

SOL_SOCKET对应的选项

宏名称 参数类型 说明
SO_BROADCAST int 允许发送广播数据
SO_DEBUG int 允许调试
SO_DONTROUTE int 不查找路由
SO_ERROR int 获得套接字错误
SO_KEEPALIVE int 保持连接
SO_LINGER struct linger 延迟关闭连接
SO_OOBINLINE int 带外数据放入正常数据流
SO_RCVBUF int 接收缓冲区大小
SO_SNDBUF int 发送缓冲区大小
SO_RCVLOWAT int 接收缓冲区下限
SO_SNDLOWAT int 发送缓冲区下限
SO_RCVTIMEO struct timeval 接收超时
SO_SNDTIMEO struct timeval 发送超时
SO_REUSERADDR int 允许重用本地地址和端口
SO_TYPE int 获得套接字类型
SO_BSDCOMPAT int 与BSD系统兼容

IPPROTO_IP对应的选项

宏名称 参数类型 说明
IP_HDRINCL int 在数据包中包含IP首部
IP_OPTINOS int IP首部选项
IP_TOS int 服务类型
IP_TTL int 生存时间

IPPRO_TCP对应的选项

宏名称 参数类型 说明
TCP_MAXSEG int TCP最大数据段的大小
TCP_NODELAY int 不适用Nagle算法

缓冲区设置

TCP一次最多发送多少数据是由系统决定的,自行设置的数据大于系统承受的最大数据时失效,自行设置的数据小于系统承受的最大数据时有效

//发送缓冲区
int tmp, bufLen=1024*2;
socklen_t optlen = sizeof(int);
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char *)&buflen, sizeof(int));
getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (int*)&tmp, &optlen);
printf("send_tmp=%d,optlen=%d\n",tmp,(int)optlen); //设置发送缓冲区2048
//接收缓冲区
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char *)&buflen, sizeof(int));
getsockopt(fd,SOL_SOCKET, SO_RCVBUF,(int *)&tmp, &optlen);
printf("recv_tmp=%d,optlen=%d\n",tmp,(int)optlen); //设置接收缓冲区2048

超时设置

1.Windows下timeout类型为int,Unix下timeout为struct timeval
2.通过设置为0秒和0微秒禁止超时(即一直阻塞),缺省情况下,超时都是禁止的
3.缺省状态为禁止超时,缓冲区被填满之后,再次向缓冲区中拷贝内容会出现一直阻塞的现象,此问题的解决只能在创建socket连接时设置超时时间,发送(send)或接收(recv)阻塞等待超时后会返回-1

int recvTimeout = 30 * 1000; //30s
int sendTimeout = 30 * 1000; //30s
setsockopt(fd, SOL_SOCKET, SO_RCVTIME0, (char *)&recvTimeout, sizeof(int));
setscokopt(fd, SOL_SOCKET, SO_SNDTIME0, (char *)&sendTimeout, sizeof(int));
/**
     * 设置Socket发送超时,不设置时,缓冲区满再次send时默认会一直阻塞 */
#if defined(_WIN32)
    int sendTimeout = 30 * 1000; //30s
    int ret = setsockopt(m_Socket, SOL_SOCKET, SO_SNDTIMEO, (const char*)&sendTimeout, sizeof(int));
    if (ret < 0) {
        printf("Error: failed to setsockopt\n");
        exit(0);
    } else {
        int timeout;
        getsockopt(m_Socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(int));
        printf("Windows time out: %d s\n", timeout);
    }
#else
    struct timeval tv;
    tv.tv_sec = 30;
    tv.tv_usec = 0;
    int ret = setsockopt(m_Socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(struct timeval));
    if (ret < 0) {
        printf("Error: failed to setsockopt\n");
        exit(0);
    } else {
        struct timeval tv;
        socklen_t optlen = sizeof(struct timeval);
        getsockopt(m_Socket, SOL_SOCKET, SO_SNDTIMEO, &tv, &optlen);
        printf("Unix time out: %d s, %d us\n", tv.tv_sec, tv.tv_usec);
    }
#endif

HP-Unix环境不适配超时设置

HP-Unix环境不适配参数SO_SNDTIMEO与SO_RCVTIMEO的设置,该环境下为防止发生send()与recv()的阻塞,可先使用select()进行套接字读写判断,相关参数为SO_SNDLOWAT、SO_RCVLOWAT,注意:套接字读写权限的select()在connect()之后才会生效,不然依旧会一直阻塞。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void print_sockopt(int m_Socket);
int main(void)
{
  int ret, m_Socket;
  m_Socket = socket(AF_INET, SOCK_STREAM, 0);
  if (m_Socket < 0) return -1;
  assert(m_Socket != -1);
  printf("socket: %d\n", m_Socket);

  struct timeval tv = {10, 0};
  int optlen = sizeof(tv);
  
  printf("Unix time out: %d s, %d us, len=%d\n", tv.tv_sec, tv.tv_usec, optlen);
  ret = setsockopt(m_Socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv));
  if (ret < 0) printf("set error\n");
  assert(ret != -1);
  printf("Unix time out: %d s, %d us, len=%d\n", tv.tv_sec, tv.tv_usec, optlen);
  
  optlen = sizeof(tv);
  printf("optlen=%d\n", optlen);
  
  ret = getsockopt(m_Socket, SOL_SOCKET, SO_SNDTIMEO, &tv, &optlen);
  if (ret < 0) printf("get error\n");
  assert(ret != -1);

  printf("Unix time out: %d s, %d us, len=%d\n", tv.tv_sec, tv.tv_usec, optlen);
  print_sockopt(m_Socket);

  int optval = 655350;
  optlen = sizeof(optval);
  setsockopt(m_Socket, SOL_SOCKET, SO_SNDBUF, &optval, optlen);
  setsockopt(m_Socket, SOL_SOCKET, SO_RCVBUF, &optval, optlen);
  
  optval = 360000;
  setsockopt(m_Socket, SOL_SOCKET, SO_SNDLOWAT, &optval, optlen);
  print_sockopt(m_Socket);
  
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr("172.16.100.149");
  addr.sin_port = htons(12345);
  if (connect(m_Socket, (struct sockaddr*)&addr, sizeof(addr)) == -1)
  {
    printf("connect error, close: %d\n", close(m_Socket));
    return -1;
  }

  //if (fcntl(m_Socket, F_SETFL, O_NDELAY) < 0) printf("err: fcntl\n");
  char send_buf[350001] = {0};
  memset(send_buf, 'a', 350000);

  int i = 1;
  while (true)
  {
    fd_set wfds;
    FD_ZERO(&wfds);
    FD_SET(m_Socket, &wfds);

    tv.tv_sec = 3;
    tv.tv_usec = 0;
    printf("begin to select\n");
    ret = select(m_Socket+1, NULL, &wfds, NULL, &tv); 
    if (ret <= 0) 
    {
      if (errno != EINTR) printf("select error, i=%d\n", i=1);
      else printf("select EINTR\n");
      continue;
    }
    else if (ret == 0) printf("select tiemout\n");
    else  printf("select over\n");
    
    int ret = send(m_Socket, send_buf, 350000, 0);
    if (ret <= 0) break;
    else printf("send cnt: %d, bytes: %d\n", i++, ret);
  }
  close(m_Socket);
  return 0;
}

void print_sockopt(int m_Socket)
{
  int optval;
  int optlen = sizeof(optval);
  getsockopt(m_Socket, SOL_SOCKET, SO_SNDBUF, &optval, &optlen);
  printf("SO_SNDBUF: %d\n", optval);
  
  optlen = sizeof(optval);
  getsockopt(m_Socket, SOL_SOCKET, SO_RCVBUF, &optval, &optlen);
  printf("SO_RCVBUF: %d\n", optval);

  optlen = sizeof(optval);
  getsockopt(m_Socket, SOL_SOCKET, SO_SNDLOWAT, &optval, &optlen);
  printf("SO_SNDLOWAT: %d\n", optval);
  
  optlen = sizeof(optval);
  getsockopt(m_Socket, SOL_SOCKET, SO_RCVLOWAT, &optval, &optlen);
  printf("SO_RCVLOWAT: %d\n", optval);
}

你可能感兴趣的:(Linux,C语言,linux,c语言,网络,centos,服务器)