基于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;
}