欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++模拟简单客户端服务端通信-Linux

程序员文章站 2022-06-06 08:30:17
...

客户端:

#include <iostream>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
using namespace std;

//TCP client class
class C_TCP_Client
{
private:
    int client_socket; //客户端套接字
public:
    C_TCP_Client(); //默认构造函数
    bool C_Connect(string, int); //建立连接函数
    bool C_Recv(char *buf, int size); //接受数据函数
    bool C_Send(char *buf, int size); //发送数据函数
    ~C_TCP_Client(); //析构函数
};

C_TCP_Client::C_TCP_Client()
{
    client_socket = 0;
}

bool C_TCP_Client::C_Connect(string server_ip, int server_port)
{
    //建立套接字
    client_socket = socket(AF_INET, SOCK_STREAM, 0);
    if(client_socket == -1)
    {
        perror("socket");
        return false;
    }

    //解析服务器域名
    struct hostent* server_host = nullptr;
    server_host = gethostbyname(server_ip.c_str());
    if(server_host == nullptr)
    {
        cout << "gethostbyname failed\n";
        return false;
    }

    //建立服务器信息结构体
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(server_port);
    memcpy(&server_addr.sin_addr, server_host->h_addr, server_host->h_length); //IP地址的赋值

    //建立连接
    if(connect(client_socket,(struct sockaddr*)&server_addr,sizeof(server_addr)) != 0)
    {
        perror("connect");
        return false;
    }
    return true;
}

bool C_TCP_Client::C_Recv(char * buf, int size)
{
    int iret;
    memset(buf, 0, size);
    iret = recv(client_socket, buf, size, 0);
    if(iret <= 0)
    {
        return false;
    }
    return true;
}

bool C_TCP_Client::C_Send(char *buf, int size)
{
    int iret;
    iret = send(client_socket, buf, size, 0);
    if(iret <= 0)
    {
        return false;
    }
    return true;
}

C_TCP_Client::~C_TCP_Client()
{
    close(client_socket);
}

int main()
{
    string server_ip;
    int server_port;
    cout << "server_ip:";
    cin >> server_ip;
    cout << "server_port:";
    cin >> server_port;
    C_TCP_Client client_socket;
    //建立连接
    if(!client_socket.C_Connect(server_ip, server_port))
    {
        //失败返回
        return -1;
    }

    //发送数据,客户端发送exit退出
    char buf[1024];
    while(1)
    {
        memset(buf, 0, sizeof(buf));
        cin >> buf;
        client_socket.C_Send(buf, strlen(buf));
        if(memcmp(buf, "exit", 4) == 0)
        {
            break;
        }
        client_socket.C_Recv(buf, sizeof(buf));
        cout << "接收:" << buf << endl;
    }
    return 0;
}

服务端:

#include <iostream>
#include <string.h>
#include <unistd.h> //进程
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
using namespace std;

class C_TCP_Server
{
private:
    int listen_socket;
    int client_socket;
public:
    C_TCP_Server(); //默认构造函数
    bool Listen(int); //监听函数
    bool Accept(); //建立连接函数
    bool Recv(char*, int); //接收数据函数
    bool Send(char*, int); //发送数据函数
    bool CloseClientSocket(); //关闭服务端中与客户端连接的套接字
    bool CloseListenSocket(); //关闭服务端中用于监听的套接字
    ~C_TCP_Server(); //析构函数
};

C_TCP_Server::C_TCP_Server()
{
    listen_socket = 0;
    client_socket = 0;
}

bool C_TCP_Server::Listen(int port)
{
    //监听套接字一旦监听后就一直运行着
    //是否有连接请求是看accept函数是否接收到连接请求
    //也就是说监听和连接应该设计成两个独立的函数

    // 初始化监听套接字
    if(listen_socket != 0)
    {
        close(listen_socket);
        listen_socket = 0;
    }

    //建立监听套接字
    listen_socket = socket(AF_INET, SOCK_STREAM, 0);
    if(listen_socket == -1)
    {
        perror("socket");
        return false;
    }

    //初始化sockaddr结构体
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    //将监听端口号与服务端信息结构体绑定
    if(bind(listen_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) != 0)
    {
        perror("bind");
        close(listen_socket);
        return false;
    }

    //开始监听
    if(listen(listen_socket, 5) != 0)
    {
        perror("listen");
        close(listen_socket);
        return false;
    }
    return true;
}

bool C_TCP_Server::Accept()
{
    //当受到连接请求时,建立连接

    //建立客户端的结构体
    /*这个结构体是保存连接上来的客户端的套接字信息,如果程序不需要这些信息,可以不设置*/
    if((client_socket = accept(listen_socket, 0, 0)) <= 0)
    {
        perror("accept");
        return false;
    }
    return true;
}

bool C_TCP_Server::Recv(char *buf, int size)
{
    int iret;
    memset(buf, 0, size);
    if((iret = recv(client_socket, buf, size, 0)) <= 0)
    {
        return false;
    }
    return true;
}

bool C_TCP_Server::Send(char *buf, int size)
{
    int iret;
    if((iret = send(client_socket, buf, size, 0)) <= 0)
    {
        return false;
    }
    return true;
}

bool C_TCP_Server::CloseClientSocket()
{
    close(client_socket);
    client_socket = 0;
    return true;
}

bool C_TCP_Server::CloseListenSocket()
{
    close(listen_socket);
    listen_socket = 0;
    return true;
}

C_TCP_Server::~C_TCP_Server()
{
    close(listen_socket);
    close(client_socket);
}

int main()
{
    signal(SIGCHLD, SIG_IGN); //忽略子进程退出的信号,避免产生僵尸进程
    //开始监听
    C_TCP_Server server_socket;
    int port;
    cout << "port:";
    cin >> port;
    //初始化并且开始监听
    if(!server_socket.Listen(port))
    {
        //初始化或者监听过程中出现错误
        return -1;
    }

    //开始检测连接
    while(1)
    {
        if(server_socket.Accept() == false)
        {
            //没有接收到连接请求或者达到最大连接数量,继续循环
            return 0;
        }

        //有连接,新建进程
        int flg = fork();
        if(flg > 0)
        {
            // fork返回值大于0是父进程,等于0是子进程,小于0是失败
            // 父进程关闭client_socket,并继续等待接收连接
            server_socket.CloseClientSocket();
            continue;
        }

        //下面的代码只有子进程才能达到

        //子进程只负责通信,不负责监听,所以关闭监听套接字,虽然这里还是使用server_socket,但是这个和if里面的server_socket是不一样的,因为开辟进程,所有信息都一样,所以变量名字也是重复的
        server_socket.CloseListenSocket();
        cout << "客户端已经连接" << endl;

        //开始通信,接收数据,发送收到,如果接收的数据是exit,退出客户端
        char buf[1024];
        while(1)
        {
            memset(buf, 0, sizeof(buf));
            server_socket.Recv(buf, sizeof(buf));
            cout << "服务端接收到数据:" << buf << endl;
            if(memcmp(buf, "exit", 4) == 0)
            {
                strcpy(buf, "服务端已经断开连接");
                server_socket.Send(buf, strlen(buf));
                break;
            }
            strcpy(buf, "服务端接收到你的数据");
            server_socket.Send(buf, strlen(buf));
        }
        cout << "客户端已经断开连接" << endl;
        return 0;
    }
}