使用C++实现简单的TCP服务器和客户端

使用C++实现简单的TCP服务器和客户端

  • 介绍
  • 准备工作
  • 1. TCP服务器实现
    • 代码结构
    • 解释
  • 2. TCP客户端实现
    • 代码结构
    • 解释
  • 3. 测试
    • 1.编译:
    • 2.运行
  • 结语

介绍

本文将通过一个简单的例子,介绍如何使用C++实现一个基本的TCP服务器和客户端。这个例子展示了如何创建服务器端接收客户端的连接,如何处理接收到的数据,并如何将数据发送回客户端。

我们会分两部分来介绍:

  1. TCP服务器的实现:包括如何创建服务器、监听端口、接收客户端请求等。
  2. TCP客户端的实现:客户端如何连接到服务器、发送数据并接收服务器返回的数据。

准备工作

首先,确保你的开发环境中已经安装了C++编译器(如GCC或Clang)以及支持的标准库。在Linux或类Unix系统上进行开发是最常见的,下面的代码也适用于这种环境。

1. TCP服务器实现

代码结构

首先,来看下TCP服务器的代码实现:

#include <arpa/inet.h>
#include <cstring>
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>
#include <thread>

#define SERVER_PORT 12345
#define BUFFER_SIZE 1024

class TcpServer {
public:
    TcpServer(int port);
    ~TcpServer();
    void Start();
    void Stop();

private:
    void ListenThreadFunc();
    void ReceiveThreadFunc(int clientSocket);
    void CloseListenSocket();
    void CloseDataSocket();

    int mListenSocket;
    int mDataSocket;
    int mPort;
    std::unique_ptr<std::thread> mListenThread;
    bool mRunning;
};

TcpServer::TcpServer(int port) : mListenSocket(-1), mDataSocket(-1), mPort(port), mListenThread(nullptr), mRunning(false) {}

TcpServer::~TcpServer() {
    Stop();
}

void TcpServer::Start() {
    if (mRunning) {
        std::cerr << "Server is already running!" << std::endl;
        return;
    }

    mRunning = true;

    mListenSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (mListenSocket == -1) {
        std::cerr << "Failed to create listen socket!" << std::endl;
        return;
    }

    sockaddr_in serverAddr{};
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(mPort);
    serverAddr.sin_addr.s_addr = INADDR_ANY;

    if (bind(mListenSocket, reinterpret_cast<const sockaddr*>(&serverAddr), sizeof(serverAddr)) == -1) {
        std::cerr << "Failed to bind socket!" << std::endl;
        CloseListenSocket();
        return;
    }

    if (listen(mListenSocket, 5) == -1) {
        std::cerr << "Failed to listen on socket!" << std::endl;
        CloseListenSocket();
        return;
    }

    mListenThread = std::make_unique<std::thread>([this] { ListenThreadFunc(); });
}

void TcpServer::Stop() {
    if (!mRunning) return;

    mRunning = false;
    CloseListenSocket();
    CloseDataSocket();

    if (mListenThread && mListenThread->joinable()) {
        mListenThread->join();
    }
}

void TcpServer::ListenThreadFunc() {
    while (mRunning) {
        sockaddr_in clientAddr{};
        socklen_t len = sizeof(clientAddr);

        int clientSocket = accept(mListenSocket, reinterpret_cast<sockaddr*>(&clientAddr), &len);
        if (clientSocket == -1) {
            std::cerr << "Accept failed!" << std::endl;
            continue;
        }

        std::cout << "Client connected!" << std::endl;

        std::thread receiveThread([this, clientSocket] { ReceiveThreadFunc(clientSocket); });
        receiveThread.detach();
    }
}

void TcpServer::ReceiveThreadFunc(int clientSocket) {
    char buffer[BUFFER_SIZE];
    while (mRunning) {
        memset(buffer, 0, BUFFER_SIZE);
        int res = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
        if (res <= 0) {
            std::cerr << "Receive failed or client disconnected!" << std::endl;
            break;
        }

        std::cout << "Received from client: " << buffer << std::endl;
        send(clientSocket, "Message received", 17, 0);  // Send acknowledgment back
    }

    CloseDataSocket();
}

void TcpServer::CloseListenSocket() {
    if (mListenSocket != -1) {
        close(mListenSocket);
        mListenSocket = -1;
    }
}

void TcpServer::CloseDataSocket() {
    if (mDataSocket != -1) {
        close(mDataSocket);
        mDataSocket = -1;
    }
}

int main() {
    TcpServer server(SERVER_PORT);
    server.Start();

    std::this_thread::sleep_for(std::chrono::minutes(10)); // Server will run for 10 minutes
    server.Stop();

    return 0;
}

解释

  • TcpServer 类:定义了一个 TcpServer 类来处理服务器的启动、停止以及接收客户端请求。
  • Start() 方法:创建一个监听套接字,绑定端口并开始监听。
  • ListenThreadFunc() 方法:负责监听来自客户端的连接请求。
  • ReceiveThreadFunc() 方法:每个客户端连接都会创建一个新的线程来处理数据接收。

