Linux_实现TCP网络通信
目录
1、实现服务器的逻辑
1.1 socket
1.2 bind
1.3 listen
1.4 accept
1.5 read
1.6 write
1.7 服务器代码
2、实现客户端的逻辑
2.1 connect
2.3 客户端代码
3、实现服务器与客户端的通信
结语
前言:
在Linux下,实现传输层协议为TCP的套接字进行网络通信,网络层协议为IPv4,需要用到的接口如下:实现服务器的接口有(socket、bind、listen、accept、read、write),实现客户端的接口有(socket、connect、write、read)。
实现方式:因为网络通信的本质是进程间通信在云服务器上创建一个服务器进程和一个客户端进程,服务器进程先启动,然后让客户端链接到服务器上,至此客户端可以向服务器发送消息,并且服务器收到消息后可以给对方反馈信息。
TCP通信示意图如下:
1、实现服务器的逻辑
将服务器封装成一个类,服务器的端口号、IP地址以及网络描述符(一种类似文件描述符的字段)作为类的成员变量,这样做的好处是对软件实现分层,方便维护代码。按照下文的接口顺序调用实现服务器类。
1.1 socket
实现网络通信的第一步基本都是调用socket接口,目前已经明确传输层协议为TCP,网络层协议为IPv4,因此可以直接调用socket,该函数介绍如下:
#include <sys/types.h>
#include <sys/socket.h>int socket(int domain, int type, int protocol);
//domain表示网络层协议,填AF_INET表示IPv4,填AF_INET6表示IPv6
//type表示传输层协议,SOCK_STREAM为TCP,SOCK_DGRAM为UDP
//protocol表示指定特定的协议,默认填0即可//调用成功返回一个类型文件描述符的网络描述符,失败返回-1
1.2 bind
在网络通信中,bind函数与socket函数密不可分,他是用来绑定网络描述符和地址信息的,目的是让后续的通信可以直接通过网络描述符进行。在使用bind函数前,需要先创建一个包含地址信息的结构体,IPv4对应的结构体类型是struct sockaddr_in,该结构体里需要程序员手动填写3个信息,分别是:1、TCP/UDP,2、自定义的端口号,3、该主机的IP地址。
体现地址信息的伪代码如下:
struct sockaddr_in local;//定义变量
memset(&local, 0, sizeof(local));//对内部内容清零
local.sin_family = AF_INET;//填写ip协议
local.sin_port = htons(port_);//填写端口号
inet_aton(ip_.c_str(), &(local.sin_addr));//填写ip地址
其中由于网络传输数据规定以大端字节序传输,因此填写端口号和ip时需要对其进行大端字节序的转换,这里可以调用htons函数和inet_aton函数实现转换,这两个函数的介绍如下:
#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);
//将主机字节序转换成网络大端字节序,以返回值的形式转换#include <arpa/inet.h>
int inet_aton(const char *string, struct in_addr*addr);
//将字符串形式的ip转换成网络字节序,并直接赋值给addr指向的空间
在地址信息完善后,紧接着就是调用bind函数进行绑定,bind函数介绍如下:
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
//socket表示要绑定的网络描述符
//address表示指向的地址信息变量
//address_len表示地址信息变量的大小//调用成功返回0,失败返回-1并设置错误码
1.3 listen
与UDP不同的是,UDP绑定之后就可以用网络描述符进行接收和发送消息的操作了,但是TCP不一样,TCP的绑定完成后需要对网络描述符再进行监听操作,监听实际上是一种抽象的连接概念(因为TCP是有连接的,而UDP是无连接的,所以需要监听),即调用listen函数。listen函数的介绍如下:
#include <sys/socket.h> int listen(int sockfd, int backlog);
//sockfd表示要监听的网络描述符
//backlog表示设置套接字的链接队列的最大数量//调用成功返回0,失败返回-1并设置错误码
1.4 accept
待上述3个函数完成基础工作后,接下来就是最重要的连接环节了,accept函数的作用是和客户端进行连接,但是注意链接不意味着直接接收对方发来的数据,连接完成之后才能开始正常通信。accept函数介绍如下:
#include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//sockfd表示服务器的网络描述符,即通过观察该描述符得知是否有客户端链接
//addr是指向地址信息的指针,用来保存客户端的地址信息
//addrlen表示存储客户端地址信息的结构体大小//重要的是调用成功时返回一个新的网络描述符,失败返回-1
accept的重要之处在于若连接成功,则返回一个新的网络描述符,而该网络描述符才是后续通信的”端口“,意味着现在服务器有两个网络描述符了,一个是socket返回的,一个是accept返回的。可以这样理解他们:socket返回的描述符表示该服务器的总描述符,类似二叉树的根结点,而accept返回的描述符就是用于服务多个客户端的次描述符,即给每个来连接的客户端都分配一个次描述符,类似二叉树的叶子结点。(但是当前服务器的实现只能服务单客户端,因为他不是多线程的)
题外话:UDP通信只有一个网络描述符,所有的客户端都是通过这一个描述符来进行和服务器的通信。
1.5 read
链接完毕之后,说明此时客户端和服务器双方建立了联系,服务器的下一步就是读取客户端发送的信息,因为TCP是面向字节流的,因此可以使用文件操作的方式进行流读取,即使用read函数读取客户端发送的数据,read函数介绍如下:
#include <unistd.h> ssize_t read(int fd, void *buf, size_t count);
//fd表示要读取的文件描述符
//buf表示将读取数据存放至缓冲区的缓冲区指针
//count表示读取的字节数
1.6 write
服务器不仅仅可以接收数据,还可以向客户端反馈数据,因为accept让服务器和客户端之间有了唯一的网络描述符,因此服务器可以通过该描述符对客户端反馈信息,而无需像UDP需要记录对方的地址信息才可以反馈信息。write函数介绍如下:
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count);
//fd表示要写入的文件描述符
//buf表示指向写入的内容
//count表示要写入的字节
1.7 服务器代码
实现服务器类的代码如下:
#pragma once#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";//云服务器默认通信ip
const int backlog = 10; // 但是一般不要设置的太大class TcpServer
{
public:TcpServer(const uint16_t &port, const std::string &ip = defaultip) : listensock_(defaultfd), port_(port), ip_(ip){}void InitServer(){listensock_ = socket(AF_INET, SOCK_STREAM, 0);printf("创建套接字成功, listensock_: %d\n", listensock_);struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port_);inet_aton(ip_.c_str(), &(local.sin_addr));bind(listensock_, (struct sockaddr *)&local, sizeof(local));printf("绑定套接字成功, listensock_: %d\n", listensock_);// Tcp是面向连接的,服务器一般是比较“被动的”,//服务器一直处于一种,一直在等待连接到来的状态listen(listensock_, backlog);printf("监听套接字成功, listensock_: %d\n", listensock_);}void Start(){printf("TCP服务器正在运行....");for (;;){// 1. 获取新连接struct sockaddr_in client;//保存客户端的地址信息socklen_t len = sizeof(client);//类似recvfromint sockfd = accept(listensock_, (struct sockaddr *)&client, &len);uint16_t clientport = ntohs(client.sin_port);//将客户端端口号拿出来char clientip[32];//将ip从网络字节序转换成主机序列并写进缓冲区clientip内inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));printf("链接到一个客户端, sockfd: %d, client port: %d\n", sockfd, clientport);// 2. 根据新连接来进行通信Service(sockfd);close(sockfd);//关闭文件描述符}}void Service(int sockfd){// 测试代码char buffer[4096];while (true){ssize_t n = read(sockfd, buffer, sizeof(buffer));if (n > 0){buffer[n] = 0;std::cout << "客户端说# " << buffer << std::endl;std::string echo_string = "服务器回答# ";echo_string += buffer;write(sockfd, echo_string.c_str(), echo_string.size());}else if(n==0){std::cout<<"读到文件末尾"<<std::endl;sleep(1);break;}else{std::cout<<"读到出错"<<std::endl;sleep(1);break;}}}~TcpServer() {if(listensock_>0) close(listensock_);}private:int listensock_;uint16_t port_;std::string ip_;
};
2、实现客户端的逻辑
与服务器相比,客户端的实现逻辑要简单许多,因为服务器要将底层的基础设施建设好,而客户端只需要完成连接这一个动作即可,然后就是发送数据和接收反馈,当然,客户端的第一步也是调用socket拿到属于客户端的网络描述符,因为后续的通信工作要通过该描述符进行。
2.1 connect
客户端只需要做连接动作即可,而连接动作是依靠connect函数实现的,connect函数介绍如下:
#include <sys/types.h>
#include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//sockfd表示要与对方建立连接的客户端网络描述符
//addr指向地址信息的地址,该地址信息即服务器的地址信息
//addrlen表示addr指向的地址信息的大小
所以调用connect前必须知道服务器的地址信息:即ip地址和端口号,可以通过命令行参数的形式传给客户端代码,并且拿到地址信息后也是要构造struct sockaddr_in变量将信息填写进去。
2.3 客户端代码
实现客户端代码如下(注意客户端直接在main函数中实现,无需在对其进行封装,因为客户端的代码简单):
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>void Usage(const std::string &proc)
{std::cout << "\n\rUsage: " << proc << " serverip serverport\n"<< std::endl;
}// ./tcpclient serverip serverport
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}//从命令行参数获取服务器的端口和ipstd::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// 初始化地址信息struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);// 将ip转换成网络字节序inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr)); int sockfd = 0;sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字// 客户端发起connect的时候,进行自动随机bind//链接服务器int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));if (n < 0){std::cout << "链接失败, reconnect: " << std::endl;sleep(2);}while (true){std::string message;std::cout << "Please Enter# ";std::getline(std::cin, message);int n = write(sockfd, message.c_str(), message.size());//发送数据if (n < 0){std::cout << "write出错" << std::endl;// break;}char inbuffer[4096];n = read(sockfd, inbuffer, sizeof(inbuffer));//接收反馈if (n > 0){inbuffer[n] = 0;std::cout << inbuffer << std::endl;}}close(sockfd);return 0;
}
3、实现服务器与客户端的通信
实现双方通信前,必须满足双方都为进程,但是服务器目前还只是一个类,因此要把服务器类实例化在main函数中,让服务器也变成进程。实现服务器进程的代码如下:
#include "TCPser.hpp"
#include <iostream>
#include <memory>void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl;
}// 下节课:守护进程化
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(1);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<TcpServer> tcp_svr(new TcpServer(port));tcp_svr->InitServer();tcp_svr->Start();return 0;
}
运行结果:
特别注意:通信时,必须保证服务器先运行起来,若客户端先运行起来,则会在connect处连接失败,因为找不到对应的服务器端口号。
结语
以上就是关于在Linux下实现TCP网络通信的讲解,实现TCP的主要思路是了解套接字的作用,以及理清对套接字相关函数的调用顺序,并且其次TCP通信的原理。
最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!
相关文章:

