server.h
/*
* @Author: Li Chao
* @Date: 2020-11-03 08:38:56
* @Last Modified by: Li Chao
* @Last Modified time: 2020-11-03 17:03:45
*/
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <thread>
#include <stdio.h>
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <sys/time.h>
using namespace std;
#define BUF_SIZE 0xFFFF
#define EXIT "EXIT"
struct Msg
{
int type;
int fromID;
int toID;
char content[1024];
};
class Server
{
private:
thread *send_thread, *recv_thread, *heartbeat_thread;
int socket_fd, client_fd;
string server_ip;
unsigned short server_port;
bool isServerwork;
bool heart_flag;
uint64_t heartbeat_time;
char recv_buf[BUF_SIZE];
char send_buf[BUF_SIZE];
char key_input_buf[BUF_SIZE];
bool init_server(string ip, unsigned short port);
void start();
void on_connected();
void on_disconnected();
public:
Server();
void send_msg(char *buf);
void recv_msg();
void send_thread_function();
void recv_thread_function();
void heartbeat_send_thread_function(); //发送心跳
void send_server_msg(); //制作并发送结构体信息
void handle_msg(Msg &msg); //处理打印结构体信息
void handle_heartbeat(); //处理心跳
};
server.cpp
/*
* @Author: Li Chao
* @Date: 2020-11-03 10:50:47
* @Last Modified by: Li Chao
* @Last Modified time: 2020-11-03 17:43:41
*/
#include "server.h"
Server::Server()
{
socket_fd = -1;
server_ip = "127.0.0.1";
server_port = 8888;
heart_flag = false;
isServerwork = true;
init_server(server_ip,server_port);
}
bool Server::init_server(string ip, unsigned short port)
{
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if(socket_fd == -1)
{
perror("socket");
exit(-1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
int reuseaddr = 1;
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));
int ret = bind(socket_fd, (struct sockaddr*)&addr, sizeof(addr));
if(ret == -1)
{
perror("bind");
exit(-1);
}
listen(socket_fd, 30);
cout<<"wait client connect"<<endl;
start();
return true;
}
void Server::start()
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
while (isServerwork) //不输入exit退出,就一直循环等等客户端接入
{
client_fd = accept(socket_fd, (struct sockaddr*)&client, &len);
if(client_fd == -1)
{
perror("accept");
exit(-1);
}
char *client_ip = inet_ntoa(client.sin_addr);
cout<<"client ip:"<<client_ip<<"connect ok!"<<endl;
if(!heart_flag)
{
send_thread = new thread(bind(&Server::send_thread_function, this));
recv_thread = new thread(bind(&Server::recv_thread_function, this));
heartbeat_thread = new thread(bind(&Server::handle_heartbeat, this));
heart_flag = true;
}
}
on_disconnected();
}
void Server::send_msg(char *buf)
{
memset(send_buf,0,BUF_SIZE);
memcpy(send_buf, buf, sizeof(buf));
send(client_fd, send_buf, strlen(send_buf), 0);
}
void Server::recv_msg()
{
int recv_size = recv(client_fd, recv_buf, sizeof(recv_buf), 0);
if(recv_size <= 0)
{
return;
}
if(recv_size == 8)//进入心跳打印
{
cout<<"recv_size:"<<recv_size<<endl;
cout<<"recv content:"<<recv_buf<<endl;
struct timeval recv_now;
gettimeofday(&recv_now, NULL);
heartbeat_time = recv_now.tv_sec * 1000 + recv_now.tv_usec / 1000;
}
else //处理结构体信息
{
Msg msg;
memset(msg.content, 0 ,sizeof(msg.content));
memcpy(&msg, recv_buf, sizeof(msg));
handle_msg(msg);
}
}
void Server::on_disconnected()
{
//send_thread->join();
//recv_thread->join();
//delete send_thread;
//delete recv_thread;
//send_thread = nullptr;
//recv_thread = nullptr;
close(socket_fd);
close(client_fd);
}
void Server::send_thread_function()
{
while (true)
{
memset(key_input_buf,0,BUF_SIZE);
fgets(key_input_buf,BUF_SIZE, stdin);
if(strncasecmp(key_input_buf, EXIT, strlen(EXIT)) == 0 )
{
cout<<"exit"<<endl;
isServerwork = false;
break;
}
//send_msg(key_input_buf);//发送手动输入信息
send_server_msg();//发送结构体信息
}
}
void Server::recv_thread_function()
{
while (true)
{
recv_msg();
}
}
void Server::heartbeat_send_thread_function()
{
while (isServerwork)
{
unsigned char sendbuf[8] = {0};
sendbuf[0] = 'h';
sendbuf[1] = 'e';
sendbuf[2] = 'a';
sendbuf[3] = 'r';
sendbuf[4] = 't';
send(client_fd, sendbuf, sizeof(sendbuf), 0);
sleep(2);
}
}
void Server::send_server_msg() //制作并发送结构体信息
{
Msg msg;
memset(msg.content, 0 ,sizeof(msg.content));
msg.type = 11;
msg.fromID = 22;
msg.toID = 33;
char send_content[] = "hello this is server send message info";
memcpy(msg.content, send_content, sizeof(send_content));
send(client_fd, (char *)&msg, sizeof(msg), 0);
}
void Server::handle_msg(Msg &msg) //处理打印结构体信息
{
cout<<"msg.type:"<<msg.type<<endl;
cout<<"msg.fromID:"<<msg.fromID<<endl;
cout<<"msg.toID:"<<msg.toID<<endl;
cout<<"msg.content:"<<msg.content<<endl;
}
void Server::handle_heartbeat() //监听心跳
{
struct timeval now;
static int heartbeat_timeout_cnt = 0;
while (heart_flag)
{
usleep(500 * 1000);
gettimeofday(&now, NULL);
uint64_t now_time = now.tv_sec * 1000 + now.tv_usec/1000; //每次获取当前时间
if( (now_time - heartbeat_time) > 1000)
{
//cout<<"heartbeat_timeout_cnt++"<<endl;
heartbeat_timeout_cnt++;
}else
{
heartbeat_timeout_cnt = 0;
}
if(heartbeat_timeout_cnt > 5) //判断5次心跳超时,退出心跳检测,关闭client_fd等等重新连接
{
cout<<"heartbeat timeout!!!"<<endl;
heart_flag = false;
heartbeat_timeout_cnt = 0;
//cout<<"client_fd:"<<client_fd<<endl;
if (client_fd > 0) {
shutdown(client_fd, SHUT_RDWR);
close(client_fd);
client_fd = -1;
cout<<"close connection, and reconnect"<<endl;
}
break;
}
}
}
server_main.cpp
/*
* @Author: Li Chao
* @Date: 2020-11-03 14:44:12
* @Last Modified by: Li Chao
* @Last Modified time: 2020-11-03 14:49:42
*/
#include "server.h"
int main()
{
Server server;
return 0;
}
client.h
/*
* @Author: Li Chao
* @Date: 2020-11-03 15:00:12
* @Last Modified by: Li Chao
* @Last Modified time: 2020-11-03 16:54:12
*/
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <thread>
#include <stdio.h>
#include <iostream>
#include <unistd.h>
#include <pthread.h>
using namespace std;
#define BUF_SIZE 0xFFFF
#define EXIT "EXIT"
struct Msg
{
int type;
int fromID;
int toID;
char content[1024];
};
class Client
{
private:
thread *send_thread, *recv_thread, *heartbeat_thread;
int socket_fd;
string server_ip;
unsigned short server_port;
bool isClientwork;
char recv_buf[BUF_SIZE];
char send_buf[BUF_SIZE];
char key_input_buf[BUF_SIZE];
bool init_client(string ip, unsigned short port);
void on_connected();
void on_disconnected();
public:
Client();
void send_msg(char *buf);
void recv_msg();
void send_thread_function();
void recv_thread_function();
void heartbeat_send_thread_function(); //发送心跳
void send_server_msg(); //制作并发送结构体信息
void handle_msg(Msg &msg); //处理打印结构体信息
};
client.cpp
/*
* @Author: Li Chao
* @Date: 2020-11-03 15:20:30
* @Last Modified by: Li Chao
* @Last Modified time: 2020-11-03 17:25:22
*/
#include "client.h"
Client::Client()
{
socket_fd = -1;
server_ip = "127.0.0.1";
server_port = 8888;
init_client(server_ip,server_port);
}
bool Client::init_client(string ip, unsigned short port)
{
reconnect:
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if(socket_fd == -1)
{
perror("socket");
exit(-1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(server_port);
addr.sin_addr.s_addr = inet_addr(server_ip.c_str());
//如果服务器没有在线循环等等服务器上线
while (true) {
int ret = connect(socket_fd, (struct sockaddr*)&addr, sizeof(addr));
if (ret < 0) {
cout<<"connect "<<ip.c_str() <<":"<< port <<"failed,trying to reconnect..."<<endl;
usleep(2000 * 1000);
} else
{
cout<<"connect ok"<<endl;
isClientwork = true;
break;
}
}
//send_thread = new thread(bind(&Client::send_thread_function, this));
//recv_thread = new thread(bind(&Client::recv_thread_function, this));
heartbeat_thread = new thread(bind(&Client::heartbeat_send_thread_function, this));
while (true)
{
int recv_size = recv(socket_fd, recv_buf, sizeof(recv_buf), 0);
if(recv_size <= 0)
{
cout<<"close connent"<<endl;
//断开之后,重新连接服务器
//send_thread->join();
//delete send_thread;
//send_thread = nullptr;
//heartbeat_thread->join();
//delete heartbeat_thread;
//heartbeat_thread = nullptr;
isClientwork = false;
close(socket_fd);
sleep(3);
goto reconnect;
}else if(recv_size == 8)//进入心跳打印
{
cout<<"recv_size:"<<recv_size<<endl;
cout<<"recv content:"<<recv_buf<<endl;
}
else if (recv_size > 0 && recv_size != 8) //处理结构体信息
{
Msg msg;
memset(msg.content, 0 ,sizeof(msg.content));
memcpy(&msg, recv_buf, sizeof(msg));
handle_msg(msg);
}
}
on_disconnected();
return true;
}
void Client::send_msg(char *buf)
{
memset(send_buf,0,BUF_SIZE);
memcpy(send_buf, buf, sizeof(buf));
send(socket_fd, send_buf, strlen(send_buf), 0);
}
void Client::recv_msg()
{
int recv_size = recv(socket_fd, recv_buf, sizeof(recv_buf), 0);
if(recv_size <= 0)
{
cout<<"close connent"<<endl;
//断开之后,重新连接服务器
}
if(recv_size == 8)//进入心跳打印
{
cout<<"recv_size:"<<recv_size<<endl;
cout<<"recv content:"<<recv_buf<<endl;
}
else //处理结构体信息
{
Msg msg;
memset(msg.content, 0 ,sizeof(msg.content));
memcpy(&msg, recv_buf, sizeof(msg));
handle_msg(msg);
}
}
void Client::on_disconnected()
{
//send_thread->join();
//recv_thread->join();
//delete send_thread;
//delete recv_thread;
//send_thread = nullptr;
//recv_thread = nullptr;
close(socket_fd);
}
void Client::send_thread_function()
{
while (isClientwork)
{
memset(key_input_buf,0,BUF_SIZE);
fgets(key_input_buf,BUF_SIZE, stdin);
if(strncasecmp(key_input_buf, EXIT, strlen(EXIT)) == 0 )
{
isClientwork = false;
}
//send_msg(key_input_buf);//发送手动输入信息
send_server_msg();//发送结构体信息
}
}
void Client::recv_thread_function()
{
while (true)
{
recv_msg();
}
}
void Client::heartbeat_send_thread_function() //发送心跳
{
while (isClientwork)
{
unsigned char sendbuf[8] = {0};
sendbuf[0] = 'h';
sendbuf[1] = 'e';
sendbuf[2] = 'a';
sendbuf[3] = 'r';
sendbuf[4] = 't';
send(socket_fd, sendbuf, sizeof(sendbuf), 0);
usleep(500 * 1000);
}
}
void Client::send_server_msg() //制作并发送结构体信息
{
Msg msg;
memset(msg.content, 0 ,sizeof(msg.content));
msg.type = 1111;
msg.fromID = 2222;
msg.toID = 3333;
char send_content[] = "hello this is client send message info";
memcpy(msg.content, send_content, sizeof(send_content));
send(socket_fd, (char *)&msg, sizeof(msg), 0);
}
void Client::handle_msg(Msg &msg) //处理打印结构体信息
{
cout<<"msg.type:"<<msg.type<<endl;
cout<<"msg.fromID:"<<msg.fromID<<endl;
cout<<"msg.toID:"<<msg.toID<<endl;
cout<<"msg.content:"<<msg.content<<endl;
}
client_main.cpp
/*
* @Author: Li Chao
* @Date: 2020-11-03 15:43:07
* @Last Modified by: Li Chao
* @Last Modified time: 2020-11-03 15:44:17
*/
#include "client.h"
int main()
{
Client client;
return 0;
}
Makefile文件
CC = g++
CFLAGS = -std=c++11
all: server_main.cpp client_main.cpp server.o client.o
$(CC) $(CFLAGS) server_main.cpp server.o -o chatroom_server -lpthread
$(CC) $(CFLAGS) client_main.cpp client.o -o chatroom_client -lpthread
server.o: server.cpp server.h
$(CC) $(CFLAGS) -c server.cpp
client.o: client.cpp client.h
$(CC) $(CFLAGS) -c client.cpp
clean:
rm -f *.o chatroom_server chatroom_client