0
点赞
收藏
分享

微信扫一扫

Linux(程序设计):51---select实现接收普通数据与带外数据

​​

一、项目目的

  • 本篇文章介绍使用select来​接收普通数据与带外数据,其中:


普通数据我们放在fd_set集合的可读集合中带外数据放在fs_set集合的异常集合中

二、编码实现

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define LISTEN_NUM 10

int main(int argc,char *argv[])
{
char *ip=argv[1];
int port=atoi(argv[2]);

int recvLen;
int connFd,acceptFd;
int selectMaxFd,selectRetValue;
socklen_t cliAddrLen;
char cliAddrBuf[24];
char commBuff[1024];
struct timeval waitTimeValue;
struct sockaddr_in serAddr;
struct sockaddr_in cliAddr;


if((connFd=socket(AF_INET,SOCK_STREAM,0))==-1){
perror("socket");
exit(EXIT_FAILURE);
}

bzero(&serAddr,sizeof(serAddr));
serAddr.sin_family=AF_INET;
serAddr.sin_port=htons(port);
if(inet_pton(AF_INET,ip,&serAddr.sin_addr.s_addr)==-1){
perror("inet_pton");
exit(EXIT_FAILURE);
}

if(bind(connFd,(struct sockaddr*)&serAddr,sizeof(serAddr))==-1){
perror("inet_pton");
exit(EXIT_FAILURE);
}

if(listen(connFd,LISTEN_NUM)==-1){
perror("listen");
exit(EXIT_FAILURE);
}

fd_set readSet;
fd_set errorSet;

bzero(&cliAddr,sizeof(cliAddr));
cliAddrLen=sizeof(cliAddr);
bzero(&waitTimeValue,sizeof(waitTimeValue));
waitTimeValue.tv_sec=0;
waitTimeValue.tv_usec=0;

if((acceptFd=accept(connFd,(struct sockaddr*)&cliAddr,&cliAddrLen))==-1){
if(errno==EINTR){
printf("accept:catch signal...\n");
exit(EXIT_SUCCESS);
}
}else{
bzero(cliAddrBuf,sizeof(cliAddrBuf));
if(inet_ntop(AF_INET,&cliAddr.sin_addr.s_addr,cliAddrBuf,sizeof(cliAddrBuf))==NULL){
printf("Get Connect: an unrecognized client address\n");
}else{
printf("Get Connect: %s:%d\n",cliAddrBuf,ntohl(cliAddr.sin_port));
}
}

while(1)
{
bzero(commBuff,sizeof(commBuff));
FD_ZERO(&readSet);
FD_ZERO(&errorSet);
FD_SET(acceptFd,&readSet);
FD_SET(acceptFd,&errorSet);
selectMaxFd=acceptFd+1;

//printf("selecting...\n");
switch(selectRetValue=select(selectMaxFd,&readSet,NULL,&errorSet,&waitTimeValue))
{
case -1:
if(errno==EINTR)
printf("select:catch signal...\n");
else
perror("select");
continue;
case 0:
printf("select:time out...\n");
continue;
default:
if(FD_ISSET(acceptFd,&readSet)){
if((recvLen=recv(acceptFd,commBuff,sizeof(commBuff),0))==-1){
perror("recv");
continue;
}else if(recvLen==0){
printf("client close..");
close(connFd);
exit(EXIT_SUCCESS);
}else{
printf("Get normal data of client::%s\n",commBuff);
break;
}
}
if(FD_ISSET(acceptFd,&errorSet)){
if((recvLen=recv(acceptFd,commBuff,sizeof(commBuff),MSG_OOB))==-1){
perror("recv");
continue;
}else if(recvLen==0){
printf("client close..");
close(connFd);
exit(EXIT_SUCCESS);
}else{
printf("Get oob data of client:%s\n",commBuff);
break;
}
}
break;
}

}
exit(EXIT_SUCCESS);
}


获取用户信息

  • 当我们的客户端连接之后,控制台打印客户的地址和端口,随即开始select

Linux(程序设计):51---select实现接收普通数据与带外数据_select实现接收普通数据与带外数据

Linux(程序设计):51---select实现接收普通数据与带外数据_数据_02



select阻塞1秒

  • 我们的select设置为阻塞1秒等到是否有描述符就绪

Linux(程序设计):51---select实现接收普通数据与带外数据_#include_03



获取客户正常数据

  • 我们通过客户端工具向程序发送数据,可以看到程序打印客户端工具发送的消息,并且继续开始select监听

Linux(程序设计):51---select实现接收普通数据与带外数据_select实现接收普通数据与带外数据_04



客户断开连接

  • 客户端断开之后,客户端的描述符会在读字符集中被设置为可读状态,此时我们的select函数执行返回,然后执行到recv函数,如果客户端连接关闭,那么recv就返回0,于是就打印如下的信息

Linux(程序设计):51---select实现接收普通数据与带外数据_select实现接收普通数据与带外数据_05

Linux(程序设计):51---select实现接收普通数据与带外数据_#include_06


三、程序总结

  • ①每次重新进行select都要初始化字符集和把描述符添加进字符集。如下图

Linux(程序设计):51---select实现接收普通数据与带外数据_数据_07

  • ②坑点:如果select是阻塞的,那么每次select之前都要重新设置struct timeval结构体的内容。否则select的最后一个参数就失效(变为非阻塞的了)。上面编程的时候我们把waitTimeValue放在了while的外面,发现select根本不阻塞,原因就是这个

Linux(程序设计):51---select实现接收普通数据与带外数据_select实现接收普通数据与带外数据_08


  • ④处理客户端带外数据,recv的最后一个选项设置为MSG_OOB
  • ③客户端关闭连接时,其文件描述符在select中变为可读状态。并且服务端的recv函数会返回0
  • ④坑点:绑定服务端地址的时候,用htonl出错了,要用htons函数转换才行


举报

相关推荐

0 条评论