2. TCP客户端实现

代码结构

接下来是客户端的实现。客户端需要连接到服务器,发送请求并接收响应。

#include <iostream>
#include <string>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
#include <sys/socket.h>

#define SERVER_PORT 12345
#define SERVER_IP "127.0.0.1"
#define BUFFER_SIZE 1024

int main() {
    int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket == -1) {
        std::cerr << "Create socket failed!" << std::endl;
        return -1;
    }

    sockaddr_in serverAddr{};
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(SERVER_PORT);
    serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);

    if (connect(clientSocket, reinterpret_cast<sockaddr*>(&serverAddr), sizeof(serverAddr)) == -1) {
        std::cerr << "Connect to server failed!" << std::endl;
        close(clientSocket);
        return -1;
    }

    std::cout << "Connected to server!" << std::endl;

    // Send message
    std::string message = "Hello from client!";
    send(clientSocket, message.c_str(), message.size(), 0);

    // Receive response
    char buffer[BUFFER_SIZE];
    ssize_t receivedBytes = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
    if (receivedBytes == -1) {
        std::cerr << "Receive failed!" << std::endl;
    } else if (receivedBytes == 0) {
        std::cerr << "Server closed connection!" << std::endl;
    } else {
        buffer[receivedBytes] = '\0';
        std::cout << "Received from server: " << buffer << std::endl;
    }

    close(clientSocket);
    return 0;
}

解释

  • TcpClient:客户端通过 connect() 与服务器建立连接,使用 send() 发送数据,并通过 recv() 接收服务器的响应。

3. 测试

1.编译:

  • 编译服务器:
g++ TcpServer.cpp -o TcpServer -std=c++17 -lpthread
  • 编译客户端
g++ TcpClient.cpp -o TcpClient -std=c++17

2.运行

  • 启动服务器:
./TcpServer
  • 启动客户端
./TcpClient

结语

通过这篇文章,我们实现了一个简单的 TCP 服务器和客户端示例,学习了如何使用 C++ 来进行网络编程。在实际项目中,你可以根据需求进一步扩展这些功能,比如加入多线程处理、客户端消息队列、认证机制等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/977879.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux内核,slub分配流程

我们根据上面的流程图&#xff0c;依次看下slub是如何分配的 首先从kmem_cache_cpu中分配&#xff0c;如果没有则从kmem_cache_cpu的partial链表分配&#xff0c;如果还没有则从kmem_cache_node中分配&#xff0c;如果kmem_cache_node中也没有&#xff0c;则需要向伙伴系统申请…

冯诺依曼体系结构 ──── linux第8课

目录 冯诺依曼体系结构 关于冯诺依曼&#xff0c;必须强调几点&#xff1a; 冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系 输入单元&#xff1a;包括键盘, 鼠标&#xff0c;网卡,扫…

国标28181协议在智联视频超融合平台中的接入方法

一. 国标28181介绍 国标 28181 协议全称是《安全防范视频监控联网系统信息传输、交换、控制技术要求》&#xff0c;是国内视频行业最重要的国家标准&#xff0c;目前有三个版本&#xff1a; 2011 年&#xff1a;推出 GB/T 28181-2011 版本&#xff0c;为安防行业的前端设备、平…

2024-2025 学年广东省职业院校技能大赛 “信息安全管理与评估”赛项 技能测试试卷(四)

2024-2025 学年广东省职业院校技能大赛 “信息安全管理与评估”赛项 技能测试试卷&#xff08;四&#xff09; 第一部分&#xff1a;网络平台搭建与设备安全防护任务书第二部分&#xff1a;网络安全事件响应、数字取证调查、应用程序安全任务书任务 1&#xff1a;应急响应&…

音乐游戏Dance Dance Revolution(DDR)模拟器

文章目录 &#xff08;一&#xff09;Dance Dance Revolution&#xff08;1.1&#xff09;基本情况&#xff08;1.2&#xff09;机体 &#xff08;二&#xff09;模拟器&#xff08;2.1&#xff09;主程序&#xff08;2.2&#xff09;模拟器主题 &#xff08;三&#xff09;曲谱…

货车一键启动无钥匙进入手机远程启动的正确使用方法

一、移动管家货车无钥匙进入系统的使用方法 基本原理&#xff1a;无钥匙进入系统通常采用RFID无线射频技术和车辆身份识别码识别系统。车钥匙需要随身携带&#xff0c;当车钥匙靠近货车时&#xff0c;它会自动与货车的解码器匹配。开门操作&#xff1a;当靠近货车后&#xff0…

vscode如何使用鼠标滚轮调整字体大小

1.打开设置 2.搜索Font Ligatures 3.编辑配置文件 4.修改代码并保存 修改前 修改后 在最后一行添加&#xff1a;“editor.mouseWheelZoom”: true 记得在上一行最后&#xff0c;加上英文版的“,”逗号 5.配置成功&#xff0c;再次按Ctrl鼠标滚轮便可以缩放了。

