Linux C 基于tcp和epoll在线聊天室

基于tcp和epoll在线聊天室

说明

  服务端:实现了验证用户是否已经存在(支持最大64用户连接)支持广播用户进入退出聊天室以及用户聊天内容。

  这里只提供里服务端代码,如果想要看客户端代码点击这里

服务端代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/epoll.h>

typedef struct sockaddr  SA;
typedef struct sockaddr_in  SIN;
#define MAXBACKLOG   100

int Socket(int domain,int type,int protocol);
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen);
int Listen(int s,int backlog);
int Accept(int s,struct sockaddr * addr,int * addrlen);
int is_exist(char * username);
void broadcast(char *r,char *n);

char Userlist[64][20] = {0};
int Userfdlist[64] = {0};

//./app 192.168.5.166  8888
int main(int argc,char *argv[])
{	
	int opt = 1;
	//建立监听套接字
	int socketfd = Socket(AF_INET,SOCK_STREAM,0);
	//需要进行重用地址及其端口号
	setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
	//绑定信息编写服务器信息
	SIN   serverinfo;
	serverinfo.sin_family =AF_INET;
	serverinfo.sin_port   =htons(atoi(argv[2])); 
	serverinfo.sin_addr.s_addr=  inet_addr(argv[1]);
	int addrlen = sizeof(SIN);
	Bind(socketfd,(SA*)&serverinfo,addrlen);
	//监听
	Listen(socketfd,MAXBACKLOG);
	//epoll创建根节点
	int epollfd = epoll_create(1024);
	
	//添加socketfd文件描述符至内核 红黑树
	struct epoll_event event;
	event.events = EPOLLIN;			//事件成员
	event.data.fd = socketfd;		//数据
	epoll_ctl(epollfd,EPOLL_CTL_ADD,socketfd, &event);
	//读写
	while(1)
	{
		struct epoll_event events[10];
		int count = epoll_wait(epollfd,events,10,-1);
		for(int i = 0; i< count;i++)
		{
			if(events[i].events == EPOLLIN)
			{
				if(events[i].data.fd == socketfd)
				{
					//wait client connect
					SIN clientinfo;
					struct epoll_event event;
					int  clientaddrlen =sizeof(SA);
					int newfd = Accept(socketfd,(SA*)&clientinfo,&clientaddrlen);
					printf("客户端地址:%s 端口号:%d\n",inet_ntoa(clientinfo.sin_addr),ntohs(clientinfo.sin_port));
					
					//read username
					char namebuff[20];
					read(newfd,namebuff,sizeof(namebuff));
					if(is_exist(namebuff))
					{
						write(newfd,"已存在",sizeof("已存在"));
						close(newfd);
					}
					else
					{
						//put newfd into Red-Black Tree
						event.events = EPOLLIN;			//事件成员
						event.data.fd = newfd;			//数据
						epoll_ctl(epollfd,EPOLL_CTL_ADD,newfd, &event);
						write(newfd,"登录成功",sizeof("登录成功"));	
						//save userfd & username
						for(int j=0;j<64;j++)
							if(strlen(Userlist[j])==0)
							{Userfdlist[j]=event.data.fd;strcpy(Userlist[j],namebuff);break;}	
						char r[50];
						sprintf(r,"%s %s",namebuff,"进入聊天室");
						printf("%s\n",r);
						broadcast(r,namebuff);
	
					}
				}
				else
				{
					//read
					char readbuff[512] = {0};
					int len = read(events[i].data.fd,readbuff,sizeof(readbuff));
					
					//get name and pos
					char namebuff[20] = {0};
					int key = 0;
					for(int i=0;i<10;i++)if(strstr(readbuff,Userlist[i])){strcpy(namebuff,Userlist[i]);key=i;break;}
					
					if(len > 0 && strlen(readbuff)!=0)
					{
						printf("%s\n",readbuff);
						broadcast(readbuff,namebuff);
						if(strstr(readbuff,"退出"))
						{
							strcpy(Userlist[key],"\0");
							Userfdlist[key] = 0;
							epoll_ctl(epollfd,EPOLL_CTL_DEL,events[i].data.fd,NULL);
							close(events[i].data.fd);
						}
					}
					else if(len <= 0)
					{
						epoll_ctl(epollfd,EPOLL_CTL_DEL,events[i].data.fd,NULL);
						close(events[i].data.fd);
					}
				}
			}
		}
	}
	//关闭
	close(socketfd);
	return 0;
}
int is_exist(char * username)
{
	for(int i = 0 ; i < 10; i++)
		if(strcmp(username,Userlist[i]) == 0)
			return 1;
	return 0;
}
void broadcast(char *r,char *n)
{
	for(int i=0 ; i<64 ;i++)
		//if it is a user and not himself
		if(strcmp(Userlist[i],n)!=0 && strlen(Userlist[i])!=0)
			write(Userfdlist[i],r,strlen(r));
}
int Socket(int domain,int type,int protocol)
{
	int socketFd = socket(domain,type,protocol);
	if(socketFd == -1)
	{
		perror("socket");
		exit(1);
	}
	return socketFd;
}
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen)
{
	int val = bind(sockfd,my_addr,addrlen);
	if(val)
	{
		perror("bind");
		exit(1);
	}
	return 0;
}
int Listen(int s,int backlog)
{
	int val = listen(s,backlog);
	if(val == -1)
	{
		perror("listen");
		exit(1);
	}
	return val;
}
int Accept(int s,struct sockaddr * addr,int * addrlen)
{
	int NEWfd = accept(s,addr,addrlen);
	if(NEWfd == -1)
	{
		perror("listen");
		exit(1);
	}
	return NEWfd;
}
相关推荐
Brilliantwxx20 分钟前
【Linux】 软件包管理器(yum)+ Vim使用
linux·运维·服务器·开发语言·编辑器·vim
skywalk816325 分钟前
在FreeBSD的Uubntu兼容环境下安装Reasonix
linux·运维·freebsd
^yi39 分钟前
【Linux系统编程】对操作系统的理解
linux·运维·服务器·操作系统·系统调用·os
Kina_C1 小时前
LVS-NAT 负载均衡实验从环境搭建到规则持久化
linux·运维·服务器·负载均衡·lvs
小肝一下1 小时前
7. 库制作与原理
linux·c语言·操作系统·bash·shell·动静态库
春生野草1 小时前
C基础--八大排序(个人笔记)
c语言·笔记·排序算法
IT方大同2 小时前
C语言分支与循环语句
c语言·开发语言·算法
hkj88082 小时前
Ubuntu 20.04 安装 OpenJDK 17
linux·运维·ubuntu
huainingning2 小时前
防火墙重启后ssl登录提示证书过期临时解决方法
linux·服务器·ssl
Fu_Lin_2 小时前
Qt嵌入式从零基础到精通(01):Qt嵌入式开发全景图:从桌面Qt到ARM Linux
linux·arm开发·qt·qt5·嵌入式linux