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 小时前
CTF密码学综合教学指南--第九章
开发语言·网络·python·安全·网络安全·密码学·ctf
xlq223222 小时前
50.UDP套接字
网络·网络协议·udp
南境十里·墨染春水2 小时前
linux学习笔记 网络编程——Socket入门与TCP客户端/服务器实现
linux·服务器·网络
qq_三哥啊3 小时前
【mitmproxy】通过 mitmproxy 的HTTP代理模式获取 OpenCode 发起的 AI API 请求的详细信息
网络·http·代理模式
nikolay3 小时前
AI重塑企业信息安全:攻防升级与信任重构
网络·人工智能·网络安全
Yupureki4 小时前
《Linux网络编程》6.UDP原理
linux·运维·服务器·网络·udp
wapicn994 小时前
设置好这一步,让你的SSL证书在到期前自动续期,永不过期
网络·网络协议·ssl
Harvy_没救了5 小时前
【网络运维】 WordPress 部署复盘
运维·网络
笨笨饿5 小时前
#79_NOP()嵌入式C语言中内联汇编宏的抽象封装模式研究
linux·c语言·网络·驱动开发·算法·硬件工程·个人开发
孙同学_5 小时前
一文带你了解:从浏览器发起HTTP请求到得到网页的整个过程
网络·网络协议·http