【计网】从零开始使用TCP进行socket编程 --- 客户端与服务端的通信实现

从零开始使用TCP进行socket编程
- 1 TCP与UDP
- 2 TCP服务器类
- 2.1 TCP基础知识
- 2.2 整体框架设计
- 2.3 初始化接口
- 2.4 循环接收接口与服务接口
- 3 服务端与客户端
- 测试运行
1 TCP与UDP
我们之前实现了UDP协议下的客户端与服务端的通信。
UDP(用户数据报协议)和TCP(传输控制协议)都是网络通信中常用的传输层协议,它们在数据传输的方式和特性上存在以下特点:
- TCP 是面向连接的协议,意味着在数据传输之前,必须先建立一个连接,完成握手过程。这个连接在数据传输结束之后需要被断开。
- TCP 提供了可靠的服务。它确保数据包的顺序传输,并且通过确认(ACK)和重传机制保证数据的可靠性。
- TCP 因为需要建立连接、保证数据顺序和可靠性,所以传输速度相对较慢。
- TCP 将数据视为一个连续的数据流,确保数据按照发送的顺序到达。
- TCP 适用于要求高可靠性的应用,如网页浏览、文件传输(FTP)、电子邮件(SMTP)等。
- TCP 头部较大,因为它需要包含更多的信息来管理连接状态和保证数据的可靠性。
- UDP 是无连接的,它发送数据之前不需要建立连接,每个数据报文都是一个独立的信息传输单位。
- UDP 不保证数据包的顺序,也不保证数据包的可靠性。如果数据在传输过程中丢失,UDP不会进行重传。
- UDP 由于无需建立连接和保证可靠性,通常用于对实时性要求较高的应用,如视频会议和在线游戏,传输速度较快。
- UDP 将数据视为独立的、离散的数据包(datagrams),每个数据包独立处理,可能以不同的顺序到达。
- UDP 头部较小,处理起来更为高效。
- UDP 适用于实时性要求高的应用,如流媒体、实时视频会议(VoIP)、在线游戏等。
通俗理解的话:TCP的传输过程类似管道,数据从一端发送,然后在另一端按顺序接收。UDP传输数据的过程类似送快递,数据报文会一股脑包装在一起发送给接收者!
2 TCP服务器类
2.1 TCP基础知识
• socket()打开一个网络通讯端口,如果成功的话,就像 open()一样返回一个文件描述符;
• 应用程序可以像读写文件一样用 read / write 在网络上收发数据,通过流来进行读取写入!
• 如果 socket()调用出错则返回-1;
• 对于 IPv4, family 参数指定为 AF_INET;
• 对于 TCP 协议,type 参数指定为 SOCK_STREAM, 表示面向流的传输协议
• protocol 参数的介绍从略,指定为 0 即可。
2.2 整体框架设计
下面我们就来设计一下TCP协议下的服务器类:
- 成员变量需要整体通信的_listensockfd和端口号_port,后续绑定网络通信接口,从中读取连接流。
- 初始化接口InitServer:对端口号进行绑定,将网络通信接口设置为"接听"模式,可以获取外部的链接。
- 循环读取接口Loop:从网络通信接口获取连接流与发送者的信息,之后进行数据接收。
- 服务端口Service:根据获取的连接流和发送者的信息开始读取接收数据
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include <memory>
#include <string>
#include <cstring>
#include <iostream>
#include <functional>
#include <unistd.h>#include "Log.hpp"
#include "InetAddr.hpp"using namespace log_ns;
//基础信息
const int gport = 8888;
const int gblocklog = 8;
//错误码
enum
{SOCKET_FD = 1,SOCKET_BIND,SOCKET_LISTNE
};class TcpServer
{
public:TcpServer(int port = gport) : _port(port),_listensockfd(-1),_isrunning(false){}// 进行初始化void InitServer(){ }void Loop(){}void Service(int sockfd, InetAddr addr){}~TcpServer(){}private:uint16_t _port; // 服务器端口int _listensockfd; // 链接文件bool _isrunning;
};
这就是基础的框架。
2.3 初始化接口
InitServer()初始化接口进行的工作很好理解:
- 首先创建socket文件,获取到
_listensockfd - 然后将服务器结构体的成员进行初始化,将服务器端口与
_listensockfd进行绑定 - 最后将
_listensockfd通过listen函数进入监听状态。
初始化任务就完成了
// 进行初始化void InitServer(){// 创建socket文件 --- 字节流方式_listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(FATAL, "socket error!!!\n");exit(SOCKET_FD);}LOG(INFO, "socket create success!!! _listensockfd: %d\n", _listensockfd);// 建立server结构体struct sockaddr_in local;memset(&local , 0 , sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY; // 服务器IP一般设置为0local.sin_port = htons(_port); //一定注意主机序列转网络序列// 进行绑定if (::bind(_listensockfd, (struct sockaddr *)&local, sizeof(local)) < 0){LOG(FATAL, "bind error!!!\n");exit(SOCKET_BIND);}LOG(INFO, "bind success!!!\n");// 将_listensockfd文件转换为listening状态!!!if (::listen(_listensockfd, gblocklog) < 0){LOG(FATAL, "listen error!!!\n");exit(SOCKET_LISTNE);}LOG(INFO, "listen success!!!\n");}
2.4 循环接收接口与服务接口
Loop()循环接收接口需要:
- 不断从套接字文件中accept获取连接流与客户端信息!
- 获取成功后,就可以进行服务了
- 服务就是从流中读取数据,然后处理之后再写回流中!!!使用的接口是read与write,文件流中我们对他们很熟悉!!!
void Loop(){_isrunning = true;while (_isrunning){// accept接收sockfdstruct sockaddr_in client;socklen_t len = sizeof(client);int sockfd = ::accept(_listensockfd, (struct sockaddr *)&client, &len);if(sockfd < 0){LOG(WARNING, "accept error\n");continue;}InetAddr addr(client);// 读取数据LOG(INFO, "get a new link, client info : %s, sockfd is : %d\n", addr.AddrStr().c_str(), sockfd);// version 0 --- 不靠谱版本Service(sockfd, addr);}_isrunning = false;}
void Service(int sockfd, InetAddr addr){LOG(INFO , "service start!!!\n");while (true){char buffer[1024];ssize_t n = ::read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;LOG(INFO , "sockfd read success!!! buffer: %s\n" , buffer);std::string str = "[server echo]#";str += buffer; write(sockfd, str.c_str(), str.size());}else if(n == 0){LOG(INFO , "client %s quit!\n" , addr.AddrStr().c_str());break;}else{LOG(ERROR, "read error: %s\n", addr.AddrStr().c_str());break;}}::close(sockfd);}
这样基础的服务器的通信工作就写好了
3 服务端与客户端
接下来我们来完善一下服务端和客户端的通信逻辑,让他们可以通信起来
服务端简单的创建一个服务器类然后进行初始化和loop就可以了!!!
#include "TcpServer.hpp"int main(int argc , char* argv[])
{if(argc != 2){std::cerr << "Usage: " << argv[0] << " local-port" << std::endl;exit(0);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(port);tsvr->InitServer();tsvr->Loop();return 0;
}
客户端稍微复杂一些:
- 首先根据传入的参数进行初始化服务器IP地址和端口号
- 然后创建套接字文件 ,并进行connect连接绑定bind,客户端回被动绑定一个端口号!!!
- 绑定成功之后就可以通过sockfd进行写入与读取了!!!
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#include <memory>
#include <string>
#include <cstring>
#include <iostream>#include "Log.hpp"using namespace log_ns;int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << " server-ip server-port" << std::endl;exit(0);}std::string ip = argv[1];uint16_t port = std::stoi(argv[2]);// 创建socket文件int sockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){LOG(FATAL, "sockfd create error!!!\n");exit(1);}struct sockaddr_in server;memset(&server, 0, sizeof(server)); // 数据归零server.sin_family = AF_INET;server.sin_port = htons(port); // 端口号 主机序列转网络序列!!!::inet_pton(AF_INET, ip.c_str(), &server.sin_addr);//安全写入// 进行发送数据int n = ::connect(sockfd, (struct sockaddr *)&server, sizeof(server));if (n < 0){std::cerr << "connect socket error" << std::endl;exit(2);}// 链接成功while (true){// 进行写入std::string line;std::cout << "Please Enter: ";std::getline(std::cin, line);::write(sockfd, line.c_str(), line.size());LOG(DEBUG , "write success !!!\n");// 读取数据char buffer[1024];int n = read(sockfd, buffer, sizeof(buffer));if (n > 0){buffer[n] = 0;std::cout << buffer << std::endl;}else{break;}}::close(sockfd);return 0;
}
测试运行
我们来测试一下服务端和客户端是否可以做到通信:

很好,可以完美的进行通信!!!
之后我们就可以加入多线程,加入回调函数逻辑,就可以进行业务处理了!!!
相关文章:
【计网】从零开始使用TCP进行socket编程 --- 客户端与服务端的通信实现
阵雨后放晴的天空中, 出现的彩虹很快便会消失。 而人心中的彩虹却永不会消失。 --- 太宰治 《斜阳》--- 从零开始使用TCP进行socket编程 1 TCP与UDP2 TCP服务器类2.1 TCP基础知识2.2 整体框架设计2.3 初始化接口2.4 循环接收接口与服务接口 3 服务端与客户端测试…...
Imagen:重塑图像生成领域的革命性突破
目录 引言 一、Imagen模型的技术原理 1. 模型概述 2. 工作流程 3. 技术创新 二、Imagen模型的应用实例 1. 创意设计 2. 虚拟角色制作 3. 概念可视化 三、Imagen模型的优势与挑战 1. 优势 2. 挑战 四、Imagen模型的未来发展方向 1. 图像生成质量的提升 2. 多模态…...
Golang | Leetcode Golang题解之第402题移掉K位数字
题目: 题解: func removeKdigits(num string, k int) string {stack : []byte{}for i : range num {digit : num[i]for k > 0 && len(stack) > 0 && digit < stack[len(stack)-1] {stack stack[:len(stack)-1]k--}stack app…...
c++ gtsam/inference/Symbol.h 详细介绍
gtsam/inference/Symbol.h 是 GTSAM 库中的一个头文件,定义了 Symbol 类。这个类用于在因子图优化中标识和管理变量。Symbol 提供了一种便捷的方式来创建和使用唯一标识符,从而避免手动管理复杂的整数键。 Symbol 类详细介绍 Symbol 类是 GTSAM 中用于…...
apache文件共享和访问控制
实现apache文件共享 文件共享路径 <Directory "/var/www/html"> #默认发布路径,功能限制 Options Indexes FollowSymLinks #indexes支持文件共享功能 AllowOverride None Require all granted </Directory> 进入到该路径下 cd…...
LeetCode 2398.预算内的最多机器人数目:滑动窗口+单调队列——思路清晰的一篇题解
【LetMeFly】2398.预算内的最多机器人数目:滑动窗口单调队列——思路清晰的一篇题解 力扣题目链接:https://leetcode.cn/problems/maximum-number-of-robots-within-budget/ 你有 n 个机器人,给你两个下标从 0 开始的整数数组 chargeTimes …...
vue 在线预览word和excel
yarn add vue-office/excel vue-office/docx <template><div><vue-office-docx:src"docx"style"height: 100%; margin: 0; padding: 0"rendered"rendered"/></div> </template><script> //引入VueOfficeDoc…...
物联网智能项目
物联网智能项目是一个涉及多个领域和技术的综合性项目,旨在通过互联网将各种物理设备连接起来,实现数据的采集、传输、处理和分析,进而实现智能化控制和管理。以下是对物联网智能项目的详细阐述,包括其定义、关键技术、应用领域、…...
828华为云征文|Flexus云服务器X:Python安装的极致便捷之旅
目录 前言 一、Flexus云服务器X介绍 1.1 Flexus云服务器X实例简介 1.2 Flexus云服务器X实例特点 1.3 Flexus云服务器X实例场景需求 二、Flexus云服务器X购买 2.1 Flexus X实例购买 2.2 重置密码 2.3 登录服务器 三、Flexus云服务器X安装Python 3.1 Python下载 3.2 Python安装 3…...
Midjourney中秋特典-12张图附魔咒
第一张 魔咒 A Mid-Autumn Festival poster, a round bright moon, a Chinese-style pavilion with a scene of a reunion from Dream of the Red Chamber, a new Chinese style --ar 3:4 --v 6.1第二张 魔咒 The bright full moon hung in the night sky,clear in outline a…...
编写程序,从键盘输入若干整数,将其保存入一个数组中。利用Arravs进行排序,然后查找出第3大的整数
编写程序,从键盘输入若干整数,将其保存入一个数组中。利用Arravs进行排序,然 后查找出第3大的整数 import java.util.ArrayList; import java.util.Arrays; import java.util.Scanner;public class helloworld {public static void main(Stri…...
VSCode 离线安装中文语言包
1.插件市场 Extensions for Visual Studio family of products | Visual Studio Marketplace 输入: language 在version history里面下载相应的版本,若没有就下载最新的 在下面安装 安装完重启就可以了。 可能会提示的失败: Unable to ins…...
vue3补充
form表单重置 const { proxy } getCurrentInstance()!; // 获取挂载在全局的上下文proxy.resetForm(ruleFormRef); // 在el-form中清空ref为ruleFormRef的表单注:不推荐使用 不推荐的原因 类型安全问题: 当在 TypeScript 环境中使用时,…...
关于Chrome浏览器没有网络,而其他浏览器正常这一问题的解决方法
网上有很多解决方案,但我尝试了之后都没有效果。后来尝试开启了VPN,问题完美解决了。 ✿✿ヽ(▽)ノ✿ 解决前:打开VPN后很容易就解决了:...
人工智能辅助汽车造型设计
随着科技的不断进步,人工智能(AI)在各个领域的应用越来越广泛,汽车设计行业也不例外。尤其在车辆外观造型设计中,AI正在成为设计师的重要助手,通过提供强大的工具和独特的创意方式,革新了传统设…...
专家访谈:心脑血管名医蔡英丽医生的医学智慧
在心脑血管疾病的诊疗领域,有这样一位医生,她以深厚的医学功底、精湛的医术和无私的爱心,赢得了广大患者的信赖与赞誉。她,就是北京心脑血管科的蔡英丽医生。今天,我们将带您走进蔡英丽医生的医学世界,一探…...
Ubuntu 22.04 源码下载的几种方法
1、查询当前系统内核版本 rootubuntu22:~# uname -r 5.15.0-118-generic 2、查询本地软件包数据库中的内核源码信息 rootubuntu22:~# apt search linux-source Sorting... Done Full Text Search... Done linux-source/jammy-updates,jammy-security,now 5.15.0.119.119 all…...
floodfill+DFS(1)
文章目录 图像渲染岛屿数量岛屿的最大面积被围绕的岛屿 图像渲染 class Solution { public:int m 0, n 0;bool check[51][51] {false};vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {m image.size…...
小程序开发设计-第一个小程序:注册小程序开发账号②
上一篇文章导航: 小程序开发设计-小程序简介①-CSDN博客https://blog.csdn.net/qq_60872637/article/details/142217803?sharetypeblogdetail&sharerId142217803&sharereferPC&sharesourceqq_60872637&spm1011.2480.3001.8118 须知:不…...
C++基础面试题 | C++中的构造函数可以是虚函数吗? C++中的析构函数一定要是虚函数吗?
文章目录 问题一:在C中,构造函数不能是虚函数。问题二:析构函数不一定需要声明为虚函数,但在多态环境下,建议一定将其声明为虚函数。示例虚函数总结 问题一:在C中,构造函数不能是虚函数。 这是…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
