【计算机网络】TCP网络程序

embedded/2024/11/18 19:45:45/

一、服务端

1.tcpServer.hpp

此文件负责实现一个tcp服务器

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>// 错误码
enum
{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,LISTEN_ERR
};static const uint16_t gport = 8080; // 默认端口号
static const int gbacklog = 5; // listen第二个参数// TCP服务器
class TcpServer
{
public:// 构造函数TcpServer(const uint16_t &port = gport): _listensock(-1), _port(port){}// 析构函数~TcpServer() {}// 初始化服务器void initServer()// 启动服务器void start()// 处理IOvoid serviceIO(int sock)private:int _listensock; // 不是用来进行数据通信的,他是用来监听链接到来,获取新链接的!uint16_t _port;  // 端口号
};

(1)initServer()

void initServer()
{// 1.创建socket文件套接字对象_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){logMessage(FATAL, "create socket error");exit(SOCKET_ERR);}logMessage(NORMAL, "create socket success:%d", _listensock);// 2.bind绑定自己的网络信息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(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0){logMessage(FATAL, "bind socket error");exit(BIND_ERR);}logMessage(NORMAL, "bind socket success");// 3.设置socket 为监听状态if (listen(_listensock, gbacklog) < 0){logMessage(FATAL, "listen socket error");exit(LISTEN_ERR);}logMessage(NORMAL, "listen socket success");
}

(2)start()

void start()
{for (;;){// 4.server 获取新链接// sock才是和client进行通信的fdstruct sockaddr_in peer;socklen_t len = sizeof(peer);int sock = accept(_listensock, (struct sockaddr *)&peer, &len);if (sock < 0){logMessage(ERROR, "accept error, next");continue;}logMessage(NORMAL,"accept a new link success, get new sock:%d",sock);// 5.未来通信我们就用这个sock,面向字节流的,后续全部都是文件操作!serviceIO(sock);close(sock); // 要关闭使用完的sock,要不然会导致文件描述符泄露}
}

(3)serviceIO()

void serviceIO(int sock)
{char buffer[1024]; // 读到的数据放这里while (true){// 读数据(接收数据)ssize_t n = read(sock, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;std::cout << "receive message: " << buffer << std::endl;std::string outbuffer = buffer;// 写数据(发送数据 -> 客户端)write(sock, outbuffer.c_str(), outbuffer.size());}else if (n == 0){// 代表client退出logMessage(NORMAL, "client quit");break;}}
}

2.tcpServer.cpp

此文件负责tcp服务器的调用逻辑

#include "tcpServer.hpp"
#include <memory>using namespace std;// 使用手册
static void Usage(string proc)
{cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}// ./tcpServer local_port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);unique_ptr<TcpServer> tsvr(new TcpServer(port));tsvr->initServer();tsvr->start();return 0;
}

二、客户端

1.tcpClient.hpp

此文件负责实现一个tcp客户端

#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>         
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#define NUM 1024class TcpClient
{
public:// 构造函数TcpClient(const std::string &serverip, const uint16_t &serverport):_sock(-1), _serverip(serverip), _serverport(serverport){}// 析构函数~TcpClient(){if(_sock >= 0)close(_sock);}// 初始化客户端void initClient()// 启动客户端void start()private:int _sock;std::string _serverip;uint16_t _serverport;
};

(1)initClient()

void initClient()
{// 1.创建socket_sock = socket(AF_INET, SOCK_STREAM, 0);if (_sock < 0){std::cerr << "socket create error" << std::endl;exit(2);}// 2.客户端不需要自己bind,让OS来bind// 3.要不要listen?不要!// 4.要不要accept?不要!
}

(2)start()

void start()
{// 5.要发起连接!struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(_serverport);server.sin_addr.s_addr = inet_addr(_serverip.c_str());if (connect(_sock, (struct sockaddr *)&server, sizeof(server)) != 0){std::cerr << "socket connet error" << std::endl;}else{// 成功连接std::string msg;while (true){std::cout << "Enter# ";std::getline(std::cin, msg);write(_sock, msg.c_str(), msg.size());char buffer[NUM];int n = read(_sock, buffer, sizeof(buffer) - 1);if (n > 0){// 目前我们可以吧读到的数据当成字符串buffer[n] = 0;std::cout << "Server回显# " << buffer << std::endl;}else{break;}}}
}

2.tcpClient.cpp

此文件负责tcp客户端的调用逻辑

#include "tcpClient.hpp"
#include <memory>// 使用手册
static void Usage(std::string proc)
{std::cout << "\nUsage:\n\t" << proc << " serverip serverport\n\n";
}// ./tcpclient serverip serverport
int main(int argc, char *argv[])
{if(argc != 3){Usage(argv[0]);exit(1);}std::string serverip = argv[1];uint16_t serverport = atoi(argv[2]);std::unique_ptr<TcpClient> tcli(new TcpClient(serverip, serverport));tcli->initClient();tcli->start();return 0;
}

