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;
}

运行结果

相关推荐
呉師傅1 小时前
得力P2500、M2500系列打印机硒鼓加粉及清零方法【纯享版】
运维·网络·windows·电脑
大侠归来2 小时前
TCP/IP 网络编程:多播与广播
网络·tcp/ip·智能路由器
weixin_416667963 小时前
银河麒麟V10看门狗试验-1
网络·chrome·python
白帽攻防录3 小时前
SRC 挖洞:WSO2 JWT 算法混淆绕过深度复盘,CVE-2026-5430 不支持的算法怎么变成管理员
网络·算法·安全·网络安全·jwt
starzy19903 小时前
Flink 优化之网络缓存优化及参数详解:从数据传输机制到反压原理的生产调优指南
网络·缓存·flink
正经教主4 小时前
【FDE系列】阶段3:Day 58:Prompt 安全 — 注入、越狱与防护
网络·人工智能·安全·prompt·fde
实点科技5 小时前
实点科技IO-Link I/O IOL7A/B系列参数配置方法介绍
网络
梦帮科技6 小时前
LoRA / QLoRA 低秩矩阵流形更新:SVD 分解、秩与缩放因子 α 的物理意义与梯度稳定性保障
网络·深度学习·神经网络·线性代数·矩阵·架构·数据挖掘
阿钱真强道6 小时前
28 嵌入式操作系统 | 网关加 TCP 命令通道:粘包/半包怎么切
网络·网络协议·tcp/ip·粘包
HY小宝F7 小时前
树莓派智能摄像头系统 30 天重学计划之 Day 03 · 网络基础:建立“包“的思维 —— 学习总结
网络·学习