TCP下服务端和多个客户端连接通信(使用select、epoll)

tcp客户端(ser)操作流程:socket----->bind----->listen----->accept------>recv------->send----->close

注意:操作时客户端cli可使用之前写过TCP通信的cli.c

一、select

①代码

#include "head.h"
int init_tcp_ser(char *ip,unsigned short port)
{
    struct sockaddr_in ser;
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(-1 == sockfd)
    {
        perror("fail sockfd");
        return -1 ;
    }

    ser.sin_family = AF_INET;
    ser.sin_port = htons(port);
    ser.sin_addr.s_addr = inet_addr(ip);
    int ret = bind(sockfd,(struct sockaddr *)&ser, sizeof(ser));
    if (-1 == ret)
	{
		perror("fail bind");
		exit(1);
	}
	
	ret = listen(sockfd, 100);

	return sockfd;
}
int main(int argc, char const *argv[])
{
    int sockfd = init_tcp_ser("192.168.1.192",50000);//建立套接字
    struct sockaddr_in cli;
    socklen_t len = sizeof(cli);

    fd_set rdfds;
    fd_set tmfds;
    FD_ZERO(&rdfds);//文件描述符集合清0

    FD_SET(sockfd,&rdfds);//将sockfd加入文件描述符集合中
    int maxfd = sockfd;
    while(1)
    {
        tmfds = rdfds;
        int cnt = select(maxfd + 1,&tmfds,NULL,NULL,NULL);//监测的文件描述符上限值(最大文件描述符的值+1),读文件描述符集合,写文件描述符集合,异常条件的描述符集合,设置超时时间
		(NULL:一直等待,直到有事件发生)
        if(-1 == cnt)
        {
            perror("fail select");
            return -1;
        }

        if(FD_ISSET(sockfd,&tmfds))//判断文件描述符sockfd是否仍在文件描述符集合中
        {
            int connfd = accept(sockfd,(struct sockaddr *)&cli,&len);
            if(-1 == connfd)
            {
                perror("fail accept");
                return -1;
            }
            printf("[%s:%d online]\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
            FD_SET(connfd,&rdfds); //将connfd加入文件描述符集合中
            maxfd = connfd > maxfd ? connfd : maxfd;
        }

        int i = 0;
        for(i = sockfd +1 ;i<maxfd;i++)
        {
            if(FD_ISSET(i,&tmfds))//判断文件描述符i是否仍在文件描述符集合中
            {
                char buff[1024] = {0};
                ssize_t size = recv(i,buff,sizeof(buff),0);
                if(size < 0 )
                {
                    perror("fail recv");
                    FD_CLR(i,&rdfds);// 将i从文件描述符集合中清除
                    close(i);
                    continue;
                }
                else if(0 == size)
                {
                    FD_CLR(i,&rdfds);// 将i从文件描述符集合中清除
                    close(i);
                    continue;
                }
                printf("cli-------->ser:%s\n",buff);

                strcat(buff,"-------I kwon\n");
                size = send(i,buff,sizeof(buff),0);
                if(size <0)
                {
                    perror("fail send");
                    FD_CLR(i,&rdfds);// 将i从文件描述符集合中清除
                    close(i);
                    continue;
                }
            }
        
        }
    }
    close(sockfd);
    return 0;
}

②操作结果(使用网络调试助手)

二、epoll

①代码

#include "head.h"

int init_tcp_ser(char *ip, unsigned short port)
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("fail socket");
		exit(1);
	}
	struct sockaddr_in ser;
	ser.sin_family = AF_INET;
	ser.sin_port = htons(port);
	ser.sin_addr.s_addr = inet_addr(ip);
	int ret = bind(sockfd, (struct sockaddr *)&ser, sizeof(ser));
	if (-1 == ret)
	{
		perror("fail bind");
		exit(1);
	}
	
	ret = listen(sockfd, 100);

	return sockfd;
}
int add_epoll_fd(int epfd,int fd,uint32_t events)
{
    struct epoll_event ev;
    ev.events = events;
    ev.data.fd = fd;
    int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev);//EPOLL_CTL_ADD新增事件
    if(-1 == ret)
    {
        perror("fail epoll_ctl add");
        return -1;
    }
}

