学习笔记 c++( socket网络编程,心跳监测,断线重连,结构体发送接收)

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

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值