TCP并发服务器

1.阻塞IO

        CPU占用率低,等待资源时将任务挂起,不占用CPU资源,等到拿到资源后继续向下执行
2.非阻塞IO 
       能够让任务不阻塞,效率低,因为没有数据时,CPU一直空转

fcntl(fd,F_GETFL);获得文件描述符的属性

flags |= O_NONBLOCK;在现有属性中加入非阻塞属性 

fcntl(fd,F_SETFL,flags);将新属性设置回文件描述符

#include "head.h"

int main(void)
{
    int fd = 0;
    char tmpbuff[4096] = {0};
    int flags;
    ssize_t nsize = 0;
    char *pret = NULL;

    mkfifo("/tmp/myfifo", 0777);
    fd = open("/tmp/myfifo", O_RDONLY);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

    /* 获得fd文件描述符的属性 */
    flags = fcntl(fd, F_GETFL);

    /* 在现有属性中加入非阻塞属性 */
    flags |= O_NONBLOCK;

    /* 将新属性设置回fd文件描述符 */
    fcntl(fd, F_SETFL, flags);

    flags = fcntl(0, F_GETFL);
    flags |= O_NONBLOCK;
    fcntl(0, F_SETFL, flags);

    while (1)
    {
        memset(tmpbuff, 0, sizeof(tmpbuff));
        nsize = read(fd, tmpbuff, sizeof(tmpbuff));
        if (nsize > 0)
        {
            printf("FIFO:%s\n", tmpbuff);
        }
        
        memset(tmpbuff, 0, sizeof(tmpbuff));
        pret = gets(tmpbuff);
        if (NULL != pret)
        {
            printf("STDIN:%s\n", tmpbuff);
        }  
    }
    
    close(fd);

    return 0;
}

3.异步IO
    将一个文件描述符设定为异步IO,当IO有事件发生时,内核会向用户层发送SIGIO信号提醒用户层处理事件 
 signal(SIGIO,handle);

flag = fcntl(fd,F_GETFL);   

flag |= O_ASYNC;//将fd设置为异步IO(文件描述符,发生可读事件时,会发送信号通知)
fcntl(fd,F_SETFL,flag);//把异步IO事件的接收进程设置为当前进程,通知给当前进程
fcntl(fd,F_SETOWN,getpid());

#include "head.h"

int fd = 0;

void handler(int signo)
{
    char tmpbuff[4096] = {0};

    memset(tmpbuff, 0, sizeof(tmpbuff));
    read(fd, tmpbuff, sizeof(tmpbuff));
    printf("RECV:%s\n", tmpbuff);

    return;
}

int main(void)
{
    char tmpbuff[4096] = {0};
    int flags;

    signal(SIGIO, handler);

    mkfifo("/tmp/myfifo", 0777);
    fd = open("/tmp/myfifo", O_RDONLY);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

    flags = fcntl(fd, F_GETFL);
    flags |= O_ASYNC;
    //将fd设置为异步IO(文件描述符发生可以读的事件,会发送信号通知)
    fcntl(fd, F_SETFL, flags);
    //通知给当前进程
    fcntl(fd, F_SETOWN, getpid());

    while (1)
    {
        memset(tmpbuff, 0, sizeof(tmpbuff));
        gets(tmpbuff);
        printf("STDIN:%s\n", tmpbuff);
    }
    
    close(fd);

    return 0;
}

4.多路复用IO
   select
        监听文件描述符集合,将所有要监听的事件加入集合中,使用select监听所有事件,当集合中有事件发生,  select不再阻塞,同时select会将产生事件的文件描述符留在集合中,而把没有产生事件的文件描述符从集合中踢出,所以留在集合中的文件描述即为产生事件的文件描述符,对其处理即可

注意:因为select会把没有产生事件的文件描述符从集合中踢出,所以要用一个临时变量tmp来使用select

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
          功能:
            监听文件描述符是否有事件发生
          参数:
            nfds:最大文件描述符的值 + 1 
            readfds:读文件描述符集合
            writefds:写文件描述符集合
            exceptfds:异常文件描述符集合
            timeout:超时时间
          返回值:
            成功返回产生事件的文件描述符个数
            失败返回-1 

void FD_CLR(int fd, fd_set *set);
        功能:将fd从集合中清除

int  FD_ISSET(int fd, fd_set *set);
        功能:判断fd是否仍在文件描述符集合中
void FD_SET(int fd, fd_set *set);
        功能:将fd加入文件描述符集合中
