diff --git "a/2018141501263\350\203\241\346\226\271\346\231\250/src/Client.cpp" "b/2018141501263\350\203\241\346\226\271\346\231\250/src/Client.cpp" new file mode 100644 index 0000000..f1e23ca --- /dev/null +++ "b/2018141501263\350\203\241\346\226\271\346\231\250/src/Client.cpp" @@ -0,0 +1,170 @@ +#include + +#include "Client.h" + +using namespace std; + +// 客户端类成员函数 + +// 客户端类构造函数 +Client::Client(){ + + // 初始化要连接的服务器地址和端口 + serverAddr.sin_family = PF_INET; + serverAddr.sin_port = htons(SERVER_PORT); + serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP); + + // 初始化socket + sock = 0; + + // 初始化进程号 + pid = 0; + + // 客户端状态 + isClientwork = true; + + // epool fd + epfd = 0; +} + +// 连接服务器 +void Client::Connect() { + cout << "Connect Server: " << SERVER_IP << " : " << SERVER_PORT << endl; + + // 创建socket + sock = socket(PF_INET, SOCK_STREAM, 0); + if(sock < 0) { + perror("sock error"); + exit(-1); + } + + // 连接服务端 + if(connect(sock, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { + perror("connect error"); + exit(-1); + } + + // 创建管道,其中fd[0]用于父进程读,fd[1]用于子进程写 + if(pipe(pipe_fd) < 0) { + perror("pipe error"); + exit(-1); + } + + // 创建epoll + epfd = epoll_create(EPOLL_SIZE); + + if(epfd < 0) { + perror("epfd error"); + exit(-1); + } + + //将sock和管道读端描述符都添加到内核事件表中 + addfd(epfd, sock, true); + addfd(epfd, pipe_fd[0], true); + +} + +// 断开连接,清理并关闭文件描述符 +void Client::Close() { + + if(pid){ + //关闭父进程的管道和sock + close(pipe_fd[0]); + close(sock); + }else{ + //关闭子进程的管道 + close(pipe_fd[1]); + } +} + +// 启动客户端 +void Client::Start() { + + // epoll 事件队列 + static struct epoll_event events[2]; + + // 连接服务器 + Connect(); + + // 创建子进程 + pid = fork(); + + // 如果创建子进程失败则退出 + if(pid < 0) { + perror("fork error"); + close(sock); + exit(-1); + } else if(pid == 0) { + // 进入子进程执行流程 + //子进程负责写入管道,因此先关闭读端 + close(pipe_fd[0]); + + // 输入exit可以退出聊天室 + cout << "Please input 'exit' to exit the chat room" << endl; + + // 如果客户端运行正常则不断读取输入发送给服务端 + while(isClientwork){ + bzero(&message, BUF_SIZE); + fgets(message, BUF_SIZE, stdin); + + // 客户输出exit,退出 + 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 { + //pid > 0 父进程 + //父进程负责读管道数据,因此先关闭写端 + close(pipe_fd[1]); + + // 主循环(epoll_wait) + while(isClientwork) { + int epoll_events_count = epoll_wait( epfd, events, 2, -1 ); + + //处理就绪事件 + for(int i = 0; i < epoll_events_count ; ++i) + { + bzero(&message, BUF_SIZE); + + //服务端发来消息 + if(events[i].data.fd == sock) + { + //接受服务端消息 + int ret = recv(sock, message, BUF_SIZE, 0); + + // ret= 0 服务端关闭 + if(ret == 0) { + cout << "Server closed connection: " << sock << endl; + close(sock); + isClientwork = 0; + } else { + cout << message << endl; + } + } + //子进程写入事件发生,父进程处理并发送服务端 + else { + //父进程从管道中读取数据 + int ret = read(events[i].data.fd, message, BUF_SIZE); + + // ret = 0 + if(ret == 0) + isClientwork = 0; + else { + // 将信息发送给服务端 + send(sock, message, BUF_SIZE, 0); + } + } + }//for + }//while + } + + // 退出进程 + Close(); +} diff --git "a/2018141501263\350\203\241\346\226\271\346\231\250/src/ClientMain.cpp" "b/2018141501263\350\203\241\346\226\271\346\231\250/src/ClientMain.cpp" new file mode 100644 index 0000000..b1dab62 --- /dev/null +++ "b/2018141501263\350\203\241\346\226\271\346\231\250/src/ClientMain.cpp" @@ -0,0 +1,9 @@ +#include "Client.h" + +// 客户端主函数 +// 创建客户端对象后启动客户端 +int main(int argc, char *argv[]) { + Client client; + client.Start(); + return 0; +} diff --git "a/2018141501263\350\203\241\346\226\271\346\231\250/src/Server.cpp" "b/2018141501263\350\203\241\346\226\271\346\231\250/src/Server.cpp" new file mode 100644 index 0000000..f0be0f0 --- /dev/null +++ "b/2018141501263\350\203\241\346\226\271\346\231\250/src/Server.cpp" @@ -0,0 +1,193 @@ +#include + +#include "Server.h" + +using namespace std; + +// 服务端类成员函数 + +// 服务端类构造函数 +Server::Server(){ + + // 初始化服务器地址和端口 + serverAddr.sin_family = PF_INET; + serverAddr.sin_port = htons(SERVER_PORT); + serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP); + + // 初始化socket + listener = 0; + + // epool fd + epfd = 0; +} + +// 初始化服务端并启动监听 +void Server::Init() { + cout << "Init Server..." << endl; + + //创建监听socket + listener = socket(PF_INET, SOCK_STREAM, 0); + if(listener < 0) { perror("listener"); exit(-1);} + + //绑定地址 + 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); + } + + cout << "Start to listen: " << SERVER_IP << endl; + + //在内核中创建事件表 + epfd = epoll_create(EPOLL_SIZE); + + if(epfd < 0) { + perror("epfd error"); + exit(-1); + } + + //往事件表里添加监听事件 + addfd(epfd, listener, true); + +} + +// 关闭服务,清理并关闭文件描述符 +void Server::Close() { + + //关闭socket + close(listener); + + //关闭epoll监听 + close(epfd); +} + +// 发送广播消息给所有客户端 +int Server::SendBroadcastMessage(int clientfd) +{ + // buf[BUF_SIZE] 接收新消息 + // message[BUF_SIZE] 保存格式化的消息 + char buf[BUF_SIZE], message[BUF_SIZE]; + bzero(buf, BUF_SIZE); + bzero(message, BUF_SIZE); + + // 接收新消息 + cout << "read from client(clientID = " << clientfd << ")" << endl; + int len = recv(clientfd, buf, BUF_SIZE, 0); + + // 如果客户端关闭了连接 + if(len == 0) + { + close(clientfd); + + // 在客户端列表中删除该客户端 + clients_list.remove(clientfd); + cout << "ClientID = " << clientfd + << " closed.\n now there are " + << clients_list.size() + << " client in the char room" + << endl; + + } + // 发送广播消息给所有客户端 + else + { + // 判断是否聊天室还有其他客户端 + if(clients_list.size() == 1) { + // 发送提示消息 + send(clientfd, CAUTION, strlen(CAUTION), 0); + return len; + } + // 格式化发送的消息内容 + sprintf(message, SERVER_MESSAGE, clientfd, buf); + + // 遍历客户端列表依次发送消息,需要判断不要给来源客户端发 + list::iterator it; + for(it = clients_list.begin(); it != clients_list.end(); ++it) { + if(*it != clientfd){ + if( send(*it, message, BUF_SIZE, 0) < 0 ) { + return -1; + } + } + } + } + return len; +} + +// 启动服务端 +void Server::Start() { + + // epoll 事件队列 + static struct epoll_event events[EPOLL_SIZE]; + + // 初始化服务端 + Init(); + + //主循环 + while(1) + { + //epoll_events_count表示就绪事件的数目 + int epoll_events_count = epoll_wait(epfd, events, EPOLL_SIZE, -1); + + if(epoll_events_count < 0) { + perror("epoll failure"); + break; + } + + cout << "epoll_events_count =\n" << epoll_events_count << endl; + + //处理这epoll_events_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 ); + + cout << "client connection from: " + << inet_ntoa(client_address.sin_addr) << ":" + << ntohs(client_address.sin_port) << ", clientfd = " + << clientfd << endl; + + addfd(epfd, clientfd, true); + + // 服务端用list保存用户连接 + clients_list.push_back(clientfd); + cout << "Add new clientfd = " << clientfd << " to epoll" << endl; + cout << "Now there are " << clients_list.size() << " clients int the chat room" << endl; + + // 服务端发送欢迎信息 + cout << "welcome message" << endl; + 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"); + Close(); + exit(-1); + } + } + //处理用户发来的消息,并广播,使其他用户收到信息 + else { + int ret = SendBroadcastMessage(sockfd); + if(ret < 0) { + perror("error"); + Close(); + exit(-1); + } + } + } + } + + // 关闭服务 + Close(); +} diff --git "a/2018141501263\350\203\241\346\226\271\346\231\250/src/ServerMain.cpp" "b/2018141501263\350\203\241\346\226\271\346\231\250/src/ServerMain.cpp" new file mode 100644 index 0000000..19955cc --- /dev/null +++ "b/2018141501263\350\203\241\346\226\271\346\231\250/src/ServerMain.cpp" @@ -0,0 +1,9 @@ +#include "Server.h" + +// 服务端主函数 +// 创建服务端对象后启动服务端 +int main(int argc, char *argv[]) { + Server server; + server.Start(); + return 0; +}