Linux_实现TCP网络通信
目录 1、实现服务器的逻辑 1.1 socket 1.2 bind 1.3 listen 1.4 accept 1.5 read 1.6 write 1.7 服务器代码 2、实现客户端的逻辑 2.1 connect 2.3 客户端代码 3、实现服务器与客户端的通信 结语 前言: 在Linux下,实现传输层协议为TCP…...

正则表达式与文本三剑客之grep
目录 前言 一、grep命令 二、基础正则表达式常见元字符 2.1、特殊字符 2.2、定位符 2.3、非打印字符 三、元字符操作实例 3.1、查找特定字符 3.2、利用中括号“[]”来查找集合字符 3.3、查找行首“^”与行尾字符“$” 3.4、查找任意一个字符“.”与重复字符“*” 3.…...

微信小程序开发:项目程序代码构成
✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…...

【云原生】Kubernetes微服务Istio:介绍、原理、应用及实战案例
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...

【Docker】Docker-consul容器服务自动发现与注册
目录 一.Consul概述 1.解决了什么问题 2.什么叫微服务或者注册与发现 3.consul的模式 4.相关命令 二.consul 部署 1.consul服务器部署 2.部署docker容器 3.Nginx负载均衡器 3.1.安装启动nginx 3.2.配置nginx负载均衡 3.3.创建配置consul complate模板文件 3.4.添加…...
Go 1.22 remote error: tls: handshake failure
Golang 1.22 remote error: tls: handshake failure 1.22之前运行下面代码是没有错误 package mainimport ("crypto/tls""fmt""net/http" )func main() {http.DefaultTransport.(*http.Transport).TLSClientConfig &tls.Config{InsecureS…...
迈向通用人工智能:AGI的到来与社会变革展望
正文: 随着科技的飞速发展,通用人工智能(AGI)的来临似乎已不再遥远。近期,多位行业领袖和专家纷纷预测,AGI的到来时间可能比我们想象的要早。在这篇博客中,我们将探讨AGI的发展趋势、潜在影响以…...

大模型额外篇章三:vercel搭建openai中转服务器
文章目录 一、起因和注意1)起因2)注意二、实现方法(原理:透传)1)nginx方案2)node服务3)纯 js 方案4)选择国外的域名服务商(DNS 解析路径缩短,建议方案国外提供 CDN 云服务商结合自建云服务业务做负载均衡)三、实践(vercel部署OpenAI代理服务器)四、测试搭建的Ope…...
使用 jQuery 中的 this 实例
在 jQuery 中,this 关键字用于表示指向当前操作的 DOM 元素。本篇博客将详细介绍如何在 jQuery 中使用 this 实例。 一、选择器中的 this 在选择器中,this 可以方便地指向当前操作的 DOM 元素。例如,当用户点击一个按钮时,我们想…...

