使用Linux系统IO多路复用中eopll创建基于TCP通信协议的多人聊天室

这篇具有很好参考价值的文章主要介绍了使用Linux系统IO多路复用中eopll创建基于TCP通信协议的多人聊天室。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.1.搭建好TCP的通信模型 2.创建红黑树根节点 3.将套接字事件添加到红黑树中,使其被监听 4.当套接字事件发生,表示有客户端连接,将连接事件加入到红黑树节点当中 5.每当连接事件发生时,表示客户端发送信息到服务器 6.每当有事件准备就绪时,将对应的红黑树节点信息放入到构建的events数组中 7.通过节点信息判断对应事件并处理,实现IO多路复用。

更多详细信息在代码注释

1.服务器代码

#include "server.h"
// TCP协议的多人聊天室服务器,创建服务器后使用epoll实现IO多路复用,分别进行客户端的连接,信息的接受
int main(int argc, char const *argv[])
{
    // 一.创建套接字1.AF_INET默认为ip(7)协议 2.SOCK_STREAM默认为TCP
    // 3.0表示使用type对应的默认协议
    int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_fd < 0)
    {
        ERR_MSG("socket");
        goto OUT1;
    }
    // 设置允许端口快速被重用
    int resue = 1;
    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &resue, sizeof(resue)) < 0)
    {
        ERR_MSG("setsockopt");
        return -1;
    }
    printf("socket success _%d_\n", __LINE__);
    // 填充服务器的信息结构体
    my_ser.sin_family = AF_INET;            // IPv4协议指向填充
    my_ser.sin_port = htons(PORT);          // 将端口转换成网络字节
    my_ser.sin_addr.s_addr = inet_addr(IP); // IP地址转换成网络字节序

    // 三.绑定
    if (bind(sock_fd, (struct sockaddr *)&my_ser, sizeof(my_ser)) < 0)
    {
        ERR_MSG("bind");
        goto OUT2;
    }
    printf("bind success _%d_\n", __LINE__);

    // 将套接字设置为监听状态
    if (listen(sock_fd, 128) < 0)
    {
        ERR_MSG("listen");
        goto OUT2;
    }
    printf("listen success _%d_\n", __LINE__);

    // 创建一个epoll句柄/红黑树根节点
    int epfd = epoll_create(10);
    if (epfd < 0)
    {
        printf("epoll_create on_success _%d_", __LINE__);
        goto OUT2;
    }
    // 将套接字的信息存入到event结构体中,为事件放入红黑树中做准备
    event.events = EPOLLIN; // 关注可读事件,套接字有数据可读时触发
    event.data.fd = sock_fd;
    // 将event存放到套接字信息放入到红黑树中
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, sock_fd, &event) < 0)
    {
        printf("epoll_ctl on_success _%d_", __LINE__);
        goto OUT2;
    }

    int newfd;               // 客户端连接
    int minfd = -1, key = 1; // 保存最小的newfd,用于循环向整个服务器发送信息
    // 循环监听是否有客户端连接
    addrlen = sizeof(my_cin);
    while (1)
    {
        // ret返回就绪事件的个数,并将就绪的事件放入到
        // events这个结构体中,参3表示最多放入10个事件,
        // 参4的-1表示不关心是否超时
        int ret = epoll_wait(epfd, events, 10, -1);
        printf("ret=%d\n", ret);
        if (ret < 0)
        {
            printf("epoll_wait on_success");
            goto OUT2;
        }
        /*1.走到这里表示有事件发生,等待处理,循环判断每一个事件*/
        for (int i = 1; i <= ret; i++)
        {
            if (events[i - 1].data.fd == sock_fd) // 表示有客户端连接服务器
            {
                printf("触发客户端连接事件%d,i=%d\n", __LINE__, i);
                newfd = accept(sock_fd, (struct sockaddr *)&my_cin, &addrlen);
                if (newfd < 0)
                {
                    ERR_MSG("accept");
                    return -1;
                }
                if (key == 1)
                {
                    minfd = newfd;
                    key--;
                }
                printf("newfd=%d 客户端连接成功\n", newfd);

                // 客户端连接成功后,将事件添加到红黑树中
                event.events = EPOLLIN; // 关注可读事件,套接字有数据可读时触发
                event.data.fd = newfd;
                // 添加到红黑树中
                if (epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &event) < 0)
                {
                    printf("epoll_ctl on_success _%d_", __LINE__);
                    goto OUT3;
                }
            }
            // 因为红黑树中只存放了sock_fd和newfd两种文件描述符,因此当不是客户端连接事件
            // 就一定是客户端发送数据到服务器,将客户端发送来的消息广播都所有客户端
            else
            {
                printf("触发客户端交互事件%d,i=%d,newfd=%d\n", __LINE__, i, events[i-1].data.fd);
                memset(buf, '\0', sizeof(buf)); // 清空消息队列
                // 接收客户端发送来的消息(参1:对应客户端,参4:阻塞等待数据)
                int res = recv(events[i - 1].data.fd, buf, sizeof(buf), 0);
                if (res < 0)
                {
                    ERR_MSG("recv");
                    goto OUT3;
                }
                else if (res == 0)
                {
                    printf("客户端%d已经关闭%d\n", events[i - 1].data.fd, __LINE__);
                    goto OUT3;
                }
                else // 想所有客户端展示信息
                {
                    sprintf(buf_ID, "%s%d%s%s", "客户端_",events[i-1].data.fd, ":", buf);
                    for (int i = minfd; i <= newfd; i++) // 循环向所有客户端发送信息
                    {   
                        printf("midfd=%d,newfd=%d\n",minfd,newfd);
                        if (send(i, buf_ID, strlen(buf_ID)+1, 0) < 0)
                        {
                            ERR_MSG("send");
                            goto OUT3;
                        }
                    }
                    printf("%s\n", buf_ID); // 展示到终端
                }
            }
        }
    }
    return 0;

