TCP并发服务器

1.阻塞IO

CPU占用率低,等待资源时将任务挂起,不占用CPU资源,等到拿到资源后继续向下执行

2.非阻塞IO

能够让任务不阻塞,效率低,因为没有数据时,CPU一直空转

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

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

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

cpp 复制代码
#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());

cpp 复制代码
#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 |

练习:

cpp 复制代码
#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

cpp 复制代码
#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;
}
相关推荐
无敌小茶3 分钟前
Linux学习笔记之环境变量
linux·笔记
Harbor Lau17 分钟前
Linux常用中间件命令大全
linux·运维·中间件
漫谈网络35 分钟前
基于 Netmiko 的网络设备自动化操作
运维·自动化·netdevops·netmiko
꧁坚持很酷꧂1 小时前
Linux Ubuntu18.04下安装Qt Craeator 5.12.9(图文详解)
linux·运维·qt
凉、介1 小时前
PCI 总线学习笔记(五)
android·linux·笔记·学习·pcie·pci
电鱼智能的电小鱼2 小时前
EFISH-SBC-RK3588无人机地面基准站项目
linux·网络·嵌入式硬件·机器人·无人机·边缘计算
电鱼智能的电小鱼2 小时前
基于 EFISH-SBC-RK3588 的无人机环境感知与数据采集方案
linux·网络·嵌入式硬件·数码相机·无人机·边缘计算
小诸葛的博客3 小时前
详解Linux中的定时任务管理工具crond
linux·运维·chrome
一默19913 小时前
CentOS 7.9升级OpenSSH到9.9p2
linux·运维·centos
keep intensify3 小时前
Linux常用指令
linux·服务器·php