【计网】从零开始使用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中,构造函数不能是虚函数。 这是…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
GitHub 趋势日报 (2025年06月06日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...