linux select编程

linux select编程

可以通过select写非阻塞io的程序,可以参考我的博客阻塞与非阻塞

函数介绍

select函数原型

 #include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

参数:
    第一个参数:int nfds是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1
    第二个参数:fd_set *readfds 用来检查一组可读性的文件描述符。
    第三个参数:fd_set *writefds 用来检查一组可写性的文件描述符。
    第四个参数:fd_set *exceptfds 用来检查文件文件描述符是否异常
    第五个参数:sreuct timeval *timeout是一个时间结构体,用来设置超时时间
        timeout:最多等待时间,对阻塞操作则为NULL

    时间体的结构:
        struct timeval {
        time_t         tv_sec;     /* seconds */
          suseconds_t    tv_usec;    /* microseconds */
        };

返回值:
    负值:select错误
    正值:表示某些文件可读或可写
    0:等待超时,没有可读写或错误的文件

select中常用的其他函数

void FD_CLR(int fd, fd_set *set);//用于在文件描述符集合中删除一个文件描述符。
int  FD_ISSET(int fd, fd_set *set);//用于测试指定的文件描述符是否在该集合中。
void FD_SET(int fd, fd_set *set);//用于在文件描述符集合中增加一个新的文件描述符
void FD_ZERO(fd_set *set);//检查集合中指定的文件描述符是否可以读写

select的使用步骤

1 初始化,把当前的listenfd加入集合,初始化socketfd数组
2 监听数据,接受到connfd把它加入集合
3 循环处理文本,如果可读,读取长度,关闭sockfd
4 循环处理文本,读取指定长度的数据,将长度和数据写入data
5 往所有就绪的socketfd里面写入string

题外话,socket关闭后端口不可用

int   sockfd;   
int   opt   =   1;   
int   len   =   sizeof(opt);   

sockfd   =   socket(AF_INET,   SOCK_STREAM,   0);   
setsockopt(sockfd,   SOL_SOCKET,   SO_REUSEADDR,   &opt,   &len);

下面的
select编程的大致逻辑

1 设置 socket为非阻塞
2 建立死循环-可以不用这么做
3 初始化 fset
4 将当前的set加入到里面
5 循环遍历fset array---用于存储可用的soketfd的集合
6 使用select监控文件句柄
7 如果有fset,accpet接受socketfd,标识socketfd为可以读
8 集中处理所有的fset array,处理读和写

实例

服务端的例子

#include <iostream>


#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>
#include <fcntl.h>

//using namespace std;

#define BUFFER_SIZE 1024
#define HEAD_LEN 4
#define MAX_FD 1023
#define BUF_LEN 1000


void tsocket(int argc, const char * argv[]);

int main(int argc, const char * argv[]) {
    tsocket(argc,argv);
    return 0;
}
void tsocket(int argc, const char * argv[]){
    if(argc < 3){
        exit(-1);
    }

    const char* ip = argv[1];
    int port = atoi(argv[2]);
    int backlog = atoi(argv[3]);

    std::cout << "ip=" << ip << " port="<<port << " backlog=" << backlog  << std::endl;

    //声明信息
    char sockBuf[BUFFER_SIZE];
    int socketfd[MAX_FD];
    char data[BUFFER_SIZE+HEAD_LEN];

    //接受数据
    size_t ret;

    int fd;
    int check_ret;
    fd = socket(PF_INET,SOCK_STREAM , 0);
    assert(fd >= 0);

    //端口重用
    int   opt   =   1;   
    int   optLen   =   sizeof(opt);  
    setsockopt(fd, SOL_SOCKET,   SO_REUSEADDR,   &opt,   optLen);

    struct sockaddr_in address;
    bzero(&address,sizeof(address));
    //转换成网络地址
    address.sin_port = htons(port);
    address.sin_family = AF_INET;
    //地址转换
    inet_pton(AF_INET, ip, &address.sin_addr);
    //绑定ip和端口
    check_ret = bind(fd,(struct sockaddr*)&address,sizeof(address));
    assert(check_ret >= 0);

    //非阻塞的socket
    int cflags = fcntl(fd,F_GETFL,0);
    fcntl(fd,F_SETFL, cflags|O_NONBLOCK);

    //创建监听队列,用来存放待处理的客户连接
    check_ret = listen(fd, backlog);
    assert(check_ret >= 0);
    struct sockaddr_in addressClient;
    socklen_t clientLen = sizeof(addressClient);

    //读的fd_set
    fd_set  readset; 
    int  sockArray[MAX_FD];
    int i,j,len,n;
    int connfd;
    for(i=0;i<MAX_FD;++i){
        sockArray[i] = 0;
    }

    while(1){
        //初始化 readset
        FD_ZERO(&readset);
        //把当前的socket加入监控
        FD_SET(fd,&readset);
        //如果出现可读,加入监控
        for(i=0;i<MAX_FD;++i){
            if(sockArray[i] == 1) FD_SET(i,&readset);
        }
        //select监视的文件句柄数,视进程中打开的文件数而定
        //一般设为你要监视各文件
        if (select(1024, &readset, NULL, NULL, NULL) < 0)
        {
            perror("select error");
            exit(-1);
        }

        //将可读放入就绪里面
        if(FD_ISSET(fd,&readset)){
            connfd = accept(fd,NULL,NULL);
            sockArray[connfd] = 1;
            printf("client %d connected\n", connfd);
        }

        //集中处理可读
        for(i=0;i<MAX_FD;++i){
            if((sockArray[i] == 1) && FD_ISSET(i,&readset) ){
                len = read(i,sockBuf,BUF_LEN);
                if(len < 0){
                    perror("read error");
                    exit(-1);
                }
                //如果没有数据就等待
                if(len == 0){
                    close(i);
                    sockArray[i] = 0;
                    continue;
                }
                printf("client %d send %s\n", len, sockBuf);
                //写入数据
                write(i,sockBuf,len);
            }
        }
    }
    close(fd);
}

客户端的例子

#include <iostream>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>

#define BUF_LEN 1023

void tserver(int argc, const char * argv[]);

int main(int argc, const char * argv[]) {
    tserver(argc,argv);
    return 0;
}
void tserver(int argc, const char * argv[]){
    std::cout << "t server" << std::endl;
    if(argc < 3){
        exit(-1);
    }

    const char* ip = argv[1];
    int port = atoi(argv[2]);
    int backlog = atoi(argv[3]);

    std::cout << "ip=" << ip << " port="<<port << " backlog=" << backlog  << std::endl;

    int fd;
    int check_ret;

    fd = socket(PF_INET,SOCK_STREAM , 0);
    assert(fd >= 0);

    struct sockaddr_in address;
    bzero(&address,sizeof(address));

    //转换成网络地址
    address.sin_port = htons(port);
    address.sin_family = AF_INET;
    //地址转换
    inet_pton(AF_INET, ip, &address.sin_addr);
    check_ret = connect(fd, (struct sockaddr*) &address, sizeof(address));
    assert(check_ret >= 0);
    //发送数据
    //const char* oob_data = "abc";
    const char* normal_data = "my boy!";
    send(fd, normal_data, strlen(normal_data), 0);
    printf("send data len=%lu,msg=%s\n",strlen(normal_data),normal_data);

    //接受数据
    int len;
    char sockBuf[BUF_LEN];
    memset(sockBuf, '\0', BUF_LEN);
    len = recv(fd, sockBuf, BUF_LEN-1, 0);
    printf("receive data len=%d,msg=%s\n",len,sockBuf);

    close(fd);
}

参考:

http://www.cnblogs.com/wenqiang/p/5508541.html
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值