// 注销等级列
OUT3:
    for (int i = minfd; i <= newfd; i++)
    {
        close(i);
    }
OUT2:
    close(sock_fd);
OUT1:
    return sock_fd;
}

2.客户端代码

正常搭建客户端TCP模型

#include "client.h"
int main(int argc, char const *argv[])
{
    // 创建套接字
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if (cfd < 0)
    {
        ERR_MSG("socket");
        goto OUT1;
    }
    // 连接服务器
    // 1.填充服务器的信息
    my_ser.sin_family = AF_INET;
    my_ser.sin_port = htons(PORT);
    my_ser.sin_addr.s_addr = inet_addr(IP);
    // 连接
    if (connect(cfd, (struct sockaddr *)&my_ser, sizeof(my_ser)) < 0)
    {
        ERR_MSG("connet");
        goto OUT2;
    }
    printf("connect server success\n");

    while (1)
    {
        memset(cli_buf,'0',sizeof(cli_buf));
        printf("输入你要发送的信息:");
        scanf("%s",cli_buf);
        while(getchar()!=10);//清除缓存区的残留字符
        
        // 发送信息给服务器
        if(send(cfd,cli_buf,sizeof(cli_buf),0)<0)
        {
            ERR_MSG("send");
            goto OUT2;
        }
        // 接收服务器的信息,并展示到终端
        memset(cli_buf,'0',sizeof(cli_buf));
        int res=-1;
        res=recv(cfd,cli_buf,sizeof(cli_buf),0);
        if(res<0)//接收失败
        {
            ERR_MSG("recv");
            goto OUT2;
        }
        else if(0==res)//服务器关闭
        {
            printf("服务器关闭\n");
            goto OUT2;
        }
        //展示到终端
        printf("%s\n",cli_buf);

    }
    // 关闭套接字
    close(cfd);
    return 0;
OUT2:
    close(cfd);
OUT1:
    return cfd;
}

!!!文章来源地址https://www.toymoban.com/news/detail-546817.html

到了这里,关于使用Linux系统IO多路复用中eopll创建基于TCP通信协议的多人聊天室的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux多路IO复用技术——epoll详解与一对多服务器实现

    本文详细介绍了Linux中epoll模型的优化原理和使用方法,以及如何利用epoll模型实现简易的一对多服务器。通过对epoll模型的优化和相关接口的解释,帮助读者理解epoll模型的工作原理和优缺点,同时附带代码实现和图解说明。

    2024年02月05日
    浏览(43)
  • 02-Linux-IO多路复用之select、poll和epoll详解

    前言: 在linux系统中,实际上所有的 I/O 设备都被抽象为了文件这个概念,一切皆文件,磁盘、网络数据、终端,甚至进程间通信工具管道 pipe 等都被当做文件对待。 在了解多路复用 select、poll、epoll 实现之前,我们先简单回忆复习以下两个概念: 一、什么是多路复用: 多路

    2024年02月10日
    浏览(57)
  • 【Linux网络编程】TCP并发服务器的实现(IO多路复用select)

    服务器模型主要分为两种, 循环服务器 和 并发服务器 。 循环服务器 : 在同一时间只能处理一个客户端的请求。 并发服务器 : 在同一时间内能同时处理多个客户端的请求。 TCP的服务器默认的就是一个循环服务器,原因是有两个阻塞 accept函数 和recv函数 之间会相互影响。

    2024年02月03日
    浏览(82)
  • 使用IO多路复用select完成TCP循环服务器接收客户端消息并打印

    服务器       客户端     结果    

    2024年02月12日
    浏览(52)
  • IO、NIO、IO多路复用

    IO是什么? 网络IO是如何连接的? 下面是一次网络读取内容的I/O示意图,数据先从外设(网卡)到内核空间,再到用户空间(JVM),最后到应用程序的一个过程。 上述一次I/O读取,所谓的阻塞和非阻塞体现在哪里呢? Java最早期的版本的I/O就是这样实现的。当程序调用到读取

    2024年01月20日
    浏览(42)
  • 说说IO多路复用

    IO多路复用 I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O流。直白点说:多路指的是多个socket连接,复用指的是复用一个线程进行管理。发明它的原因,是尽量多的提高服务

    2024年02月09日
    浏览(39)
  • IO多路复用练习

    一、通过select搭建TCP服务器 二、通过select搭建TCP客户端 三、通过poll搭建客户端

    2024年02月12日
    浏览(33)
  • IO多路复用详解

    在IO多路复用模型中,引入了一种新的系统调用,查询IO的就绪状态。在Linux系统中,对应的系统调用为select/poll/epoll系统调用。通过该系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核能够将就绪的状态返回给应用程序

    2024年02月08日
    浏览(50)
  • 【TCP服务器的演变过程】使用IO多路复用器epoll实现TCP服务器

    手把手教你从0开始编写TCP服务器程序,体验开局一块砖,大厦全靠垒。 为了避免篇幅过长使读者感到乏味,对【TCP服务器的开发】进行分阶段实现,一步步进行优化升级。 本节,在上一章节的基础上,将IO多路复用机制select改为更高效的IO多路复用机制epoll,使用epoll管理每

    2024年01月17日
    浏览(70)
  • 网络模型与 IO 多路复用

      socket也称作“套接字”,用于描述IP地址和端口,是一个通信链路的描述符。应用程序通常通过“套接字”向对端发出请求或者应答网络请求。   socket是连接运行在网络上的两个程序之间的通信端点。通信的两端都有socket,它是一个通道,数据在两个socket之间进行传输

    2024年02月01日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包