网络编程 IO多路复用 [epoll版] (TCP网络聊天室)

这篇具有很好参考价值的文章主要介绍了网络编程 IO多路复用 [epoll版] (TCP网络聊天室)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

//head.h            头文件

//TcpGrpSer.c     服务器端

//TcpGrpUsr.c     客户端

通过IO多路复用实现服务器在单进程单线程下可以与多个客户端交互

 API

epoll函数

#include<sys/epoll.h>
int epoll_create(int size);
功能:创建一个epoll句柄//创建红黑树根节点
epoll把要监测的事件文件描述符挂载到红黑树上
参数:size 没有意义,但是必须>0
返回值:成功返回根节点对应的文件描述符,失败返回-1

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:实现对于epoll的控制
参数:
epfd:epoll_create创建的句柄
op:控制方式
     EPOLL_CTL_ADD:添加要监测的事件文件描述符
     EPOLL_CTL_MOD:修改epoll检测的事件类型
     EPOLL_CTL_DEL:将文件描述符从epoll删除
fd:要操作的文件描述符
event:事件结构体
typedef union epoll_data {
               void        *ptr;
               int          fd;//使用这个
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;

 struct epoll_event {
               uint32_t     events; //EPOLLIN(读) EPOLLOUT(写)
               epoll_data_t data;        /* User data variable */
           };
返回值:成功返回0,失败返回-1

int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);
功能:阻塞等待准备好的文件描述符
参数:
epfd:epoll句柄
events:存放就绪事件描述符的结构体数组首地址
maxevents:监听的事件最大个数
timeout:超时检测
    >0:毫秒级检测
    ==0:立即返回
    -1:不关心是否超时

返回值:
>0:准备好的文件描述符的个数
==0:超时
<0:失败

 head.h

#ifndef __HEAD_H__
#define __HEAD_H__


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<math.h>
#include<errno.h>
#include<fcntl.h>
#include<signal.h>

#include<sys/stat.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/shm.h>
#include<sys/time.h>
#include<sys/sem.h>

#include<pthread.h>
#include<semaphore.h>

#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<poll.h>
#include<sys/epoll.h>
#include<sys/fcntl.h>


#define NUM 10
#define ERR_MSG(msg)                    \
	do                                  \
	{                                   \
		printf("line: %d\n", __LINE__); \
		perror(msg);                    \
	} while (0)

#define PORT 6666			// 端口号的网络字节序  1024~49151
#define IP "192.168.250.100" // ifconfig查看本机IP  (ipv4)

#endif

TcpGrpSer.c

#include "head.h"



