socket编程实现简单的TCP网络程序(下)

2023-11-20

socket编程实现简单的TCP网络程序(下)

1.封装TCP socket
#include <iostream>
#include <string>
#include <stdio.h>
#include <cassert>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define CHECK_RET(q) if(!(q)) {return false;}

class TcpSocket {
    public:
        TcpSocket() : _fd(-1) {}
        TcpSocket(int fd) :_fd(fd) {}
        ~TcpSocket(){}
        //绑定地址信息-IPV4协议,字节流传输
        bool Socket() {
            _fd = socket(AF_INET, SOCK_STREAM, 0);
            if (_fd < 0) {
                perror("socket error~~\n");
                return false;
            }
            return true;
        }
        //关闭套接字
        bool Close() {
            int ret = close(_fd);
            if (ret < 0) {
                perror("close error~~\n");
                return false;
            }
            return true;
        }
        //为套接字绑定地址信息,地址为一个通用的结构体sockaddr
        //结构体包含了协议类型,ip地址,port端口号
        bool Bind(std::string& ip, uint16_t port) {
            sockaddr_in addr;
            addr.sin_family = AF_INET;
            //inet_addr点分十进制字符串转换为网络ip(本质上是一个32字节数据)
            addr.sin_addr.s_addr = inet_addr(ip.c_str());
            //htons主机字节序转换为网络字节序
            addr.sin_port = htons(port);
            int ret = bind(_fd, (sockaddr*)&addr, sizeof(addr));
            if (ret < 0) {
                perror("bind error~~\n");
                return false;
            }
            return true;
        }
        //开始监听,num=5 最多允许5个客户端处于连接等待状态
        bool Listen(int num = 5) {
            int ret = listen(_fd, num);
            if (ret < 0) {
                perror("listen error~~\n");
                return false;
            }
            return true;
        }
        //监听套接字用于接受连接请求,建立连接后会accept返回一个新的套接字
        //这个套接字就是客户端建立的套接字,是真正用于进行通信的套接字
        //而监听套接字不用于进行通信
        //同时可以接收到对端的地址信息
        bool Accept(TcpSocket& peer, std::string* ip = nullptr, uint16_t* port = nullptr) {
            sockaddr_in addr;
            socklen_t len = sizeof(addr);
            int new_sock = accept(_fd, (sockaddr*)&addr, &len);
            if (new_sock < 0) {
                perror("accept error~~\n");
                return false;
            }
            peer._fd = new_sock;
            if (ip != nullptr) {
                *ip = inet_ntoa(addr.sin_addr);
            }
            if (port != nullptr) {
                *port = ntohs(addr.sin_port);
            }
            return true;
        }
        //请求建立连接,客户端使用
        bool Connect(std::string& ip, uint16_t port) {
            sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = inet_addr(ip.c_str());
            addr.sin_port = htons(port);
            int ret = connect(_fd, (sockaddr*)&addr, sizeof(addr));
            if (ret < 0) {
                perror("connect error~~\n");
                return false;
            }
            return true;
        }
        //发送消息
        bool Send(std::string& buf) {
            int ret = send(_fd, buf.c_str(), buf.size(), 0);
            if (ret < 0) {
                perror("send error~~\n");
                return false;
            }
            return true;
        }
        //接收消息
        bool Recv(std::string& buf) {
            char tmp[1024*4] = {0};
            int len = recv(_fd, tmp, sizeof(tmp)-1, 0);
            if (len < 0) {
                perror("recv error~~\n");
                return false;
            }
            if (len == 0) {
                return false;
            }
            buf.assign(tmp, len);
            return true;
        }
        int GetFd() {
            return _fd;
        }
    private:
        int _fd;
};

2.TCP通用服务器
#include "tcp_socket.hpp"
#include <functional>

typedef std::function<void (const std::string& req, std::string& resp)> Handler;

class TcpServer {
    public:
        TcpServer(std::string& ip, uint16_t port)
            : _ip(ip)
            , _port(port)
        {}
        ~TcpServer() {}

        bool Start(Handler handler) {
            //1.创建套接字
            CHECK_RET(sock.Socket());
            //2.绑定地址信息
            CHECK_RET(sock.Bind(_ip, _port));
            //3.开始监听
            CHECK_RET(sock.Listen());
            //4.进入事件循环
            while (true) {
                //新的套接字,用于接受客户端的套接字
                TcpSocket new_sock;
                std::string ip;
                uint16_t port;
                //5.阻塞建立新连接
                if(!sock.Accept(new_sock, &ip, &port))
                    continue;
                printf("[ip-%s][port-%d] --> connect~~\n", ip.c_str(), port);
                
                //6.对新建立的连接循环读写
                while (true) {
                    //7.接收请求,失败跳出循环并且关闭新的连接
                    std::string req;
                    bool ret = new_sock.Recv(req);
                    if (!ret) {
                        printf("[ip-%s][port-%d] --> disconnect~~\n", ip.c_str(), port);
                        break;
                    }
                    std::cout << "请求:" << req << std::endl;

                    //8.计算响应
                    std::string resp;
                    handler(req, resp);

                    //9.发送响应
                    new_sock.Send(resp);
                }
                new_sock.Close();
            }
            return true;
        }
    private:
        TcpSocket sock;
        std::string _ip;
        uint16_t _port;
};

3.英译汉服务器
#include "tcp_socket.hpp"

class TcpClient {
    public:
        TcpClient(std::string& ip, uint16_t port) 
            : _ip(ip)
            , _port(port) {
            assert(sock.Socket());
        }
        ~TcpClient() {
            assert(sock.Close());
        }
        
        bool Connect() {
            return sock.Connect(_ip, _port);
        } 
        bool Recv(std::string& req) {
            return sock.Recv(req);
        }
        bool Send(std::string& resp) {
            return sock.Send(resp);
        }
    private:
        TcpSocket sock;
        std::string _ip;
        uint16_t _port;
};


4.TCP通用客户端
#include "tcp_server.hpp"
#include <unordered_map>

void Dict(const std::string& req, std::string& resp) {
    std::unordered_map<std::string, std::string> dic;
    dic.insert(std::make_pair("hello", "你好~~"));
    dic.insert(std::make_pair("world", "世界~~"));
    dic.insert(std::make_pair("shen", "珅~~"));
    if (dic.find(req) != dic.end()) {
        auto it = dic.find(req);
        resp = it->second;
    } else {
        resp = "未找到~~";
    }
}

// ./dict_server 127.0.0.1 9000
int main(int argc, char* argv[]) {
    if (argc != 3) {
        std::cout << "./dict_server 127.0.0.1 9000" << std::endl;
        return -1;
    }
    std::string ip = argv[1];
    uint16_t port = atoi(argv[2]);

    TcpServer server(ip, port);
    server.Start(Dict);
    
    return 0;
}

5.英译汉客户端
#include "tcp_client.hpp"

// ./dict_client 127.0.0.1 9000
int main(int argc, char* argv[]) {
    if (argc != 3) {
        std::cout << "./dict_client 127.0.0.1 9000" << std::endl;
        return -1;
    }
    std::string ip = argv[1];
    uint16_t port = atoi(argv[2]);

    TcpClient client(ip, port);
    client.Connect();
    
    while (true) {
        std::string req;
        std::cout << "请求:";
        std::cin >> req;
        client.Send(req);

        std::string resp;
        client.Recv(resp);
        std::cout << "响应:" << resp << std::endl;
    }
    return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

socket编程实现简单的TCP网络程序(下) 的相关文章

随机推荐