当前位置: 首页 > article >正文

多路复用——poll

         在之前我们说过TCP的模型,用户可以通过连接服务器从而通过服务器去加载服务端对应功能的文件来达到通信,但是我们知道,一个用户加载文件需要一个进程,在Linux中,一个进程就要占用4G的虚拟内存,如果在并发量高的情况下,很明显服务器是不堪重负的。下面来讲解决方案即多路复用技术。

        多路复用,举个例子就好比把多条狭窄的乡间小路修成一条双向16车道的快速路,即在这条大路上去进行网络通信。多路复用技术的核心就在于在单个网络信道上传输多个网络信号,能能大幅提高通信效率,并且能够很大程度上降低服务器的负担。

下面我们用TCP作为通信模型来看多路复用的第二种 I/O 机制,即poll:

服务端如下:

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

int add_fds(struct pollfd *fds, int nfds, int fd, short events)
{
    for (int i = 0; i < nfds; i++)
    {
        if(0==fds[i].fd)
        {
            fds[i].fd=fd;
            fds[i].events=events;
            return 0;
        }
    }
    return -1;
}

//tcp server 只有server端能用多路复用
int main(int argc, char *argv[])
{
    //创建socket
    int svr_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (svr_fd < 0)
     {
        perror("socket");
        return -1;
    }
    //准备通信地址
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    socklen_t addrlen=sizeof(addr);
    //绑定地址
    if (bind(svr_fd, (struct sockaddr*)&addr, addrlen))
    {
        perror("bind");
        return -1;
    }
    //监听连接
    if (listen(svr_fd, 10))
    {
        perror("listen");
        return -1;
    }
    //创建pollfd数组并初始化
    struct pollfd *fds=calloc(sizeof(struct pollfd), 10);
    //设置[0]位置要监听的描述符和事件
    fds[0].fd=svr_fd;
    fds[0].events=POLLIN;
    //定义超时时间
    int timeout=10000;

    char buf[1024];
    size_t buf_size=sizeof(buf);

    while (1)
    {
        int ret=poll(fds, 10, timeout);
        if (ret == 0) continue; //超时
        if (ret < 0)
        {
            perror("poll");
            return -1;
        }
        //判断[0]位置是否有读事件产生
        if (fds[0].revents & POLLIN)
        {
            //接受连接
            int cli_fd=accept(svr_fd, (struct sockaddr*)&addr, &addrlen);
            if(cli_fd>0)
            {
                //把客户端的socket描述符添加到pollfd数组的空位置,并设置事件
                if(add_fds(fds, 10, cli_fd, POLLIN))
                {
                    printf("客户端数量已满\n");
                }
            }
        }
        else
        {
            //遍历pollfd数组,判断其它位置是否有读事件产生
            for (int i = 1; i < 10; i++)
            {
                if (!fds[i].revents & POLLIN) continue;
                int ret=recv(fds[i].fd, buf, buf_size, 0);
                if (ret <= 0||0==strcmp(buf, "quit"))
                {
                    printf("客户端 %d 退出\n", fds[i].fd);
                    fds[i].fd=0;
                    fds[i].events=0;
                    continue;
                }
                printf("recv:%s bits:%d\n", buf, ret);
                strcat(buf, ":return");
                ret=send(fds[i].fd, buf, strlen(buf)+1, 0);
                if (ret <= 0)
                {
                    printf("客户端 %d 退出\n", fds[i].fd);
                    fds[i].fd=0;
                    fds[i].events=0;
                    continue;
                }
            }
        }
    }

	return 0;
}

客户端如下:

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


typedef struct sockaddr *SP;

int main(int argc,const char* argv[])
{
	//创建socket
	int cli_fd=socket(AF_INET,SOCK_STREAM,0);
	if(cli_fd<0)
	{
		perror("socket");
		return -1;
	}
	//准备通信地址
	struct sockaddr_in addr={};
	addr.sin_family=AF_INET;
	addr.sin_port=htons(8888);
	addr.sin_addr.s_addr=inet_addr("127.0.0.1");
	socklen_t addrlen=sizeof(addr);
	//连接服务器
	if(connect(cli_fd,(SP)&addr,addrlen))
	{
		perror("connect");
		return -1;
	}

	char buf[4096];
	size_t buf_size=sizeof(buf);
	while(1)
	{
		//发送请求
		printf(">>>>>");
		scanf("%s",buf);
		int ret=send(cli_fd,buf,strlen(buf)+1,0);
		//ret=write(cli_fd,buf,strlen(buf)+1);
		if(ret<=0)
		{
			printf("服务器正在升级,请稍后重试\n");
			break;
		}
		if(0==strcmp("quit",buf))
		{
			printf("通信结束\n");
			break;
		}
		//接收请求
		//int ret=read(cli_fd,buf,buf_size);
		ret=recv(cli_fd,buf,buf_size,0);
		if(ret<=0)
		{
			printf("服务器正在维护,请稍候重试\n");
			break;
		}
		printf("read:%s bits:%d\n",buf,ret);
	}
		
	return 0;
}

over


http://www.kler.cn/news/340759.html

相关文章:

  • 了解Java的基础
  • 单细胞转录组 —— STARsolo 原始数据处理
  • L0-Linux-关卡材料提交
  • 初始Linux(二)基础命令
  • 多路复用——select
  • 【CUDA】【PyTorch】安装 PyTorch 与 CUDA 11.7 的详细步骤
  • ECCV24高分论文:MVSplat稀疏视图下的高效的前馈三维重建模型
  • 每日OJ题_牛客_mari和shiny_线性dp_C++_Java
  • 前端提升方向
  • [Python] 函数入参中的 *args和**kwargs 是什么意思
  • 【C语言】指针
  • GO网络编程(三):海量用户通信系统1:登录功能初步
  • C语言-进程控制编程
  • Java 根据字符生成背景透明的图片
  • 栈的操作算法实现(数据结构)
  • yolov11人物背景扣除
  • 离线服务器上复现G3SR论文实验
  • 如何基于vite实现清除特定环境下的console和debugger
  • map和set(c++)
  • 【vue2.7.16系列】手把手教你搭建后台系统__封装工具库(4)