下载最新版Anaconda、安装、更换源、配置虚拟环境并在vscode中使用
文章目录 进入官网进入下载页安装更换源配置虚拟环境env安装包requests在vscode中使用虚拟环境 进入官网 https://repo.anaconda.com/ 或进入清华大学下载 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 进入下载页 安装 更换源 查看已经存在的镜像源 bash cond…...
极狐GitLab Git LFS(大文件存储)如何管理?
GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab :https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署…...

迭代学习笔记
一、迭代学习定义和分类 1、直观理解 迭代学习一般应用于重复性的场景。比如控制一个单自由度的小车以特定的速度曲线移动到指定位置,整个时间是10s,控制频率是0.01,那么整个控制序列就会有1000个点。这1000个点在10s内依次发出,…...

【安全】系统安全设计规范(DOC完整版)
1.1安全建设原则 1.2 安全管理体系 1.3 安全管理规范 1.4 数据安全保障措施 1.4.1 数据库安全保障 1.4.2 操作系统安全保障 1.4.3 病毒防治 1.5安全保障措施 1.5.1实名认证保障 1.5.2 接口安全保障 1.5.3 加密传输保障 1.5.4终端安全保障 软件资料清单列表部分文档&…...

windows常用命令整理
本文分享一些常用的windows命令。根据功能的不同,大致可分为以下几个方面,一是文件操作命令,二是进程相关命令,三是磁盘相关命令,四是网络相关命令,五是其他命令。 1.文件操作命令 dir:显示当…...

