Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
FangchenHu authored Jun 16, 2021
1 parent 2a8c730 commit 34359cf
Show file tree
Hide file tree
Showing 4 changed files with 381 additions and 0 deletions.
170 changes: 170 additions & 0 deletions 2018141501263胡方晨/src/Client.cpp
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();
}
9 changes: 9 additions & 0 deletions 2018141501263胡方晨/src/ClientMain.cpp
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;
}
193 changes: 193 additions & 0 deletions 2018141501263胡方晨/src/Server.cpp
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();
}
9 changes: 9 additions & 0 deletions 2018141501263胡方晨/src/ServerMain.cpp
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;
}

0 comments on commit 34359cf

Please sign in to comment.