Socket--TCP

相比于UDP,TCP以字节流为传输模式,面向连接且更为可靠。就像打电话一样:

当确定有TCP通讯即将发生时,需要先将服务端设为监听(listen)状态告诉其准备干活接受客户端的连接;随后服务端会(accept)陷入阻塞状态,等待客户端(connect)发起连接

这三个函数详情可见之前的文章,或者ai,找资料。

由于TCP的实现代码跟UDP的很像,仅需注意服务端监听、阻塞等待和客户端的过程,所以下面就直接放代码:

TCPServer.hpp

复制代码
#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include "InetAddr.hpp"
#include "Logger.hpp"

using namespace NS_LOG_MODULE;

enum
{
    SUCCESS = 0,
    USAGE_ERR,
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR,
};

static const int gbacklog = 16;
static const uint16_t gport = 8888;

class TcpServer
{
public:
    TcpServer(uint16_t port = gport) : _port(port)
    {
    }
    void InitServer()
    {
        signal(SIGPIPE, SIG_IGN);

        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error";
            exit(SOCKET_ERR);
        }

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        if (bind(_listensockfd, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            LOG(LogLevel::FATAL) << "bind error";
            exit(BIND_ERR);
        }

        if (listen(_listensockfd, gbacklog) < 0)
        {
            LOG(LogLevel::FATAL) << "listen error";
            exit(LISTEN_ERR);
        }
        LOG(LogLevel::DEBUG) << "server init success";
    }

    void serviceIO(int sockfd, InetAddr address)
    {
        char inbuffer[1024] = {0};
        while (true)
        {
            ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
            if (n > 0)
            {
                inbuffer[n] = 0;
                LOG(LogLevel::INFO) << address.ToString() << " say# " << inbuffer;

                std::string echo = "server echo# ";
                echo += inbuffer;
                write(sockfd, echo.c_str(), echo.size());
            }
            else if (n == 0)
            {
                LOG(LogLevel::INFO) << "client quit";
                break;
            }
            else
            {
                LOG(LogLevel::ERROR) << "read error";
                break;
            }
        }
        close(sockfd);
    }

    void Start()
    {
        while (true)
        {
            struct sockaddr_in clientaddr;
            socklen_t len = sizeof(clientaddr);
            int sockfd = accept(_listensockfd, (struct sockaddr *)&clientaddr, &len);
            if (sockfd < 0)
            {
                continue;
            }
            InetAddr addr(clientaddr);
            serviceIO(sockfd, addr);
        }
    }

    ~TcpServer()
    {
        close(_listensockfd);
    }

private:
    uint16_t _port;
    int _listensockfd;
};

TCPClient.cc

复制代码
#include <iostream>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "InetAddr.hpp"

static void Usage(const std::string &name)
{
    std::cerr << "Usage:\n\t";
    std::cerr << name << " server_ip server_port" << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    std::string server_ip = argv[1];
    uint16_t server_port = std::stoi(argv[2]);

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        std::cerr << "socket error" << std::endl;
        exit(2);
    }

    InetAddr serveraddress(server_port, server_ip);
    int n = connect(sockfd, (struct sockaddr *)serveraddress.GetNetAddress(), serveraddress.Len());
    if (n < 0)
    {
        std::cerr << "connect to " << serveraddress.ToString() << " failed!" << std::endl;
        exit(3);
    }
    std::cerr << "connect to " << serveraddress.ToString() << " success!" << std::endl;


    while (true)
    {
        std::string line;
        std::cout << "please Enter# ";
        std::getline(std::cin, line);

        write(sockfd, line.c_str(), line.size());

        char inbuffer[1024];
        ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer));
        if (n > 0)
        {
            inbuffer[n] = 0;
            std::cout << inbuffer << std::endl;
        }
        else if (n == 0)
        {
            std::cout << "read enf of file!" << std::endl;
            break;
        }
        else
        {
            std::cerr << "read error!" << std::endl;
            break;
        }
    }

    return 0;
}

ChatTCPMain.cc

复制代码
#include "TCPServer.hpp"
#include <memory>

static void Usage(const std::string &process)
{
    std::cerr << "Usage:\n\t";
    std::cerr << process << " local_port" << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    ENABLE_CONSOLE_LOG_STRATEGY();
    uint16_t server_port = std::stoi(argv[1]);

    std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(server_port);
    tsvr->InitServer();
    tsvr->Start();

    return 0;
}

运行结果

相关推荐
sdghterhd4 分钟前
WebSocket 快速入门教程(附示例源码)
网络·websocket·网络协议
xy34531 小时前
Wireshark捕获过滤器——语法元素详解
网络·测试工具·渗透测试·wireshark
DLYSB_2 小时前
生命通道:如何用 HIS 医疗系统直连网络声光终端,打造“零延误”的危急值响应网关?
java·网络·数据库·报警灯
德福危险3 小时前
富有想象力的XSS盲打:靶机练习之Tempus_fugit5
服务器·网络·xss
哥是强混4 小时前
Means:基于 .NET 10 打造的开源自部署 S3 兼容对象存储服务
网络·数据库·.net
Piko6145 小时前
锐捷 VSU 虚拟化堆叠配置
运维·网络·笔记
爱研究的小梁6 小时前
乾元通多链路聚合通信主流落地应用案例
网络·信息与通信
Bobolink_7 小时前
跨境网络丢包优化实践,从5%到0.2%的排查与调整过程
网络·跨境网络·丢包优化
Piko6147 小时前
锐捷交换机 DHCP Server部署
运维·网络·笔记
多巴胺梦想家7 小时前
数据库恢复技术:当灾难来临
网络·数据库·oracle