文章目录套接字属性套接字选项的核心级别SOL_SOCKET的常用选项IPPROTO_IP的常用选项核心函数参数返回值应用场景demo套接字属性套接字选项即套接字属性用于对套接字的行为、功能进行精细化控制选项的操作分为获取和设置部分选项仅可获取如套接字状态、类型部分选项既可获取也可设置如缓冲区大小、保活机制选项按协议 / 作用范围划分不同级别不同级别下包含专属的选项操作时需指定对应级别套接字选项的核心级别级别是选项的作用域标识操作选项时必须指定核心常用级别及作用如下级别作用范围适用场景SOL_SOCKET套接字本身通用级别所有类型套接字的基础属性控制IPPROTO_IPIPv4 协议层IPv4 相关的网络属性配置IPPROTO_IPV6IPv6 协议层IPv6 相关的网络属性配置IPPROTO_TCPTCP 协议流式套接字TCP 专属属性控制IPPROTO_UDPUDP 协议数据报套接字UDP 专属属性控制其他如 SOL_LRLMP特定协议如 IrDA小众协议的专属配置SOL_SOCKET的常用选项最常用的基础级别包含套接字的核心通用属性重点选项如下区分获取 / 设置权限选项名说明操作权限核心用途SO_ACCEPTCONN套接字是否处于监听状态仅获取判断 listen 是否调用成功SO_BROADCAST允许发送广播数据获取 / 设置UDP 广播通信开启SO_KEEPALIVE保活连接获取 / 设置TCP 长连接保活检测断连SO_LINGER延迟关闭连接获取 / 设置控制 close 时的连接关闭行为SO_RCVBUF/SO_SNDBUF接收 / 发送缓冲区大小获取 / 设置调整套接字读写缓冲区优化 I/OSO_REUSEADDR允许重用本地地址和端口获取 / 设置解决端口占用问题如重启服务SO_TYPE获得套接字类型仅获取判断是 TCPSOCK_STREAM/UDPSOCK_DGRAMSO_ERROR获得套接字错误码仅获取排查套接字运行中的错误IPPROTO_IP的常用选项针对 IPv4 协议的专属配置常用选项选项名说明操作权限IP_ADD_MEMBERSHIP加入多播组仅设置IP_OPTIONS设置 / 获取 IP 头部选项获取 / 设置IP_HDRINCL自定义 IP 头部原始套接字获取 / 设置IP_TTL设置 / 获取 IP 生存时间获取 / 设置核心函数#includesys/socket.h//获取选项getsockoptintgetsockopt(intsockfd,intlevel,intoptname,void*optval,socklen_t*optlen);//设置选项setsockoptintsetsockopt(intsockfd,intlevel,intoptname,constvoid*optval,socklen_toptlen);参数参数名类型作用sockfdint要操作的套接字描述符socket 创建的返回值levelint选项的级别如 SOL_SOCKET、IPPROTO_IPoptnameint要操作的具体选项名如 SO_REUSEADDR、IP_TTLoptvalvoid*存放选项值的缓冲区getsockopt内核将选项值写入该缓冲区setsockopt将待设置的值传入内核optlensocklen_t*getsockopt传入缓冲区大小返回实际读取的大小setsockopt直接传入缓冲区的实际大小非指针返回值成功返回0失败返回-1错误码存入errno常见错误码EBADF无效的套接字描述符EINVAL未知的级别 / 选项名ENOTSOCK描述符不是套接字是普通文件 / 设备ENOPROTOOPT协议不支持该选项应用场景服务端重启时设置SO_REUSEADDR解决端口占用问题需在bind前设置否则端口重用失效是服务端开发的必设选项高并发 / 大流量场景调整SO_RCVBUF/SO_SNDBUF扩大读写缓冲区减少 I/O 次数TCP 长连接如 IM、物联网设置SO_KEEPALIVE开启保活机制自动检测断连UDP 广播通信设置SO_BROADCAST允许发送广播包排查问题时通过SO_ERROR获取套接字错误码通过SO_TYPE判断套接字类型多播开发时通过IP_ADD_MEMBERSHIP让套接字加入多播组demo#ifndef_NET_H_#define_NET_H_#includestdio.h#includestdlib.h#includesys/socket.h#includenetinet/in.h#includenetinet/tcp.h#includearpa/inet.h#includeunistd.h#includestrings.h#includeerrno.htypedefstructsockaddrAddr;typedefstructsockaddr_inAddr_in;#defineBACKLOG5#defineErrExit(msg)do{perror(msg);exit(EXIT_FAILURE);}while(0)voidArgment(intargc,char*argv[]);intCreateSocket(char*argv[]);intDataHandle(intfd);#endif#includenet.hvoidArgment(intargc,char*argv[]){if(argc3){fprintf(stderr,%saddrport\n,argv[0]);exit(0);}}intCreateSocket(char*argv[]){/*创建套接字*/intfdsocket(AF_INET,SOCK_STREAM,0);if(fd0)ErrExit(socket);/*允许地址快速重用*/intflag1;if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,flag,sizeof(flag)))perror(setsockopt);/*设置通信结构体*/Addr_in addr;bzero(addr,sizeof(addr));addr.sin_familyAF_INET;addr.sin_porthtons(atoi(argv[2]));/*绑定通信结构体*/if(bind(fd,(Addr*)addr,sizeof(Addr_in)))ErrExit(bind);/*设置套接字为监听模式*/if(listen(fd,BACKLOG))ErrExit(listen);returnfd;}intDataHandle(intfd){charbuf[BUFSIZ]{};Addr_in peeraddr;socklen_tpeerlensizeof(Addr_in);if(getpeername(fd,(Addr*)peeraddr,peerlen))perror(getpeername);intretrecv(fd,buf,BUFSIZ,0);if(ret0)perror(recv);if(ret0){printf([%s:%d]data: %s\n,inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port),buf);}returnret;}#includenet.h#includesys/select.h#defineMAX_SOCK_FD1024voidsetKeepAlive(intsockfd,intattr_on,socklen_tidle_time,socklen_tinterval,socklen_tcnt){setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(constchar*)attr_on,sizeof(attr_on));setsockopt(sockfd,SOL_TCP,TCP_KEEPIDLE,(constchar*)idle_time,sizeof(idle_time));setsockopt(sockfd,SOL_TCP,TCP_KEEPINTVL,(constchar*)interval,sizeof(interval));setsockopt(sockfd,SOL_TCP,TCP_KEEPCNT,(constchar*)cnt,sizeof(cnt));}intmain(intargc,char*argv[]){inti,ret,fd,newfd;fd_set set,tmpset;Addr_in clientaddr;socklen_tclientlensizeof(Addr_in);/*检查参数小于3个 直接退出进程*/Argment(argc,argv);/*创建已设置监听模式的套接字*/fdCreateSocket(argv);FD_ZERO(set);FD_ZERO(tmpset);FD_SET(fd,set);while(1){tmpsetset;if((retselect(MAX_SOCK_FD,tmpset,NULL,NULL,NULL))0){perror(select);getchar();}if(FD_ISSET(fd,tmpset)){/*接收客户端连接并生成新的文件描述符*/if((newfdaccept(fd,(Addr*)clientaddr,clientlen))0){perror(accept);getchar();}#if1intkeepAlive1;//设定KeepAliveintkeepIdle5;//开始首次KeepAlive探测前的TCP空闭时间intkeepInterval5;//两次KeepAlive探测间的时间间隔intkeepCount3;//判定断开前的KeepAlive探测次数setKeepAlive(newfd,keepAlive,keepIdle,keepInterval,keepCount);#endifprintf([%s:%d]已建立连接\n,inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));FD_SET(newfd,set);}else{//处理客户端数据for(ifd1;iMAX_SOCK_FD;i){if(FD_ISSET(i,tmpset)){if(DataHandle(i)0){if(getpeername(i,(Addr*)clientaddr,clientlen))perror(getpeername);printf([%s:%d]断开连接\n,inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));FD_CLR(i,set);close(i);}}}}}close(fd);return0;}