Linux:基于TCP Socket的客户端-服务器实现的远程命令行项目

本文将基于一个完整的项目代码,系统讲解如何使用 C++ 实现一个 TCP 客户端与服务器,并重点说明实现过程中所涉及的核心知识点


一、项目整体结构

本项目采用典型的 客户端 - 服务器模型

  • 客户端:负责连接服务器、发送请求、接收响应

  • 服务器:负责监听连接、处理请求、返回结果

核心模块包括:

复制代码
InetAddr      ------ 网络地址封装
TcpServer     ------ TCP服务器框架
Command       ------ 业务处理逻辑
Client        ------ 客户端程序

二、TCP 编程基础知识

在实现之前,需要掌握以下基础知识:


1. Socket 编程模型

TCP 通信基于 socket,基本流程如下:

服务端流程

复制代码
socket → bind → listen → accept → read/write

客户端流程

复制代码
socket → connect → read/write

2. 网络字节序

网络传输统一使用 大端字节序,因此需要:

  • htons:主机序 → 网络序(端口)

  • ntohs:网络序 → 主机序

  • inet_pton:字符串 IP → 二进制

  • inet_ntop:二进制 → 字符串 IP


3. sockaddr 结构

TCP 使用:

复制代码
struct sockaddr_in

但接口统一使用:

复制代码
struct sockaddr*

因此需要进行类型转换。


三、InetAddr:网络地址封装

为了避免重复操作底层结构,本项目封装了 InetAddr 类。


1. 功能

  • 封装 IP + Port

  • 提供网络序与主机序转换

  • 提供接口给 socket 使用


2. 构造方式

(1)从网络结构构造(服务端 accept)

复制代码
InetAddr(const sockaddr_in &addr)

作用:

  • 提取 IP

  • 转换端口为主机序


(2)客户端构造

复制代码
InetAddr(const std::string& ip, uint16_t port)

作用:

  • 构造用于 connect 的地址结构

(3)服务端构造

复制代码
InetAddr(uint16_t port)

特点:

  • IP 设置为 INADDR_ANY

  • 监听所有网卡


3. 核心接口

复制代码
const struct sockaddr* NetAddrPtr();
const size_t NetAddrLen();

👉 用于 socket API 调用


四、客户端实现

客户端逻辑较为简单,核心代码如下:


1. 创建 socket

复制代码
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  • SOCK_STREAM 表示 TCP

2. 连接服务器

复制代码
connect(sockfd, cilent.NetAddrPtr(), cilent.NetAddrLen());

3. 数据通信

复制代码
write(sockfd, line.c_str(), line.size());
read(sockfd, buffer, sizeof(buffer));

流程:

  1. 从标准输入读取数据

  2. 发送给服务器

  3. 接收服务器返回结果

  4. 输出到终端


4. 关闭连接

复制代码
close(sockfd);

五、TcpServer:服务器实现

服务器是整个项目的核心。


1. 成员变量

复制代码
uint16_t _port;
int _listenSockfd;
bool _isrunning;
func_t _func;

其中:

复制代码
using func_t = std::function<std::string(const std::string&, InetAddr&)>;

👉 表示业务处理函数


2. 初始化(Init)

(1)创建 socket

复制代码
_listenSockfd = socket(AF_INET, SOCK_STREAM, 0);

(2)绑定端口

复制代码
InetAddr local(_port);
bind(_listenSockfd, local.NetAddrPtr(), local.NetAddrLen());

(3)监听

复制代码
listen(_listenSockfd, backlog);

3. 获取连接(accept)

复制代码
int sockfd = accept(_listenSockfd, CONV(peer), &len);

特点:

  • 阻塞等待客户端连接

  • 返回新的通信 socket


4. 请求处理(Service)

复制代码
ssize_t n = read(sockfd, buffer, sizeof(buffer));

处理逻辑:

  • 读取客户端数据

  • 调用业务函数 _func

  • 将结果返回给客户端

    std::string result = _func(buffer, cilent);
    write(sockfd, result.c_str(), result.size());