int main(int argc, const char *argv[])
{
	// 创建流式套接字
	int sfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}

	// 填充服务器自身的地址信息结构体
	// 真实的地址信息结构体根据地址族制定AF_INET ;
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;			 // 必须填充AF_INET
	sin.sin_port = htons(PORT);			 // 端口号的网络字节序  1024~49151
	sin.sin_addr.s_addr = inet_addr(IP); // ifconfig查看本机IP

	int reuse = 1;
	if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) // 允许端口快速被重复使用
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	// 绑定连接
	if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success\n");

	// 设置监听
	if (listen(sfd, 128) < 0)
	{
		ERR_MSG("listen");
		return -1;
	}

	struct epoll_event event;
	struct epoll_event events[10]; // 存放就绪事件描述符的数组
	char buf[128] = {0};

	// 创建epoll句柄
	int epfd = epoll_create(1);
	if (epfd < 0)
	{
		ERR_MSG("epoll_create");
		exit(-1);
	}

	// 添加准备就绪事件进入epoll;
	event.events = EPOLLIN; // 读事件
	event.data.fd = sfd;	// 监听套接字放入列表
	if (epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &event) < 0)
	{
		ERR_MSG("epoll_ctl");
		exit(-1);
	}
	event.events = EPOLLIN; // 读事件
	event.data.fd = 0;		// 终端输入套接字放入列表
	if (epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &event) < 0)
	{
		ERR_MSG("epoll_ctl");
		exit(-1);
	}
	// 监听事件是否发生

	int s_res = 0;
	struct sockaddr_in cin;
	socklen_t len = sizeof(cin);
	struct sockaddr_in savcin[1024];
	int flags[1024] = {0};
	int newfd = -1;
	ssize_t res = 0;

	while (1)
	{
		// 监测文件描述符是否准备就绪
		// 如果成功,s_res接收返回的事件个数,就绪的事件存储到events数组中
		s_res = epoll_wait(epfd, events, 10, -1);
		if (s_res < 0)
		{
			ERR_MSG("select");
			return -1;
		}

		// 与客户端通信
		for (int i = 0; i < s_res; i++)
		{
			if (events[i].events & EPOLLIN)
			{
				if (events[i].data.fd == sfd)
				{
					printf("客户端连技事件\n");
					newfd = accept(sfd, (struct sockaddr *)&cin, &len);
					if (newfd < 0)
					{
						perror("accept");
						return -1;
					}
					savcin[newfd] = cin;
					flags[newfd] = 1;
					printf("[%s:%d] 客户端连接成功 newfd = %d __%d__ \n",
						   inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __LINE__);

					event.events = EPOLLIN | EPOLLET; // 读事件
					event.data.fd = newfd;	// 新套接字放入链表
					if (epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &event) < 0)
					{
						ERR_MSG("epoll_ctl");
						exit(-1);
					}
				}

				else if (0 == events[i].data.fd)
				{
					printf("触发键盘输入事件\n");
				int sndfd=-1;
				res=scanf("%d %s",&sndfd,buf);
				while(getchar() !=10);
				if(res!=2)
				{
					printf("请输入正确数据格式:[fd(4~1023)] string\n");
		
					continue;
				}
				//判断文件是否合法
				if(flags[i])
				{
					printf("sndfd = %d 是非法文件描述符\n",sndfd);
					continue;
				}
				if(send(sndfd,buf,sizeof(buf),0)<0)
				{
					ERR_MSG("send");
				}
				bzero(buf,sizeof(buf));
				}
				else
				{

					printf("客户端交互事件\n");
					// res = scanf("%s", buf);
					// while (getchar() != 10)
						res = recv(events[i].data.fd, buf, sizeof(buf), 0);
					if (res < 0)
					{
						ERR_MSG("send");
						return -1;
					}
					else if (0 == res)
					{
						printf("[%s:%d] 客户端下线 newfd = %d __%d__ \n",
							   inet_ntoa(savcin[i].sin_addr), ntohs(savcin[i].sin_port), events[i].data.fd, __LINE__);

						close(i); // 关闭文件描述符
						flags[i] = 0;
					
					}
					printf("[%s:%d] 客户端 newfd = %d : %s, __%d__ \n",
						   inet_ntoa(savcin[i].sin_addr), ntohs(savcin[i].sin_port), events[i].data.fd, buf, __LINE__);
				}
			}
		}
	}

	if (close(sfd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}

	return 0;
}

TcpGrpUsr.c

#include "head.h"


int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}

	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0) //允许端口快速被重复使用
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	//填充服务器自身的地址信息结构体
	//真实的地址信息结构体根据地址族制定AF_INET ; man 7 ip
	struct sockaddr_in sin; 
	sin.sin_family      = AF_INET;            //必须填充AF_INET
	sin.sin_port        = htons(PORT);        //端口号的网络字节序  1024~49151
	sin.sin_addr.s_addr = inet_addr(IP);      //ifconfig查看本机IP

	if(connect(sfd,(struct sockaddr *)&sin,sizeof(sin))<0)
	{
		perror("connect");
		return -1;
	}
	printf("连接成功\n");

	//创建集合
	struct pollfd fds[2];

	fds[0].fd = 0;
	fds[0].events = POLLIN;
	fds[1].fd = sfd;
	fds[1].events = POLLIN;

	char buf[128]="";
	int res=0;

	while(1)
	{
		//阻塞方式监测集合
		res = poll(fds,2,-1);
		if(res < 0)
		{
			ERR_MSG("poll");
			return -1;
		}
		else if(0 == res)
		{
			printf("time out...\n");      //超时
			break;
		}

		//判断0文件描述符是否右POLLIN事件
		if((fds[0].revents & POLLIN))
		{
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1] = 0;
			if(send(sfd,buf,sizeof(buf),0) < 0)
			{
				ERR_MSG("send");
				return -1;
			}
			printf("发送成功\n");
		}
		//判断sfd文件描述符是否右POLLIN事件
		if(fds[1].revents & POLLIN)
		{
			//接收数据
			bzero(buf,sizeof(buf));
			res = recv(sfd,buf,sizeof(buf),0);
			if(res<0)
			{
				ERR_MSG("recv");
				return -1;
			}
			else if(res == 0)
			{
				printf("[%s:%d] 服务器下线__%d__ \n",\
					inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),__LINE__);
				break;
			}
			printf("[%s:%d] cfd = %d : %s__%d__ \n",\
					inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),sfd,buf,__LINE__);
		}	
	}

	if(close(sfd)<0)
	{
		ERR_MSG("close");
		return -1;
	}

	return 0;
}

