栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Linux&C语言简单使用IO多路复用(select)实现TCP并发服务器-传输层

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux&C语言简单使用IO多路复用(select)实现TCP并发服务器-传输层

目录
  • IO多路复用实现TCP并发服务器-select
    • 前言
    • 原理
    • 代码实现
      • 服务器—01server.c
      • 客户端---02client.c
    • 执行结果
  • 6.非原创

IO多路复用实现TCP并发服务器-select 前言

服务器模型分为两种,循环服务器、并发服务器。

循环服务器: 服务器在同一时刻只能处理同一个客户端的请求。
并发服务器: 服务器在同一时刻能处理多个客户端的请求。

TCP服务器默认的就是一个循环服务器,因为内部有两个阻塞的函数, accept recv 会相互影响
UDP服务器默认的就是一个并发服务器,因为只有一个阻塞的函数 recvfrom

原理

TCP服务器有两个阻塞的函数,recv 和 accept ,相互之间会有影响,使用 io多路复用可是实现同时处理多个IO,也就是服务器能支持并发。

代码实现 服务器—01server.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define ERRLOG(errmsg)                                       
    do                                                       
    {                                                        
        printf("%s--%s(%d):", __FILE__, __func__, __LINE__); 
        perror(errmsg);                                      
        exit(-1);                                            
    } while (0)

//创建套接字-填充服务器网络信息结构体-绑定-监听
int socket_bind_listen(const char *argv[]);

int main(int argc, const char *argv[])
{
    //检测命令行参数个数
    if (3 != argc)
    {
        printf("Usage : %s  n", argv[0]);
        exit(-1);
    }
    //创建套接字-填充服务器网络信息结构体-绑定-监听
    int sockfd = socket_bind_listen(argv);
    
    //创建文件描述符表
    fd_set readfds;//母本
    FD_ZERO(&readfds);//清空
    //每次给select使用,因为select会擦除掉一部分
    fd_set readfds_temp; //用来备份原集合的
    FD_ZERO(&readfds_temp);//清空
    //记录表中最大的文件描述符
    int max_fd = 0;
    //将要监视的文件描述符放到表中
    FD_SET(sockfd, &readfds);
    max_fd = (max_fd > sockfd ? max_fd : sockfd);//记录最大文件描述符

    int ret,ret1= 0;
    int accept_fd;
    char buff[128] = {0};
    int i;

    while (1)
    {
        // select 每次返回后 会将没有就绪的文件描述符擦除,所以
        //每次调用都需要重置这个集合
        readfds_temp = readfds;
        ret= select(max_fd + 1, &readfds_temp, NULL, NULL, NULL);
        if (ret == -1)
            ERRLOG("select error");

        //说明有文件描述符准备就绪
		// && ret > 0 是为了提高效率:
        //select返回几个就绪的,就只处理几个就绪的就行了
		//对于其他没有就绪的文件描述符 无需判断处理    
        for (i = 0; i < max_fd + 1&& ret > 0; i++)
        {
            if (FD_ISSET(i, &readfds_temp))//存在
            {//说明i就绪了
                if (i == sockfd)
                {
                    //阻塞等待客户端连接--一旦有客户端连接就会解除阻塞
                    accept_fd = accept(sockfd, NULL, NULL);
                    if (-1 == accept_fd)
                        ERRLOG("accept error");
            
                    printf("客户端 [%d] 连接了n", accept_fd);
                    //将acceptfd也加入到要监视的集合中
                    FD_SET(accept_fd, &readfds);
                    //记录最大文件描述符
                    max_fd = (max_fd > accept_fd ? max_fd : accept_fd);
                }
                else
                {//说明有客户端发来消息了
                    accept_fd = i;
                    //接收客户端发来的数据
                    if (0 > (ret1 = recv(accept_fd, buff,sizeof(buff), 0)))
                    {
                        perror("recv error");
                        break;
                    }
                    else if (0 == ret1)//客户端CTRL+C
                    {
                        printf("客户端 [%d] 断开连接n",accept_fd);
                        //将该客户端在集合中删除
                        FD_CLR(accept_fd, &readfds); 
                        //关闭该客户端的套接字
                        close(i);
						continue;//结束本层本次循环
                    }
                    else
                    {
                        if (0 == strcmp(buff, "quit"))
                        {
                            printf("客户端 [%d] 退出了n",accept_fd);
                            //将该客户端在集合中删除
                            FD_CLR(accept_fd, &readfds); 
                            //关闭该客户端的套节字
                            close(i);
							continue;//结束本层本次循环
                        }

                        printf("客户端 [%d] 发来数据:[%s]n", i, buff);
                        //组装回复给客户端的应答
                        strcat(buff, "---996");
                        //回复应答
                        if (0 > (ret1= send(accept_fd, buff,sizeof(buff), 0)))
                        {
                            perror("send error");
                            break;
                        }
                    }
                }
                ret--;
            }
        }
    }
    //关闭监听套接字  一般不关闭
    close(sockfd);
    return 0;
}