三、整体调用逻辑

1.双方调用逻辑

上面写的TCP服务器还不支持多个客户同时访问,分析如图

引入多线程实现多个客户同时访问

2.调整对应代码

(1)新增线程数据

class TcpServer;// 线程数据
class ThreadData
{
public:ThreadData(TcpServer *self, int sock): _self(self), _sock(sock){}public:TcpServer *_self;int _sock;
};

(2)线程要执行的函数

// 多线程版 需要的函数
static void *threadRoutine(void *args)
{// 线程分离 -> 主进程不用在等待pthread_detach(pthread_self());// 通过传参拿到线程数据ThreadData *td = static_cast<ThreadData *>(args);// 进行IOtd->_self->serviceIO(td->_sock);close(td->_sock);delete td;return nullptr;
}

(3)调整服务器start()函数

void start()
{for (;;){// 4.server 获取新链接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sock = accept(_listensock, (struct sockaddr *)&peer, &len);if (sock < 0){logMessage(ERROR, "accept error, next");continue;}logMessage(NORMAL, "accept a new link success, get new sock:%d", sock);// 5.未来通信我们就用这个sockpthread_t tid;ThreadData *td = new ThreadData(this, sock);pthread_create(&tid, nullptr, threadRoutine, (void *)td);}
}

3.展示运行结果

​​​​​​​


http://www.ppmy.cn/embedded/138616.html

相关文章

关于mysql中的锁

mysql中包含的锁分为&#xff1a; 一、全局锁 二、表锁 三、行锁 一、全局锁 全局锁的力度是最大的&#xff0c;全局锁对整个数据库实例加锁&#xff0c;加锁后整个实例就处于只读状态&#xff0c;后续的DML的写语句&#xff0c;DDL语句&#xff0c;已经更新操作的事务提交语句…

SQL注入注入方式(大纲)

SQL注入注入方式&#xff08;大纲&#xff09; 常规注入 通常没有任何过滤&#xff0c;直接把参数存放到SQL语句中。 宽字节注入 GBK 编码 两个字节表示一个字符ASCII 编码 一个字节表示一个字符MYSQL默认字节集是GBK等宽字节字符集 原理&#xff1a; 设置MySQL时错误配置…

MATLAB用CNN-LSTM神经网络的语音情感分类深度学习研究

全文链接&#xff1a;https://tecdat.cn/?p38258 在语音处理领域&#xff0c;对语音情感的分类是一个重要的研究方向。本文将介绍如何通过结合二维卷积神经网络&#xff08;2 - D CNN&#xff09;和长短期记忆网络&#xff08;LSTM&#xff09;构建一个用于语音分类任务的网络…

TKinter实现与Dash应用的同步启停控制

使用Python集成Tkinter和Dash&#xff1a;创建交互式数据可视化应用 在数据可视化项目中&#xff0c;我们经常需要结合传统GUI和现代Web可视化框架的优势。本文将介绍如何整合Tkinter和Dash&#xff0c;创建一个既有桌面应用界面&#xff0c;又能展示交互式图表的应用程序。 …

Spring纯注解开发

在我的另一篇文章中&#xff08;初识Spring-CSDN博客&#xff09;&#xff0c;讲述了Bean&#xff0c;以及通过xml方式定义Bean。接下来将讲解通过注解的方法管理Bean。 我们在创建具体的类的时候&#xff0c;可以直接在类的上面标明“注解”&#xff0c;以此来声明类。 1. 常…

MoneyPrinterTurbo - AI自动生成高清短视频

MoneyPrinterTurbo是一款基于AI大模型的开源软件&#xff0c;旨在通过一键操作帮助用户自动生成高清短视频。只需提供一个视频 主题或 **关键词** &#xff0c;就可以全自动生成视频文案、视频素材、视频字幕、视频背景音乐&#xff0c;然后合成一个高清的短视频。 ​ ​ 主要…

Vue中template模板报错

直接<v出现如下模板&#xff0c;出现如下错误 注意两个地方&#xff1a; 1.template里面加一个div标签 2.要写name值 如下图

[C++] 异常

文章目录 异常的概念异常的抛出与捕获栈展开&#xff08;Stack Unwinding&#xff09;四、总结 查找匹配的处理代码 异常的重新抛出三、模拟示例&#xff1a;服务模块中的异常处理四、总结 C 异常规范详解一、C98异常规范二、C11及其后的异常规范 (noexcept)三、使用noexcept的…