int delet_epoll_fd(int epfd,int fd)
{
    int ret = epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);//EPOLL_CTL_DEL		删除事件
    if(-1 == ret)
    {
        perror("epll_ctl del");
        return -1;
    }
    return 0 ;
}
int main(int argc, char const *argv[])
{
    struct sockaddr_in cli;
    socklen_t len = sizeof(cli);
    int sockfd = init_tcp_ser("192.168.1.192",50000);
    int epfd = epoll_create(1024);//创建epoll文件描述符集合
    if(-1 == epfd)
    {
        perror("faol epoll_create");
        return -1;
    }

    add_epoll_fd(epfd,sockfd,EPOLLIN);//EPOLLIN		读事件
    struct epoll_event evs[1024];
       while(1)
    {
        int epcnt = epoll_wait(epfd,evs,1024,-1);//返回事件的个数,监控io事件
        if(-1 == epcnt)
        {
            perror("fail epoll_wait");
            break;
        }
        int i = 0;
       
        for(i = 0;i<epcnt;i++)
        {
            if(sockfd == evs[i].data.fd)
            {
                int connfd= accept(sockfd,(struct sockaddr *)&cli,&len);
                if(-1 == connfd)
                {
                    perror("fail accept");
                    continue;
                }
                add_epoll_fd(epfd,connfd,EPOLLIN);
            }
             else 
             {   
                char buff[1024] = {0};
                ssize_t size = recv(evs[i].data.fd,buff,sizeof(buff),0);
               if(-1 == size)
               {
                perror("fail recv");
                delet_epoll_fd(epfd,evs[i].data.fd);
                close(evs[i].data.fd);
                continue;
               }
               else if(0 == size)
               {
                delet_epoll_fd(epfd,evs[i].data.fd);
                close(evs[i].data.fd);
                continue;
               }
                printf("cli------ser: %s\n", buff);

                strcat(buff,"------------I kwon!\n");

                ssize_t s = send(evs[i].data.fd,buff,strlen(buff),0);
                if(-1 == s)
               {
                perror("fail recv");
                delet_epoll_fd(epfd,evs[i].data.fd);
                close(evs[i].data.fd);
                continue;
               }               
             }
        }
    }
        
    close(sockfd);
    return 0;
}

②操作结果

头文件head.h

#ifndef __HEAD_H__ //防止头文件被重复定义
#define __HEAD_H__

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

#endif

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
网络编程中,可以使用epoll来实现多个客户端转发到多个服务器的功能。epollLinux下的一种高性能I/O事件通知机制,可以有效地处理大量的并发连接。 以下是使用epoll实现多个客户端转发到多个服务器的基本步骤: 1. 创建一个监听套接字,用于接收客户端连接。将该套接字添加到epoll事件集合中,监听读事件。 2. 创建一个epoll实例,通过epoll_create函数进行创建。 3. 将监听套接字添加到epoll实例中,使用epoll_ctl函数进行添加。 4. 进入事件循环,通过epoll_wait函数等待事件的发生。该函数会阻塞,直到有事件发生或超时。 5. 当有事件发生时,通过遍历事件数组,处理相应的事件。 6. 如果是监听套接字上有读事件发生,说明有新的客户端连接请求。使用accept函数接受连接,并将新的客户端套接字添加到epoll实例中。 7. 如果是客户端套接字上有读事件发生,说明有客户端发送数据到服务器。可以读取数据,并根据转发策略选择目标服务器,将数据转发给相应的服务器。 8. 如果是服务器套接字上有读事件发生,说明有服务器返回数据给客户端。可以读取数据,并将数据发送给相应的客户端。 9. 处理完事件后,将相应的套接字从epoll实例中删除。 10. 重复步骤4-9,直到事件循环结束。 通过以上步骤,可以实现多个客户端同时连接到服务器,并进行数据转发的功能。需要注意的是,epoll是一种边缘触发模式,需要在处理事件时确保将所有数据都读取完毕或发送完毕,以免遗漏数据或造成阻塞。另外,需要根据具体的业务需求来确定转发策略和服务器选择算法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值