视频裂变加群推广分享引流源码

源码介绍 视频裂变加群推广分享引流源码 最近网上很火&#xff0c;很多人都在用&#xff0c;适合引流裂变推广 测试环境&#xff1a;PHP7.4(PHP版本不限制) 第一次访问送五次观看次数&#xff0c;用户达到观看次数后需要分享给好友或者群,好友必须点击推广链接后才会增加观看次…

redis小记

redis小记 下载redis sudo apt-get install redis-server redis基本命令 ubuntu16下的redis没有protected-mode属性&#xff0c;就算sudo启动&#xff0c;也不能往/var/spool/cron/crontabs写计划任务&#xff0c;感觉很安全 #连接到redis redis-cli -h 127.0.0.1 -p 6379 …

IDEA关闭SpringBoot程序后仍然占用端口的排查与解决

IDEA关闭SpringBoot程序后仍然占用端口的排查与解决 问题描述 在使用 IntelliJ IDEA 开发 Spring Boot 应用时&#xff0c;有时即使关闭了应用&#xff0c;程序仍然占用端口&#xff08;例如&#xff1a;4001 端口&#xff09;。这会导致重新启动应用时出现端口被占用的错误&a…

Vue04

自定义指令 directives是Vue的一个配置项 这里写自定义指令 自定义指令被调用的时机 指令与元素成功绑定时 指令所在的模板被重新解析时 函数式 <span v-big"n"></span> directives:{ big(element,binding){ element.innerText bingin…

岳阳市美术馆预约平台(小程序论文源码调试讲解)

第4章 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xff0c;以及让来访用户可以花费更多时间停留在系统上&#xff0c;则表明该系统设计得比较专…

Linux 基本开发工具的使用(yum、vim、gcc、g++、gdb、make/makefile)

文章目录 Linux 软件包管理器 - yum理解什么是软件包和yum如何查看/查找软件包如何安装软件如何实现本地机器和云服务器之间的文件互传如何卸载软件 Linux 编辑器 - vim 的使用vim 的基本概念vim 的基本操作vim 命令模式各命令汇总vim 底行模式各命令汇总vim 的简单配置 Linux …

4部署kibana:5601

kibana 是一个基于浏览器页面的Elasticsearch前端展示工具&#xff0c;, 是一个开源和免费的工具 Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面, 可以帮你汇总、分析和搜索重要数据日志 1.安装-所有的es节点 # tar xf kibana-6.4.1-linux-x86_64.t…

1.介绍一下TCP/IP模型和OSI模型的区别【中高频】

OSI模型 将 这个协议 划分为7个不同的层级&#xff0c;分别为物理层、数据链路层、网络层、传输层、会话层、表示层和应用层&#xff0c;而TCP/IP模型只有4个层级&#xff0c;分别为网络接口层、网络层、传输层和应用层&#xff0c;其中应用层在用户态&#xff0c;传输层及以下…

【反爬】拦截comBusiness.js disable-devtool.js

一、现象 无法使用ctrls保存网页&#xff0c;但是可以在设置菜单中可以保存&#xff1b;无法使用F12和ctrlshifti打开开发者窗口&#xff0c;但是可以在设置菜单中打开&#xff1b;打开开发者窗口后网站快速关闭&#xff0c;说明被检测到了&#xff1b; 二、涉及js 打开设置菜…

【11】子网

区块链子网概述 什么是子网&#xff1f; 子网是在较大网络上下文中运行的较小网络&#xff0c;因此由对应的“主网”&#xff0c;主网即包含多个子网的较大网络或具有隶属关系的上一级网络。子网允许在主网中进行一些独立的事务或控制网络参数。 对于互联网而言&#xff0c;…

爬虫基础入门之爬取豆瓣电影Top250-Re正则的使用

网址:豆瓣电影 Top 250 本案例所需要的模块 requests (用于发送HTTP请求)re (用于字符串匹配和操作) 确定需要爬取的数据 &#xff1a; 电影的名称电影的年份电影的评分电影评论人数 一. 发送请求 模拟浏览器向服务器发送请求 准备工作 -分析页面: F12 or 右击点击检查 查看…

论文笔记(七十二)Reward Centering(五)

Reward Centering&#xff08;五&#xff09; 文章概括摘要附录B 理论细节C 实验细节D 相关方法的联系 文章概括 引用&#xff1a; article{naik2024reward,title{Reward Centering},author{Naik, Abhishek and Wan, Yi and Tomar, Manan and Sutton, Richard S},journal{arX…

Flash-03

1-问题&#xff1a;Flash软件画两个图形&#xff0c;若有部分重合则变为一个整体 解决方法1&#xff1a;两个图形分属于不同的图层 解决方法2&#xff1a;将每个图形都转化为【元件】 问题2&#xff1a;元件是什么&#xff1f; 在 Adobe Flash&#xff08;现在称为 Adobe Anim…