void FD_ZERO(fd_set *set);
        功能:将文件描述符集合清0  

练习:

#include "head.h"

int CreateTcpConnection(const char *pip, int port)
{
    int sockfd = 0;
    int ret = 0;
    struct sockaddr_in seraddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(port);
    seraddr.sin_addr.s_addr = inet_addr(SER_IP);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        return -1;
    }

    ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if (-1 == ret)
    {
        return -1;
    }

    return sockfd;
}

int HandleConnection(int sockfd)
{
    char tmpbuff[4096] = {0};
    static int cnt = 0;
    ssize_t nsize = 0;

    sprintf(tmpbuff, "hello world --- %d", cnt);
    nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);
    if (-1 == nsize)
    {
        return -1;
    }
    cnt++;

    memset(tmpbuff, 0, sizeof(tmpbuff));
    nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);  
    if (-1 == nsize)
    {
        return -1;
    }
    else if (0 == nsize)
    {
        return 0;
    }
    
    printf("RECV:%s\n", tmpbuff);

    return nsize;
}

int main(void)
{
    int sockfd = 0;
    int ret = 0;

    sockfd = CreateTcpConnection(SER_IP, SER_PORT);
    if (-1 == sockfd)
    {
        printf("连接服务器异常\n");
        return -1;
    }

    while (1)
    {
        ret = HandleConnection(sockfd);
        if (-1 == ret)
        {
            printf("连接出错!\n");
            break;
        }
        else if (0 == ret)
        {
            printf("连接关闭\n");
            break;
        }

        sleep(1);
    }
    
    close(sockfd);

    return 0;
}

 server.c

#include "head.h"

int CreateListenSocket(const char *pip, int port)
{
    int sockfd = 0;
    int ret = 0;
    struct sockaddr_in seraddr;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        return -1;
    }

    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(port);
    seraddr.sin_addr.s_addr = inet_addr(pip);
    ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if (-1 == ret)
    {
        return -1;
    }

    ret = listen(sockfd, 10);
    if (-1 == ret)
    {
        return -1;
    }

    return sockfd;
}

int HandleConnection(int confd)
{
    char tmpbuff[4096] = {0};
    ssize_t nsize = 0;

    nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
    if (-1 == nsize)
    {
        return -1;
    }
    else if (0 == nsize)
    {
        return 0;
    }

    printf("RECV:%s\n", tmpbuff);

    sprintf(tmpbuff, "%s --- echo", tmpbuff);

    nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
    if (-1 == nsize)
    {
        return -1;
    }

    return nsize;
}

int main(void)
{
    int sockfd = 0;
    int confd = 0;
    int ret = 0;
    fd_set rdfds;
    fd_set tmpfds;
    int nready = 0;
    int maxfd = 0;
    int i = 0;

    //创建监听套接字
    sockfd = CreateListenSocket(SER_IP, SER_PORT);
    if (-1 == sockfd)
    {
        printf("创建监听套接字失败\n");
        return -1;
    }

    //将sockfd加入监听集合中
    FD_ZERO(&rdfds);
    FD_SET(sockfd, &rdfds);
    maxfd = sockfd;

    while (1)
    {
        //开始监听
        tmpfds = rdfds;
        nready = select(maxfd+1, &tmpfds, NULL, NULL, NULL);
        if (-1 == nready)
        {
            perror("fail to select");
            return -1;
        }

        //如果sockfd产生事件,处理新的连接请求,并将新的文件描述符加入集合,下次一起监听
        if (FD_ISSET(sockfd, &tmpfds))
        {
            confd = accept(sockfd, NULL, NULL);
            if (-1 == confd)
            {
                FD_CLR(sockfd, &rdfds);
                close(sockfd);
                continue;
            }   

            maxfd = maxfd > confd ? maxfd : confd;
            FD_SET(confd, &rdfds);
        }

        //遍历所有已经连接的客户端中是否有事件发生
        for (i = sockfd+1; i <= maxfd; i++)
        {
            if (FD_ISSET(i, &tmpfds))
            {
                ret = HandleConnection(i);
                if (-1 == ret)
                {
                    printf("连接异常\n");
                    FD_CLR(i, &rdfds);
                    close(i);
                    continue;
                }
                else if (0 == ret)
                {
                    printf("连接断开\n");
                    FD_CLR(i, &rdfds);
                    close(i);
                    continue;
                }
            }
        }
    }
    
    close(sockfd);

    return 0;
}

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值