-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2a8c730
commit 34359cf
Showing
4 changed files
with
381 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
#include <iostream> | ||
|
||
#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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#include "Client.h" | ||
|
||
// 客户端主函数 | ||
// 创建客户端对象后启动客户端 | ||
int main(int argc, char *argv[]) { | ||
Client client; | ||
client.Start(); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
#include <iostream> | ||
|
||
#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<int>::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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#include "Server.h" | ||
|
||
// 服务端主函数 | ||
// 创建服务端对象后启动服务端 | ||
int main(int argc, char *argv[]) { | ||
Server server; | ||
server.Start(); | ||
return 0; | ||
} |