5. 并发处理(多线程)

为了支持多个客户端同时访问,使用 pthread:


线程数据封装

复制代码
class ThreadData
{
    TcpServer* _tsvr;
    int _sockfd;
    InetAddr _addr;
};

线程入口函数

复制代码
static void* Routine(void* args)

流程:

  1. 分离线程 pthread_detach

  2. 执行 Service

  3. 释放资源


创建线程

复制代码
pthread_create(&tid, nullptr, Routine, td);

👉 每个客户端一个线程


六、业务解耦(回调机制)

服务器不直接处理业务逻辑,而是通过回调函数:

复制代码
std::function<std::string(const std::string&, InetAddr&)>

在 main 中绑定:

复制代码
std::bind(&Command::Execute, &cmd, std::placeholders::_1, std::placeholders::_2)

优点

  • 服务器只负责通信

  • 业务逻辑独立

  • 可扩展性强


七、Command 模块(业务层)

该模块负责处理客户端请求:

复制代码
std::string Execute(const std::string& req, InetAddr& client);

功能可以是:

  • 字符串处理

  • 命令执行

  • 数据查询


八、程序运行流程总结


服务端

复制代码
启动 → 初始化socket → 监听端口 → accept连接 → 创建线程 → 处理请求

客户端

复制代码
启动 → 创建socket → connect服务器 → 输入数据 → 接收响应

九、涉及的核心知识总结

实现本项目需要掌握以下知识:


1. C++ 基础

  • 类与对象

  • 构造函数

  • STL(string、function)

  • 智能指针(unique_ptr)


2. Linux 系统编程

  • socket API

  • 文件描述符

  • read / write

  • close


3. 网络编程

  • TCP 协议

  • 三次握手(建立连接)

  • 四次挥手(断开连接)

  • 网络字节序


4. 多线程

  • pthread

  • 线程创建与分离

  • 并发处理模型


5. 设计思想

  • 封装(InetAddr)

  • 解耦(回调函数)

  • 模块化(TcpServer / Command)

通过这个项目,可以完整理解 TCP 编程从底层 API 到结构设计的全过程。相比只调用接口,这种方式更有助于深入理解网络通信机制,以及服务器程序的基本构建方式

这个实现不仅具备基本通信能力,同时具备良好的扩展性,是进一步学习高性能网络编程的重要基础,接下来我们将学习自定义协议,敬请期待啦

相关推荐
jingyu飞鸟7 小时前
Linux系统发送邮件,解决信誉等级低问题 docker compose修改启动一键使用
linux·运维·docker
Lumos_7777 小时前
Linux -- exec 进程替换
linux·运维·chrome
李白客7 小时前
国产数据库选型指南:从技术路线到实战要点
运维·数据库·数据库架构·迁移学习
数智化精益手记局7 小时前
人员排班管理软件的自动化功能解析:解决传统手工人员进行排班管理耗时长的难题
运维·数据结构·人工智能·信息可视化·自动化·制造·精益工程
路溪非溪7 小时前
网络运输层:TCP协议详解(一)
网络·网络协议·tcp/ip
jy41932177 小时前
VPS 网络质量怎么测?一篇讲清楚多节点 ping、tcping 和回程路由
运维
汽车仪器仪表相关领域7 小时前
Kvaser Leaf Light HS v2 M12:5 针 M12 NMEA 2000 接口,海事与工业 CAN 总线测试的防水耐用之选
大数据·网络·人工智能·功能测试·安全性测试
爱吃芹菜炒肉8 小时前
Chapter 16: Power Management
服务器·c语言·网络·tcp/ip·pcie
wicb91wJ68 小时前
Nginx反向代理与负载均衡配置详解
运维·nginx·负载均衡
ElfBoard8 小时前
飞凌精灵(ElfBoard)技术贴|如何在RK3506开发板上实现UART功能复用
大数据·linux·人工智能·驱动开发·单片机·嵌入式硬件·物联网