Linux c 多路IO转接服务器(select函数使用)

Linux c 多路IO转接服务器(select函数使用),第1张

Linux c 多路IO转接服务器(select函数使用)

文章目录
  • 1、select函数介绍
  • 2、select代码
  • 3、socket常用函数出错处理封装

1、select函数介绍
int select(int nfds, fd_set *readfds, fd_set *writefds,
            fd_set *exceptfds, struct timeval *timeout);
参数:
    nfds 监听所有文件描述符中,最大文件描述符+1
    readfds:读 文件描述符监听集合(这三个参数都是传入传出参数)
    writefds:写 文件描述符监听集合
    exceptfds 异常 文件描述符监听集合
    timeout:
        >0 设置监听超时长
        NULL 阻塞监听
        0 非阻塞监听,轮询

返回值:
    >0 所有监听集合中, 满足对应事件的总数
    0 没有满足监听条件的文件描述符
    -1 设置errno


void FD_ZERO(fd_set* set);  // 清空一个文件描述符集合
    fd_set rset;
    FD_ZERO(rset);

void FD_SET(int fd,fd_set* set);    // 将待监听的文件描述符,添加到监听集合中
    FD_SET(3,&rset);

void FD_CLR(int fd,fd_set* set);    // 将一个文件描述符从监听中移除
    FD_CLR(4,&rset);

int FD_ISSET(int fd,fd_set* set);   // 判断一个文件描述符是否在监听集合中
    FD_ISSET(4,&rset);  
    返回值:在1,不在0

之前写的多线程和多进程服务器,都是通过,accept进行阻塞监听,来一个客户端(子进程、线程)就给他创建一个通信文件描述符,进行读写
要是客户端太多,就会创建太多线程或进程

select:先将建立连接的文件描述符添加到集合中,监听select 的读事件,确定是建立连接,就调用accept,此时不会阻塞,将通信的文件描述符添加到数组中,并且也添加到集合中,要是select返回数大于1表示,还有其他读事件,则去一一处理

2、select代码
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "socket_wrap.h"

