介于阻塞和非阻塞之间,可以自己设置一个时间,在设置的时间内,如果没数据就阻塞等待,如果设置的时间到了还没数据,则立即切换为非阻塞。
#include
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
其中 最后一个参数 timeout 就是超时时间
NULL 永久阻塞 直到就绪为止
也可以使用下面的结构体指定超时时间
struct timeval {
long tv_sec; /* 秒 */
long tv_usec; /* 微秒 */
};
如果结构体的两个成员都为 0 则表示非阻塞
返回值:
成功 就绪的文件描述符个数
失败 -1 重置错误码
超时 0
#include
int main(int argc, char const *argv[])
{
if(3 != argc){
printf("Usage:%s Ipv4 port\n",argv[0]);
exit(-1);
}
//创建套接字
int sockfd=0;
if(-1 ==(sockfd = socket(AF_INET,SOCK_STREAM,0))){
ERR_LOG("socket error");
}
//填充结构体
struct sockaddr_in serveraddr;
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
serveraddr.sin_port=htons(atoi(argv[2]));
socklen_t serverlen = sizeof(serveraddr);
//绑定
if(-1 == bind(sockfd,(struct sockaddr *)&serveraddr,serverlen)){
ERR_LOG("bind error");
}
//开启监听
if(-1 ==listen(sockfd,5)){
ERR_LOG("listen error");
}
//创建集合
fd_set readfds;
FD_ZERO(&readfds);
fd_set tempfds;
FD_ZERO(&tempfds);
int acceptfd=0;
int max_fd=0;
int ret=0;
int i=0;
int nbytes=0;
char buff[128]={0};
//将套接字加入集合
FD_SET(sockfd,&readfds);
max_fd=max_fd>sockfd?max_fd:sockfd;
struct timeval tm;
while(1){
tm.tv_sec=5;
tm.tv_usec=0;
tempfds = readfds;
if(-1 == (ret = select(max_fd+1,&tempfds,NULL,NULL,&tm))){
ERR_LOG("select error");
}else if(0 == ret){
printf("已经五秒没有fd就绪了\n");
continue;
}
printf("有fd就绪\n");
//说明有fd就绪了,判断是不是sockfd
for(i=3;i<max_fd+1 && 0<ret;i++){
//先判断是不是这个fd就绪了
if(FD_ISSET(i,&tempfds)){
ret--;
//判断就绪的fd是不是sockfd
if(sockfd == i){//说明有新的客户端接入
if(-1 == (acceptfd = accept(i,NULL,NULL))){
ERR_LOG("accept error");
}
//将新的acceptfd加入集合
FD_SET(acceptfd,&readfds);
max_fd=max_fd>acceptfd?max_fd:acceptfd;
printf("用户[%d]连接\n",i);
}else{//说明是已连接的客户端要通信
printf("接收到用户[%d]的消息\n",i);
if(-1 == (nbytes = recv(i,buff,sizeof(buff),0))){
ERR_LOG("recv error");
}else if(0 == nbytes){
printf("用户[%d]断开连接\n",i);
//从集合中删除
FD_CLR(i,&readfds);
close(i);
continue;
}
if(!strcmp(buff,"quit")){//用户退出
printf("用户[%d]退出\n",i);
//从集合中删除
FD_CLR(i,&readfds);
close(i);
continue;
}
//正常数据处理
printf("用户[%d]发送消息[%s]\n",i,buff);
strcat(buff,"T^T");
if(-1 == send(i,buff,sizeof(buff),0)){
ERR_LOG("send error");
}
}
}
}
}
close(sockfd);
return 0;
}

#include
#include
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);
功能:设置或者获取套接字属性
参数:
sockfd:要操作的套接字
level:选项的级别
套接字API级别 SOL_SOCKET
TCP级别 IPPROTO_TCP
IP级别 IPPROTO_IP
optname:选项的名字
套接字API级别
SO_BROADCAST 允许发送广播
SO_RCVBUF 接收缓冲区的大小
SO_SNDBUF 发送缓冲区的大小
SO_RCVTIMEO 接收超时时间
参数是一个 struct timeval 结构体
如果超时了 调用会返回-1 错误码 为 EAGAIN
SO_SNDTIMEO 发送超时时间
SO_REUSEADDR 允许端口复用
TCP级别
TCP_NODELAY 关闭Nagle算法
IP级别
IP_ADD_MEMBERSHIP 设置加入多播组
optval:选项的值,没有特殊说明时,一般是int型指针;1打开;0关闭
optlen:optval的长度
返回值:
成功 0
失败 -1 重置错误码
#include
int main(int argc, char const *argv[])
{
int sockfd=0;
if(-1 == (sockfd = socket(AF_INET,SOCK_STREAM,0))){
ERR_LOG("socket error");
}
int snd_buff_size = 0;
int snd_buff_size_len = sizeof(snd_buff_size);
if(-1 == getsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,&snd_buff_size,&snd_buff_size_len)){
ERR_LOG("getsockopt error");
}
printf("Sendbuff:%dKB\n",snd_buff_size/1024);
int recv_buff_size = 0;
int recv_buff_size_len = sizeof(recv_buff_size);
if(-1 == getsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&recv_buff_size,&recv_buff_size_len)){
ERR_LOG("getsockopt error");
}
printf("Recvbuff:%dKB\n",recv_buff_size/1024);
return 0;
}
输出结果:

int flag = 1;
if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)))
ERR_LOG("setsockopt error");
//把上述代码加在 创建套接字之后 和 bind 之前即可
struct timeval tm;
tm.tv_sec = 5;
tm.tv_usec = 0;
if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tm, sizeof(tm)))
ERR_LOG("setsockopt error");
当超时时,会立刻返回一个错误(EAGAIN),可以检测错误码来决定如何处理。
注:由已经设置过超时时间的 sockfd 产生的 acceptfd 会继承超时属性,
如果想用一样的超时时间,就无需对 acceptfd 设置了;
如果不想用一样的时间,可以使用setsockopt函数对每个acceptfd单独设置