本文所有知识均参考网课:
https://study.163.com/course/courseLearn.htm?courseId=1002913013&share=1&shareId=1145943119#/learn/video?lessonId=1003301249&courseId=1002913013
套接字选项用于修饰套接字以及其底层通讯协议的各种行为。函数setsockopt和getsockopt可以查看和设置套接字的各种选项。
以下两个函数返回值:若成功,返回0;若出错,返回-1
int getsockopt(int sockfd,int level,int optname,
void *optval,socklen_t *optlen);
int setsockopt(int sockfd,int level,int optname,
const void *optval,socklen_t optlen);
其中参数level标识了选项应用的协议。如果选项是通用的套接字层次选项,则level设置成SOL_SOCKET。否则,level设置成控制这个选项的协议编号。对于TCP选项,level是IPPROTO_TCP,对于IP,level是IPPROTO_IP。
选项 | 参数optval的类型 | 描述 |
---|---|---|
SO_ACCEPTCONN | int | 返回信息指示该套接字是否能被监听(仅getsockopt) |
SO_BROADCAST | int | 如果*optval非0,广播数据段 |
SO_DEBUG | int | 如果*optval非0,启用网络驱动调试功能 |
SO_DONTROUTE | int | 如果*optval非0,绕过通常路由 |
SO_ERROR | int | 返回挂起的套接字错误并清除(仅getsockopt) |
SO_KEEPALIVE | int | 如果*optval非0,启用周期性keep-alive报文 |
SO_LINGER | struct linger | 当还有未发报文而套接字已关闭时,延迟时间 |
SO_OOBINLINE | int | 如果*optval非0,将带外数据放在普通数据中 |
SO_RCVBUF | int | 接收缓冲区的字节长度 |
SO_RCVLOWAT | int | 接收调用中返回的最小数据字节数 |
SO_RCVTIMEO | struct timeval | 套接字接收调用的超时值 |
SO_REUSEADDR | int | 如果*optval非0,重用bind中的地址 |
SO_SNDBUF | int | 发送缓冲区的字节长度 |
SO_SNDLOWAT | int | 发送调用中传送的最小数据字节数 |
SO_SNDTIMEO | struct timeval | 套接字发送调用的超时值 |
SO_TYPE | int | 标识套接字类型(仅getsockopt) |
SO_BROADCAST选项控制着UDP套接字是否能够发送广播数据报,选项的类型为int,非零意味这"是",注意,只有UDP套接字可以使用这个选项,TCP是不能使用广播的。
int opt = 1;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) < 0){
//错误处理
}
if(setsockopt(sockfd,SOL_SOCKET,
SO_BROADCAST,&opt,sizeof(opt)) < 0){
//错误处理
}
每一个套接字有一个发送缓冲区和接收缓冲区,这两个缓冲区由底层协议使用,接收缓冲区存放由协议接收的数据直到被应用程序读走,发送缓冲区存放应用写出的数据直到被协议发送出去。SO_SNDBUF和SO_RCVBUF选项分别控制发送和接收缓存区的大小,他们的类型均为int,以字节为单位。
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0){
//错误处理
}
//获得发送缓冲区的大小存放在下面程序中的opt参数中
if(getsockopt(sockfd,SOL_SOCKET,
SO_SNDBUF,&opt,sizeof(opt)) < 0){
//错误处理
}
opt += 2048;
if(setsockopt(sockfd,SOL_SOCKET,
SO_SNDBUF,&opt,sizeof(opt)) < 0){
//错误处理
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
int sockfd;
void sig_handler(int signo){
if(signo == SIGINT){
printf("receiver will exited\n");
close(sockfd);
exit(1);
}
}
int main(int argc,char *argv[]){
if(argc < 2){
fprintf(stderr,"usage:%s port\n",argv[0]);
exit(1);
}
if(signal(SIGINT,sig_handler) == SIG_ERR){
perror("signal sigint error");
exit(1);
}
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd < 0){
perror("socket error");
exit(1);
}
struct sockaddr_in serveraddr;
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[1]));
serveraddr.sin_addr.s_addr = INADDR_ANY;
if(bind(sockfd,(struct sockaddr*)&serveraddr,
sizeof(serveraddr)) < 0){
perror("bind error");
exit(1);
}
char buffer[1024];
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
while(1){
memset(buffer,0,sizeof(buffer));
memset(&clientaddr,0,sizeof(clientaddr));
if(recvfrom(sockfd,buffer,sizeof(buffer),0,
(struct sockaddr*)&clientaddr,&len) < 0){
perror("recvfrom error");
exit(1);
}
else{
char ip[16];
inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,
ip,sizeof(ip));
int port = ntohs(clientaddr.sin_port);
printf("%s(%d):%s\n",ip,port,buffer);
}
}
return 0;
}
#include
#include
#include
#include
#include
#include
int main(int argc,char *argv[]){
if(argc < 3){
fprintf(stderr,"usage: %s ip port\n",argv[0]);
exit(1);
}
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd < 0){
perror("socket error");
exit(1);
}
int opt = 1;
//采用广播方式发送
setsockopt(sockfd,SOL_SOCKET,
SO_BROADCAST,&opt,sizeof(opt));
struct sockaddr_in serveraddr;
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
inet_pton(AF_INET,argv[1],
&serveraddr.sin_addr.s_addr);
printf("I will broadcast...\n");
char *info = "hello llc";
size_t size = strlen(info) * sizeof(char);
if(sendto(sockfd,info,size,0,
(struct sockaddr*)&serveraddr,
sizeof(serveraddr)) < 0){
perror("sento error");
exit(1);
}
else{
printf("broadcast success\n");
}
return 0;
}
以下都在本机测试:
1 启动接收端接收客户端:
2 启动发送端连接,同时接收端接收到客户端的连接:
返回信息指示该套接字是否能被监听(仅getsockopt)