//创建套接字-填充服务器网络信息结构体-绑定-监听
int socket_bind_listen(const char *argv[])
{
    // 1.创建套接字      //IPV4   //TCP
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
        ERRLOG("socket error");


    // 2.填充服务器网络信息结构体
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));//清空
    server_addr.sin_family = AF_INET;// IPV4
    //端口号  填 8888 9999 6789 ...都可以
    // atoi字符串转换成整型数
    //htons将无符号2字节整型  主机-->网络
    server_addr.sin_port = htons(atoi(argv[2]));
    // ip地址 要么是当前Ubuntu主机的IP地址 或者
    //如果本地测试的化  使用  127.0.0.1 也可以
    //inet_addr字符串转换成32位的网络字节序二进制值
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);

    //结构体长度
    socklen_t server_addr_len = sizeof(server_addr);

    // 3.将套接字和网络信息结构体绑定
    if (-1 == bind(sockfd, (struct sockaddr *)&server_addr, server_addr_len))
        ERRLOG("bind error");
    

    //将套接字设置成被动监听状态
    if (-1 == listen(sockfd, 10))
        ERRLOG("listen error");
    
    return sockfd;
}
客户端—02client.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define ERRLOG(errmsg) do{
		printf("%s--%s(%d):", __FILE__, __func__, __LINE__);
		perror(errmsg);
		exit(-1);
	}while(0)

int main(int argc, const char *argv[])
{
	//检测命令行参数个数
	if(3 != argc){
		printf("Usage : %s  n", argv[0]);
		exit(-1);
	}

	//1.创建套接字
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(-1 == sockfd)
		ERRLOG("socket error");
	

	//2.填充服务器网络信息结构体
	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	//端口号  填 8888 9999 6789 ...都可以
	server_addr.sin_port = htons(atoi(argv[2]));
	//ip地址 要么是当前Ubuntu主机的IP地址 或者
	//如果本地测试的化  使用  127.0.0.1 也可以
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);

	//结构体长度
	socklen_t server_addr_len = sizeof(server_addr);

	//与服务器建立连接
	if(-1 == connect(sockfd, (struct sockaddr *)&server_addr, server_addr_len))
		ERRLOG("connect error");
	
	printf("---连接服务器成功---n");
	char buff[128] = {0};
	while(1){
		scanf("%s", buff);
		int ret = 0;
		if(0 > (ret = send(sockfd, buff,sizeof(buff), 0)))
			ERRLOG("send error");
		
		if(0 == strcmp(buff, "quit"))
			break;
		
		if(0 > (ret = recv(sockfd, buff,sizeof(buff), 0)))
			ERRLOG("recv error");
		
		printf("收到服务器回复:[%s]n", buff);
	}
	//关闭套接字
	close(sockfd);

	return 0;
}
执行结果

6.非原创
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/925506.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号