视频处理基础知识1
1、图像基本知识 图像的组成:像素、RGB(每个像素由三个发光二极管组成)、分辨率(横纵向像素的个数乘积) PPI每英寸的像素数 DPI每英寸的点数,有可能一个点有多个像素 PPI>300 就属于视网膜级别,就是很清晰&#…...
Linux退不出vim编辑模式
目录 第一章、问题分析1.1)报错提示 第二章、解决方式 友情提醒: 先看文章目录,大致了解文章知识点结构,点击文章目录可直接跳转到文章指定位置。 第一章、问题分析 1.1)报错提示 报错如下:使用Linux的vi…...

TikTok养号的网络环境及相关代理IP知识
TikTok作为一个流行的短视频分享平台,其用户量非常庞大,很多商家和个人都会使用TikTok来进行引流和推广。由于TikTok的规则和政策限制了每个用户每天发布视频的数量,因此许多用户会使用多个账号来发布更多的视频以提高曝光率。 然而ÿ…...

过程调用和数组的分配访问
系列文章 : 深入理解计算机系统笔记 文章目录 系列文章3.7 过程3.7.1 运行时栈3.7.2 转移控制3.7.3 数据传送3.7.4 栈上的局部存储3.7.5 寄存器中的局部存储空间3.7.6 递归过程 3.8 数组分配和访问3.8.1 基本原则3.8.2 指针运算3.8.3 嵌套的数组3.8.4 定长数组3.8.5 变长数组…...

TeamViewer手机端APP提示:请先验证账户
当你在手机端下载安装了TeamViewerAPP后,需要你先登录个人账号,然后还会要求你验证账户,同时跳转到一个网址中,但是这个网址并没有自动跳转到验证账户的位置。 解决办法: 在手机浏览器中进入下面这个网址:…...
【SpringBoot】分页查询
1. Controller ApiOperation("分页查询")GetMapping("/page")public Result<PageResult> pageResultResult(EmployeePageQueryDTO employeePageQueryDTO) {System.out.println(employeePageQueryDTO.toString());PageResult pageResult employeeSer…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...

在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例
目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码:冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...