网络编程 IO多路复用 [epoll版] (TCP网络聊天室),网络编程,网络,php,开发语言 文章来源地址https://www.toymoban.com/news/detail-665075.html

到了这里,关于网络编程 IO多路复用 [epoll版] (TCP网络聊天室)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【高并发网络通信架构】3.引入IO多路复用(select,poll,epoll)实现高并发tcp服务端

    目录 一,往期文章 二,基本概念 IO多路复用 select 模型 poll 模型 epoll 模型 select,poll,epoll 三者对比 三,函数清单 1.select 方法 2.fd_set 结构体 3.poll 方法 4.struct pollfd 结构体 5.epoll_create 方法 6.epoll_ctl 方法 7.epoll_wait 方法 8.struct epoll_event 结构体 四,代码实现 select 操作流程 s

    2024年02月14日
    浏览(31)
  • BIO、NIO、IO多路复用模型详细介绍&Java NIO 网络编程

    上文介绍了网络编程的基础知识,并基于 Java 编写了 BIO 的网络编程。我们知道 BIO 模型是存在巨大问题的,比如 C10K 问题,其本质就是因其阻塞原因,导致如果想要承受更多的请求就必须有足够多的线程,但是足够多的线程会带来内存占用问题、CPU上下文切换带来的性能问题

    2024年02月14日
    浏览(34)
  • 使用Linux系统IO多路复用中eopll创建基于TCP通信协议的多人聊天室

    一.1.搭建好TCP的通信模型 2.创建红黑树根节点 3.将套接字事件添加到红黑树中,使其被监听 4.当套接字事件发生,表示有客户端连接,将连接事件加入到红黑树节点当中 5.每当连接事件发生时,表示客户端发送信息到服务器 6.每当有事件准备就绪时,将对应的红黑树节点信息

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

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

    2024年01月17日
    浏览(49)
  • Linux多路IO复用:epoll

            epoll是为克服select、poll每次监听都需要在用户、内核空间反复拷贝,以及需要用户程序自己遍历发现有变化的文件描述符的缺点的多路IO复用技术。 epoll原理 创建内核空间的红黑树; 将需要监听的文件描述符上树; 内核监听红黑树上文件描述符的变化; 返回有变化

    2024年02月04日
    浏览(34)
  • IO多路复用之select/poll/epoll

    掌握select编程模型,能够实现select版本的TCP服务器. 掌握poll编程模型,能够实现poll版本的TCP服务器. 掌握epoll的编程模型,能够实现epoll版本的TCP服务器. epoll的LT模式和ET模式. 理解select和epoll的优缺点对比. 提示:以下是本篇文章正文内容,下面案例可供参考 多路转接天然的是让我

    2023年04月09日
    浏览(60)
  • 驱动开发,IO多路复用实现过程,epoll方式

    被称为当前时代最好用的io多路复用方式; 核心操作:一棵树(红黑树)、一张表(内核链表)以及三个接口;  思想:(fd代表文件描述符)         epoll要把检测的事件fd挂载到内核空间红黑树上,遍历红黑树,调用每个fd对应的操作方法,找到发生事件的fd,如果没有发

    2024年02月07日
    浏览(41)
  • Linux网络编程:多路I/O转接服务器(select poll epoll)

    文章目录: 一:select 1.基础API  select函数 思路分析 select优缺点 2.server.c 3.client.c 二:poll 1.基础API  poll函数  poll优缺点 read函数返回值 突破1024 文件描述符限制 2.server.c 3.client.c 三:epoll 1.基础API epoll_create创建   epoll_ctl操作  epoll_wait阻塞 epoll实现多路IO转接思路 epoll优缺点

    2024年02月11日
    浏览(41)
  • 【APUE】网络socket编程温度采集智能存储与上报项目技术------多路复用

    作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生在读,研究方向无线联邦学习 擅长领域:驱动开发,嵌入式软件开发,BSP开发 作者主页:一个平凡而乐于分享的小比特的个人主页 文章收录专栏:网络socket编程之温度采集智能存储与上报项目,本

    2024年04月10日
    浏览(33)
  • 网络编程代码实例:IO复用版

    网络编程代码实例:IO复用版。 yezhening/Environment-and-network-programming-examples: 环境和网络编程实例 (github.com) Environment-and-network-programming-examples: 环境和网络编程实例 (gitee.com) 使用传输控制协议(TCP) 服务端多进程,一个服务端可连接多个客户端 用户在客户端终端输入,可多次

    2024年02月02日
    浏览(26)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包