int main()
{
    int listenfd,connfd,sockfd;
    char buf[BUFSIZ];
    bzero(buf,sizeof(buf));
    struct sockaddr_in clie_addr,serv_addr;
    socklen_t clie_addr_len = sizeof(clie_addr);
    listenfd = Socket(AF_INET,SOCK_STREAM,0);   // 创建套接字
    int opt = 1;
    setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));  // 端口复用函数
    bzero(&serv_addr,sizeof(serv_addr));

    serv_addr.sin_family  = AF_INET;
    serv_addr.sin_port = htons(9999);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    Bind(listenfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));  // 绑定ip port
    Listen(listenfd,128);   // 设置连接上限

    fd_set rset,allset; // all监听的,rset是传出的
    FD_ZERO(&allset);   //  将allset集合全部置零
    FD_SET(listenfd,&allset);      // 将用于连接文件描述符添加到集合中
    int ret,maxfd=listenfd; // maxfd表示当前集合中最大的文件描述符术
    int n,i,j,maxi=-1;      // maxi存储最后一个下标
    char client[FD_SETSIZE],str[INET_ADDRSTRLEN];   // FD_SETSIZE 1024 INET_ADDRSTRLEN 16
    for(i=0;imaxi)   // 保证maxi中存储最后一个下标
                maxi = i;

            FD_SET(connfd,&allset); // 将通信文件描述符添加到集合中
            if(maxfd < connfd)  // max中存储最大的文件描述符
                maxfd = connfd;
            if(ret == 1)    // 说明select 只返回了一个并且listenfd
                continue;
        }

        for(i=0;i<=maxi;i++)
        {
            if((sockfd=client[i])<0)
                continue;
            if(FD_ISSET(sockfd,&rset))
            {
                if((n=Read(sockfd,buf,sizeof(buf)))==0)
                {
                    Close(sockfd);
                    FD_CLR(sockfd,&allset);
                    client[i] = -1;
                    printf("客户端:%s:%d已经断开n",inet_ntop(AF_INET,&clie_addr.sin_addr.s_addr,str,sizeof(str)),
                    ntohs(clie_addr.sin_port));
                }
                else if(n>0)
                {
                    for(j=0;j 
 缺点:监听上限文件描述符限制,最大1024
    检测满足条件fd,自己添加业务逻辑提高小,提高了编码难度

    优点:
        跨平台
3、socket常用函数出错处理封装

socket_wrap.h

#ifndef __WRAP_H_
#define __WRAP_H_

void perr_exit(const char* s);
int Accept(int fd,struct sockaddr* sa,socklen_t* salenptr);
int Bind(int fd,const struct sockaddr* sa,socklen_t salen);
int Connect(int fd,const struct sockaddr* sa,socklen_t salen);
int Listen(int fd,int backlog);
int Socket(int family,int type,int protocol);
ssize_t Read(int fd,void* ptr,size_t nbytes);
ssize_t Write(int fd,const void* ptr,size_t nbytes);
int Close(int fd);
ssize_t Readn(int fd,void* vptr,size_t n);
ssize_t Writen(int fd,const void* vptr,size_t n);
static ssize_t my_read(int fd,char* ptr);
ssize_t Readline(int fd,void* vptr,size_t maxlen);

#endif // !__WRAP_H_

socket_wrap.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "socket_wrap.h"

void perr_exit(const char* s)
{
    perror(s);
    exit(1);
}

int Accept(int fd,struct sockaddr* sa,socklen_t* salenptr)
{
    int n;
again:
    if((n=accept(fd,sa,salenptr))<0)
    {
        if(errno == EConNABORTED || (errno==EINTR))
            goto again;
        else
            perr_exit("accept error");
    }
    return n;
}

int Bind(int fd,const struct sockaddr* sa,socklen_t salen)
{
    int n;
    if((n=bind(fd,sa,salen))<0)
    {
        perr_exit("bind error");
    }
    return n;
}

int Connect(int fd,const struct sockaddr* sa,socklen_t salen)
{
    int n;
    if((n=connect(fd,sa,salen))<0)
        perr_exit("connect error");
    return n;

}

int Listen(int fd,int backlog)
{
    int n;
    if((n=listen(fd,n))<0)
    {
        perr_exit("listen error");
    }
    return n;
}


int Socket(int family,int type,int protocol)
{
    int n;
    if((n=socket(family,type,protocol))<0)
        perr_exit("socket error");
    return n;
}

ssize_t Read(int fd,void* ptr,size_t nbytes)
{
    ssize_t n;
again:
    if((n=read(fd,ptr,nbytes))<0)
    {
        if(errno==EINTR)
            goto again;
        else
            return -1;
    }
    return n;
}

ssize_t Write(int fd,const void* ptr,size_t nbytes)
{
    ssize_t n;
    if((n=write(fd,ptr,nbytes))<0)
        perr_exit("write error");
    return n;
}

int Close(int fd)
{
    int n;
    if((n=close(fd))<0)
        perr_exit("close error");
    return n;
}

ssize_t Readn(int fd,void* vptr,size_t n)
{
    size_t nleft;   // unsigned int 剩余未读取字节数
    ssize_t nread; // int 实际读到的字节数
    char* ptr;
    ptr = vptr;
    nleft = n;
    while (nleft>0)
    {
        if((nread = read(fd,ptr,nleft))<0)
        {
            if(errno == EINTR)        
                nread = 0;
            else
             return -1;
        }
        else if(nread == 0)
            break;
        nleft -= nread;
        ptr+=nread;
    }
    return n-nread;
    
}

ssize_t Writen(int fd,const void* vptr,size_t n)
{
    size_t nleft;
    ssize_t nwritten;
    const char* ptr;
    ptr = vptr;
    nleft = n;
    while(nleft>0)
    {
        if((nwritten=write(fd,ptr,nleft))<=0)
        {
            if(nwritten<0 && errno==EINTR)
                nwritten = 0;
            else
                return -1;
        }
        nleft -= nwritten;
        ptr += nwritten;
    }
    return n;
}

static ssize_t my_read(int fd,char* ptr)
{
    static int read_cnt;
    static char* read_ptr;
    static char read_buf[100];
    if(read_cnt<=0)
    {
    again:
        if((read_cnt=read(fd,read_buf,sizeof(read_buf)))<0)
        {
            if(errno == EINTR)
                goto again;
            return -1;
        }
        else if(read_cnt == 0)
            return 0;
        read_ptr = read_buf;
    }
    read_cnt--;
    *ptr = *read_ptr++;
    return 1;
}

// 传出参数 vptr
ssize_t Readline(int fd,void* vptr,size_t maxlen)
{
    ssize_t n,rc;
    char c,*ptr;
    ptr = vptr;
    for(n=1;n					
										


					

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5433805.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-11
下一篇 2022-12-11

发表评论

登录后才能评论

评论列表(0条)

保存