共有的头文件
/* utility.h */ #ifndef __UTILITY_H_INCLUDED #define __UTILITY_H_INCLUDED #include<iostream> #include<list> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/epoll.h> #include<fcntl.h> #include<errno.h> #include<unistd.h> #include<stdio.h> #include<stdlib.h> #include<string.h> using namespace std; //存储所有客户端的sockfd list<int> client_list; #define SERVER_IP "127.0.0.1" #define SERVER_PORT 8888 #define EPOLL_SIZE 5000 #define BUF_SIZE 0xFFFF #define SERVER_WELCOME "Welcome you join to chat room! Your chat ID is: Clent %d" #define SERVER_MESSAGE "ClientID %d say >> %s" #define EXIT "EXIT" #define CAUTION "There is only one int the char room!" int setnonblocking(int sockfd) { fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK); return 0; } void addfd(int epollfd, int fd, bool enable_et) { struct epoll_event ev; ev.data.fd = fd; ev.events= EPOLLIN; if (enable_et) { ev.events = EPOLLIN | EPOLLET; } epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev); setnonblocking(fd); printf("fd added to epoll! "); } int sendBroadcasemessage(int clientfd) { //buf接收新的聊天信息 //message保存格式信息 char buf[BUF_SIZE], message[BUF_SIZE]; bzero(buf, BUF_SIZE); bzero(buf, BUF_SIZE); printf("read from client(ClientID = %d) ", clientfd); int len = recv(clientfd, buf, BUF_SIZE, 0); //len等于0表示客户端关闭了链接 if (0 == len) { close(clientfd); client_list.remove(clientfd); printf("Clientfd = %d closed. now there are %d client int the chat room ", clientfd, (int)client_list.size()); } else //广播信息 { if (client_list.size() == 1) { send(clientfd, CAUTION, strlen(CAUTION), 0); return len; } sprintf(message, SERVER_MESSAGE, clientfd, buf); list<int>::iterator it; for (it = client_list.begin(); it != client_list.end(); ++it) { if (*it != clientfd) { if (send (*it, message, BUF_SIZE, 0) < 0) { perror("error"); exit(-1); } } } } return len; } #endif
/* server.h */ #include"utility.h" int main() { struct sockaddr_in serverAddr; serverAddr.sin_family = PF_INET; serverAddr.sin_port = htons(SERVER_PORT); serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP); //创建监听socket int listener = socket(PF_INET, SOCK_STREAM, 0); if (listener < 0) { perror("listener"); exit(-1); } printf("listen socket created "); //绑定地址 if (bind(listener, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { perror("bind error"); exit(-1); } //监听 int ret = listen(listener, 5); if (ret < 0) { perror("listen error"); exit(-1); } printf("Start to listen: %s ", SERVER_IP); //在内核中创建事件表 int epfd = epoll_create(EPOLL_SIZE); if (epfd < 0) { perror("epfd error"); exit(-1); } printf("epoll created, epollfd = %d ", epfd); static struct epoll_event events[EPOLL_SIZE]; //往内核事件中添加事件 addfd(epfd, listener, true); while (1) { //表示就绪的事件的数目 int epoll_events_count = epoll_wait(epfd, events, EPOLL_SIZE, -1); if (epoll_events_count < 0) { perror("epoll failure"); break; } printf("epoll_events_count = %d ", epoll_events_count); //处理这epoll_event_count个就绪事件 for (int i = 0; i < epoll_events_count; ++i) { int sockfd = events[i].data.fd; //表示新用户连接 if (sockfd == listener) { struct sockaddr_in client_address; socklen_t client_addrLength = sizeof(struct sockaddr_in); int clientfd = accept(listener, (struct sockaddr*) &client_address, &client_addrLength); printf("client connection from: %s : %d(IP : port) client = %d ", inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port), clientfd); addfd(epfd, clientfd, true); //服务端用list保存用户连接 client_list.push_back(clientfd); printf("Add new clientfd = %d to epoll ", clientfd); printf("Now there are %d clients int the chat room ", (int)client_list.size()); //服务器发送欢迎消息 printf("welcome message "); char message[BUF_SIZE]; bzero(message, BUF_SIZE); sprintf(message, SERVER_WELCOME, clientfd); int ret = send(clientfd, message, BUF_SIZE, 0); if (ret < 0) { perror("send error"); exit(-1); } } //处理用户发来的小心,并广播,使其他用户收到小心 else { int ret = sendBroadcasemessage(sockfd); if (ret < 0) { perror("error"); exit(-1); } } } } close(listener); close(epfd); return 0; }
客户端
/* client.h */ #include"utility.h" int main(int argc, char *argv[]) { struct sockaddr_in serverAddr; serverAddr.sin_family = PF_INET; serverAddr.sin_port = htons(SERVER_PORT); serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP); //创建socket int sock = socket(PF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("error"); exit(-1); } //连接服务器 if (connect(sock, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) { perror("connect "); exit(-1); } //创建管道,其中fd[0]表示父进程读,fd[1]表示子进程写 int pipe_fd[2]; if (pipe(pipe_fd) < 0) { perror("pipe error"); exit(-1); } //创建epoll int epfd = epoll_create(EPOLL_SIZE); if (epfd < 0) { perror("epfd error"); exit(-1); } static struct epoll_event events[2]; //将sock和管道读端描述符都添加到内核事件中 addfd(epfd, sock, true); addfd(epfd, pipe_fd[0], true); bool isClientwork = true; char message[BUF_SIZE]; int pid = fork(); if (pid < 0) { perror("fork error"); exit(-1); } else if (0 == pid) //子进程 { //子进程负责写入管道,因此先关闭读端 close(pipe_fd[0]); printf("Please input 'exit' to exit the chat room "); while (isClientwork) { bzero(&message, BUF_SIZE); fgets(message, BUF_SIZE, stdin); if (strncasecmp(message, EXIT, strlen(EXIT)) == 0) { isClientwork = 0; } else //子进程将信息写入管道 { if (write(pipe_fd[1], message, strlen(message) - 1) < 0) { perror("fork error"); exit(-1); } } } } else //父进程 { //父进程负责都管道数据,因此先关闭写端 close(pipe_fd[1]); while(isClientwork) { int epoll_event_count = epoll_wait(epfd, events, 2, -1); for (int i = 0; i < epoll_event_count; i++) { bzero(&message, BUF_SIZE); //处理就绪事件 if (events[i].data.fd == sock) { int ret = recv(sock, message, BUF_SIZE, 0); if (0 == ret) { printf("Server closed connection: %d ", sock); close(sock); isClientwork = 0; } else { printf("%s ", message); } } else //子进程写入事件发生,父进程处理并发送服务端 { int ret = read(events[i].data.fd, message, BUF_SIZE); if (0 == ret) { isClientwork = 0; } else { send(sock, message, BUF_SIZE, 0); } } } } } if (pid) { close(pipe_fd[0]); close(sock); } else { close(pipe_fd[1]); } return 0; }