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

select阻塞1秒

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

获取客户正常数据

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

客户断开连接

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

三、程序总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值