怎样做营销型网站,河南建筑官网首页,江苏系统建站怎么用,wordpress 导出pdf文件大小1 基本概念
设置套接字的选项对套接字进行控制除了设置选项外#xff0c;还可以获取选项选项的概念相当于属性#xff0c;所以套接字选项也可说是套接字属性有些选项#xff08;属性#xff09;只可获取#xff0c;不可设置#xff1b;有些选项既可设置也可获取
2 选项…1 基本概念
设置套接字的选项对套接字进行控制除了设置选项外还可以获取选项选项的概念相当于属性所以套接字选项也可说是套接字属性有些选项属性只可获取不可设置有些选项既可设置也可获取
2 选项的级别
一些选项都是针对一种特定的协议 一些选项适用于所有类型的套接字 选项级别(level)的概念
2.1 常用的级别
SOL_SOCKET该级别的选项只作用于套接字本身SOL_LRLMP该级别的选项作用于IrDA协议IPPROTO_IP该级别的选项作用于IPv4协议IPPROTO_IPV6该级别的选项作用于IPv6协议IPPROTO_RM该级别的选项作用于可靠的多播传输IPPROTO_TCP该级别的选项适用于流式套接字IPPROTO_UDP该级别的选项适用于数据报套接字
2.2 SOL_SOCKET的常用选项
选项名称说明获取/设置SO_ACCEPTCONN套接字是否处于监听状态获取SO_BROADCAST允许发送广播数据两者都可SO_DEBUG允许调试两者都可SO_DONTROUTE不查找路由两者都可SO_ERROR获得套接字错误获取SO_KEEPALIVE保活连接两者都可SO_LINGER延迟关闭连接两者都可SO_OOBINLINE带外数据放入正常数据流两者都可SO_RCVBUF接收缓冲区大小两者都可SO_SNDBUF发送缓冲区大小两者都可SO_REUSERADDR允许重用本地地址和端口两者都可SO_TYPE获得套接字类型获取
2.3 IPPROTO_IP级别的常用选项
选项名称说明获取/设置IP_OPTIONS获取或设置IP头部内的选项两者都可IP_HDRINCL是否将IP头部与数据一起提交给Winsock函数两者都可IP_TTLIP TTL相关两者都可
3获取套接字选项
3.1 getsockopt函数
#include sys/types.h /* See NOTES */
#include sys/socket.h
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);参数 sockfd套接字描述符 level表示选项的级别 optname表示要获取的选项名称 optval指向存放接收到的选项内容的缓冲区 optlen指向optval所指缓冲区的大小 函数返回值 执行成功返回0否则返回‒1errno来获取错误码 常见的错误码 EBADF参数sockfd不是有效的文件描述符 EFAULT参数optlen太小或optval所指缓冲区非法 EINVAL参数level未知或非法 ENOPROTOOPT选项未知或不被指定的协议族所支持 ENOTSOCK描述符不是一个套接字描述符
3.2 示例获取流套接字和数据报套接字接收和发送的内核缓冲区大小
#include stdio.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h
#include sys/ioctl.hint main()
{int err,s socket(AF_INET, SOCK_STREAM, 0);//创建流套接字if (s -1) {printf(Error at socket()\n);return -1;}int su socket(AF_INET, SOCK_DGRAM, 0); //创建数据报套接字if (s -1) {printf(Error at socket()\n);return -1;}int optVal;int optLen sizeof(optVal);//获取流套接字接收缓冲区大小if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)optVal,(socklen_t *)optLen) -1)printf(getsockopt failed:%d, errno);elseprintf(Size of stream socket receive buffer: %ld bytes\n, optVal);//获取流套接字发送缓冲区大小if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)optVal,(socklen_t *)optLen) -1)printf(getsockopt failed:%d, errno);else printf(Size of streaming socket send buffer: %ld bytes\n, optVal);//获取数据报套接字接收缓冲区大小if (getsockopt(su, SOL_SOCKET, SO_RCVBUF, (char*)optVal,(socklen_t *)optLen) -1)printf(getsockopt failed:%d, errno);elseprintf(Size of datagram socket receive buffer: %ld bytes\n, optVal);//获取数据报套接字发送缓冲区大小if (getsockopt(su, SOL_SOCKET, SO_SNDBUF, (char*)optVal,(socklen_t *)optLen) -1)printf(getsockopt failed:%d, errno);elseprintf(Size of datagram socket send buffer:%ld bytes\n, optVal);getchar();return 0;
}3.3 示例获取当前套接字类型
#include stdio.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h
#include sys/ioctl.hint main()
{int err;int s socket(AF_INET, SOCK_STREAM, 0); //创建流套接字if (s -1) {printf(Error at socket()\n);return -1;}int su socket(AF_INET, SOCK_DGRAM, 0); //创建数据报套接字if (s -1) {printf(Error at socket()\n);return -1;}int optVal;int optLen sizeof(optVal);//获取套接字s的类型if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char*)optVal, (socklen_t *)optLen) -1)printf(getsockopt failed:%d, errno);else{if (SOCK_STREAM optVal) // SOCK_STREAM宏定义值为1printf(The current socket is a stream socket.\n); //当前套接字是流套接字else if (SOCK_DGRAM optVal) // SOCK_ DGRAM宏定义值为2printf(The current socket is a datagram socket.\n);//当前套接字是数据报套接字}//获取套接字su的类型if (getsockopt(su, SOL_SOCKET, SO_TYPE, (char*)optVal, (socklen_t *)optLen) -1)printf(getsockopt failed:%d, errno);else{if (SOCK_STREAM optVal) // SOCK_STREAM宏定义值为1printf(The current socket is a stream socket.\n);else if (SOCK_DGRAM optVal) // SOCK_ DGRAM宏定义值为2printf(The current socket is a datagram socket.\n);}getchar();return 0;
}
3.4 示例判断套接字是否处于监听状态
#include stdio.h
#include stdlib.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h
#include sys/ioctl.htypedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;#define ErrExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)int main(int argc, char *argv[])
{Addr_in service;if(argc 3){printf(%s[ADDR][PORT]\n, argv[0]);exit(0);}int s socket(AF_INET, SOCK_STREAM, 0); //创建一个流套接字if (s -1) ErrExit(socket);//允许地址的立即重用char on 1;setsockopt(s, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on));service.sin_family AF_INET;service.sin_addr.s_addr inet_addr(argv[1]);service.sin_port htons( atoi(argv[2]) );if (bind(s, (Addr*)service, sizeof(service)) -1) //绑定套接字ErrExit(bind);int optVal;int optLen sizeof(optVal);//获取选项SO_ACCEPTCONN的值if (getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, (char*)optVal, (socklen_t*)optLen) -1)printf(getsockopt failed:%d,errno);else printf(Before listening, The value of SO_ACCEPTCONN:%d, The socket is not listening\n, optVal);// 开始侦听if (listen(s, 100) -1)ErrExit(listen);//获取选项SO_ACCEPTCONN的值if (getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, (char*)optVal, (socklen_t*)optLen) -1)ErrExit(getsockopt);else printf(After listening,The value of SO_ACCEPTCONN:%d, The socket is listening\n, optVal);return 0;
}4 设置套接字选项
4.1 setsockopt函数
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);参数 sockfd套接字描述符 level表示选项的级别 optname表示要获取的选项名称 optval指向存放接收到的选项内容的缓冲区 optlen指向optval所指缓冲区的大小 函数返回值 执行成功返回0否则返回‒1errno来获取错误码
4.2 示例启用套接字的保活机制**
#include stdio.h
#include stdlib.h
#include assert.h
#include sys/time.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include unistd.h
#include errno.h
#include sys/ioctl.htypedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;#define ErrExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)int main(int argc, char *argv[])
{Addr_in service;if(argc 3){printf(%s[ADDR][PORT]\n, argv[0]);exit(0);}int s socket(AF_INET, SOCK_STREAM, 0); //创建一个流套接字if( s 0)ErrExit(socket);char on 1;setsockopt(s, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on));service.sin_family AF_INET;service.sin_addr.s_addr inet_addr(argv[1]);service.sin_port htons(atoi(argv[2]));if (bind(s, (Addr *) service, sizeof(service)) -1) //绑定套接字ErrExit(bind);int optVal 1;//一定要初始化int optLen sizeof(int);//获取选项SO_KEEPALIVE的值if (getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*)optVal, (socklen_t *)optLen) -1)ErrExit(getsockopt);else printf(After listening,the value of SO_ACCEPTCONN:%d\n, optVal);optVal 1;if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*)optVal, optLen) ! -1)printf(Successful activation of keep alive mechanism.\n);//启用保活机制成功if (getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*)optVal, (socklen_t *)optLen) -1)ErrExit(getsockopt);else printf(After setting,the value of SO_KEEPALIVE:%d\n, optVal);return 0;
}5 综合示例
server.c
#include net.h
#include sys/select.h
#define MAX_SOCK_FD 1024void setKeepAlive (int sockfd, int attr_on, socklen_t idle_time, socklen_t interval, socklen_t cnt)
{setsockopt (sockfd, SOL_SOCKET, SO_KEEPALIVE, (const char *) attr_on, sizeof (attr_on));setsockopt (sockfd, SOL_TCP, TCP_KEEPIDLE, (const char *) idle_time, sizeof (idle_time));setsockopt (sockfd, SOL_TCP, TCP_KEEPINTVL, (const char *) interval, sizeof (interval));setsockopt (sockfd, SOL_TCP, TCP_KEEPCNT, (const char *) cnt, sizeof (cnt));
}int main(int argc, char *argv[])
{int i, ret, fd, newfd;fd_set set, tmpset;Addr_in clientaddr;socklen_t clientlen sizeof(Addr_in);/*检查参数小于3个 直接退出进程*/Argment(argc, argv);/*创建已设置监听模式的套接字*/fd CreateSocket(argv);FD_ZERO(set);FD_ZERO(tmpset);FD_SET(fd, set);while(1){tmpset set;if( (ret select(MAX_SOCK_FD, tmpset, NULL, NULL, NULL)) 0){perror(select);getchar();}if(FD_ISSET(fd, tmpset) ){/*接收客户端连接并生成新的文件描述符*/if( (newfd accept(fd, (Addr *)clientaddr, clientlen) ) 0){perror(accept);getchar();}
#if 1int keepAlive 1; //设定KeepAliveint keepIdle 5; //开始首次KeepAlive探测前的TCP空闭时间int keepInterval 5; //两次KeepAlive探测间的时间间隔int keepCount 3; //判定断开前的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(i fd 1; i MAX_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);return 0;
}socket.c
#include net.hvoid Argment(int argc, char *argv[]){if(argc 3){fprintf(stderr, %saddrport\n, argv[0]);exit(0);}
}
int CreateSocket(char *argv[]){/*创建套接字*/int fd socket(AF_INET, SOCK_STREAM, 0);if(fd 0)ErrExit(socket);/*允许地址快速重用*/int flag 1;if( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, flag, sizeof(flag) ) )perror(setsockopt);/*设置通信结构体*/Addr_in addr;bzero(addr, sizeof(addr) );addr.sin_family AF_INET;addr.sin_port htons( atoi(argv[2]) );/*绑定通信结构体*/if( bind(fd, (Addr *)addr, sizeof(Addr_in) ) )ErrExit(bind);/*设置套接字为监听模式*/if( listen(fd, BACKLOG) )ErrExit(listen);return fd;
}
int DataHandle(int fd){char buf[BUFSIZ] {};Addr_in peeraddr;socklen_t peerlen sizeof(Addr_in);if( getpeername(fd, (Addr *)peeraddr, peerlen) )perror(getpeername);int ret recv(fd, buf, BUFSIZ, 0);if(ret 0)perror(recv);if(ret 0){printf([%s:%d]data: %s\n, inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port), buf);}return ret;
}net.h
#ifndef _NET_H_
#define _NET_H_#include stdio.h
#include stdlib.h
#include sys/socket.h
#include netinet/in.h
#include netinet/tcp.h
#include arpa/inet.h
#include unistd.h
#include strings.h
#include errno.htypedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;
#define BACKLOG 5
#define ErrExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0)void Argment(int argc, char *argv[]);
int CreateSocket(char *argv[]);
int DataHandle(int fd);#endif通过wireshark查看设置keepalive的包
6 练习
编程实现带保活连接功能的TCP代码要求开始首次KeepAlive探测前的TCP空闭时间为3秒两次KeepAlive探测间的时间间隔为3次判定断开前的KeepAlive探测次数为7次。提交代码和完成通信的截图 注意部分操作系统需要添加额外头文件 netinet/tcp.h
#include stdio.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include sys/time.h
#include sys/types.h
#include unistd.h
#include stdlib.h
#include string.h
#include netinet/tcp.h#define MAX_SOCK_FD 1024
#define BACKLOG 5#define ErrExit(msg) do{perror(msg); exit(EXIT_FAILURE);} while(0)void SetKeepAlive(int sockfd,int attr_on, socklen_t idle_time, socklen_t interval, socklen_t cnt)
{setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (const char *) attr_on, sizeof(attr_on));setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, (const char *) idle_time, sizeof(idle_time));setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (const char *) interval, sizeof(interval));setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (const char *) cnt, sizeof(cnt));
}int DataHandle(int fd)
{char buf[BUFSIZ] {};int ret;struct sockaddr_in peeraddr;socklen_t peerlen sizeof(struct sockaddr_in);if(getpeername(fd, (struct sockaddr *)peeraddr, peerlen) )perror(getpeername);ret recv(fd, buf, BUFSIZ, 0);if(ret 0){perror(recv);}if( ret 0){printf([%s:%d]data: %s\n, inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port), buf);}return ret;
}int main(int argc,char *argv[])
{int fd, new_fd, i ,ret;fd_set set, tmpset;struct sockaddr_in addr, client_addr;socklen_t clientlen sizeof(client_addr);int flag 1;if(argc 3){printf(%s addr port\n,argv[0]);exit(0);}//create socketfd socket(AF_INET, SOCK_STREAM, 0);if(fd 0){ErrExit(socket);}//avoids the error of ports being occupiedif( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, flag, sizeof(flag) ) ){perror(setsockopt);}//init struct sockaddr_inmemset(addr, 0, sizeof(addr));addr.sin_family AF_INET;addr.sin_port htons( atoi(argv[2]));if (inet_aton(argv[1], addr.sin_addr) 0){printf(Invalid address\n);exit(EXIT_FAILURE);}//bindif(bind(fd, (struct sockaddr *) addr, sizeof(struct sockaddr_in)) -1){ErrExit(bind);}//listenif(listen(fd, BACKLOG) -1){ErrExit(listen);}//selectFD_ZERO(set);FD_ZERO(tmpset);FD_SET(fd, set);while(1){tmpset set;if((ret select(MAX_SOCK_FD, tmpset,NULL,NULL,NULL)) 0){ErrExit(select);}if(FD_ISSET(fd, tmpset) 0){new_fd accept(fd,(struct sockaddr *)client_addr,clientlen);if(new_fd 0){perror(accept);}#if 1int keepAlive 1;int keepIdle 3;int keepInterval 3;int keepCount 7;SetKeepAlive(new_fd, keepAlive, keepIdle, keepInterval, keepCount);
#endif printf([%s:%d]connected\n,inet_ntoa(client_addr.sin_addr),\ntohs(client_addr.sin_port));FD_SET(new_fd, set);}else //if(FF_ISSET(fd, tmpset 0) //handle client{for(i fd 1; i MAX_SOCK_FD; i){// can readif(FD_ISSET(i,tmpset)){if(DataHandle(i) 0){if(getpeername(i,(struct sockaddr *)client_addr,clientlen)) perror(getpeername);printf([%s:%d]disconnected\n,inet_ntoa(client_addr.sin_addr),\ntohs(client_addr.sin_port));FD_CLR(i,set); }}}}}close(fd);return 0;
}