TCP网络编程(一)—— 服务器端模式和客户端模式

这篇文章将会编写基本的服务器网络程序,主要讲解服务器端和客户端代码的原理,至于网络名词很具体的概念,例如什么是TCP协议,不会过多涉及。

首先介绍一下TCP网络编程的两种模式:服务器端和客户端模式:

        首先说明一下:黑色线代表状态的转换,红色线表示的是数据的传输,read 和 write 之间的循环表示:例如读取完数据,进入写入的状态,写入完再进入读取的状态,一直循环,实现了服务器和客户端之间的通信。


首先来解释一下服务器端:

int socket(int domain, int type, int protocol)

socket() 表示创建一个套接字。套接字是网络通信的基本数据结构,用于定义通信协议(如 TCP 或 UDP)和地址族(如 IPv4 或 IPv6)。通过套接字,服务器和客户端可以在网络上传输数据,可以把套接字理解为一个编程接口,利用套接字实现程序和网络的连接,像是用户层和传输层(TCP)中间的一个抽象层,有了套接字才可以向网络发送数据。

传入的内容是(协议族,套接字类型,默认协议(通常为0))
返回:成功返回套接字描述符,失败返回-1 

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

bind() 表示将套接字绑定到一个特定的地址和端口。绑定的地址和端口标识服务器,使客户端能够找到并连接到该服务。只有套接字还不够,我还要知道是哪个主机(IP)发送的,哪个应用程序(端口)发送的,端口可以理解为电脑通信的入口和出口。

传入的内容是:(套接字描述符,地址结构体的地址,地址结构体大小)
返回:成功返回0,失败返回-1。

int listen(int sockfd, int backlog)

listen() 表示将套接字转换为监听模式,并设置等待连接的队列长度。当多个客户端请求连接时,服务器会将这些请求加入队列,按顺序处理。

传入的内容是(套接字描述符,队列的长度)
返回:成功返回0,失败返回-1。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

accept() 表示等待接受客户端的连接请求,接收到请求,成功连接后,accept() 返回一个新的套接字,用于与该客户端通信,而原始监听套接字则继续处理新的连接请求。

传入的内容是:(套接字描述符,地址结构体的地址,地址结构体大小的地址)
返回:成功返回新的套接字描述符,失败返回-1 

ssize_t read(int sockfd, void *buf, size_t count)

read() 表示从套接字描述符中读取数据,用于接收客户端发送的消息。读取的数据存储在提供的缓冲区中。

传入的内容是:(套接字描述符,缓冲区指针(数组),要读取的字节数)
返回:成功返回实际读取的字节数,失败返回-1。

ssize_t write(int sockfd, const void *buf, size_t count)

write() 表示向套接字描述符中写入数据,用于向客户端发送响应数据。

传入的内容是:(套接字描述符,缓冲区指针(数组),要写入的字节数)
返回:成功返回实际写入的字节数,失败返回-1。

int close(int sockfd)

close()表示关闭套接字描述符。

传入的内容是:(套接字描述符)
返回:成功返回0,失败返回-1。 


接着解释一下客户端的新出现的函数:

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

connet()表示客户端向服务器发起连接请求。客户端告诉操作系统需要连接到哪个服务器的哪个端口。

传入的内容是:(套接字描述符,地址结构体的地址,地址结构体大小)
返回:成功返回0,失败返回-1。

看完这些,你会发现:套接字描述符和文件描述符很像,都可以根据描述进行写入读取和各种其他操作,其实,这就是UNIX系统和类UNIX系统(Linux系统)的抽象资源管理方式,通过整数来标识系统中的资源,使用统一的接口设计,“一切皆文件”。

看到这里,你一定有几个问题:

1.为什么客户端少了bind()和listen()的操作?

2.为什么connect操作指向了accept操作之后?

3.地址结构体的地址addr是个什么东西?

4.为什么有的函数传addr大小,有的传addr大小的地址?

1.对于服务器端来说,服务器需要绑定到固定的端口这样客户端才能知道它,对于客户端来说,操作系统会在必要的时候分配临时的本地端口和地址,不需要再绑定端口。

2.因为服务器端的accept函数是阻塞的,等待客户端发起请求,当connect发送给服务器端请求之后,才会继续进行后面的读写操作。

3.addr的类型如下:有两个成员,分别是地址族,地址和端口信息,但是这不方便我们进行设置,所以一般采用 sockaddr_in 这个结构,最后在进行强制类型转换得到sockaddr,注意这两个结构体类型大小是一样的,只是结构不一样。

struct sockaddr {sa_family_t sa_family;    // 地址族,例如 AF_INET(IPv4)或 AF_INET6(IPv6)char sa_data[14];         // 地址和端口信息
};

下面是sockaddr_in结构体类型,可以清楚地看到每个成员的含义:

struct sockaddr_in {sa_family_t sin_family;   // 地址族,通常为 AF_INET(IPv4)uint16_t sin_port;        // 端口号(16 位),以网络字节序表示struct in_addr sin_addr;  // IP 地址(32 位)char sin_zero[8];         // 保留字段,填充用
};

4.可以看到accept函数的addrlen参数是 addr 大小(变量)的地址,但是connect和bind函数的addrlen参数是 addr 大小(变量)本身,这是因为accept不知道调用者提供的 addr 缓冲区的大小可能是IPv4可能是IPv6,所以需要地址地址。

我猜测可能和TCP的三次握手或者accept返回新的套接字或者客户端分配动态端口有关系,而connect和bind函数都是用已知的套接字进行操作,所以不会进行addr大小的改变,所以可以直接传值。

这就是TCP编程的两种模式,从下篇文章开始,我们将学习如何编写服务器端和客户端的代码。

这就是文章的所有内容了,希望对你有所帮助,如有错误欢迎指出。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在K8S中,如何部署kubesphere?

在Kubernetes集群中,对于一些基础能力较弱的群体来说K8S控制面板操作存在一定的难度,此时kubesphere可以有效的解决这类难题。以下是部署kubesphere的操作步骤: 操作部署: 1. 部署nfs共享存储目录 yum -y install nfs-server e…

树莓派之旅-第一天 系统的烧录和设置

自言自语: 在此记录一下树莓派的玩法。以后有钱了买点来玩啊草 系统的安装烧录 系统下载 树莓派官网:https://www.raspberrypi.com/ 首页点击SoftWare进入OS下载页面 这里是安装工具:安装工具负责将系统镜像安装到sd卡中 点击下载符合自己…

商用车自动驾驶,迎来大规模量产「临界点」?

商用车自动驾驶,正迎来新的行业拐点。 今年初,交通部公开发布AEB系统运营车辆标配征求意见稿,首次将法规限制条件全面放开,有望推动商用车AEB全面标配,为开放场景的商用车智能驾驶市场加了一把火。 另外,…

人工智能及深度学习的一些题目

1、一个含有2个隐藏层的多层感知机(MLP),神经元个数都为20,输入和输出节点分别由8和5个节点,这个网络有多少权重值? 答:在MLP中,权重是连接神经元的参数,每个连接都有一…

Solon 加入 GitCode:助力国产 Java 应用开发新飞跃

在当今数字化快速发展的时代,Java 应用开发框架不断演进,开发者们始终在寻找更快、更小、更简单的解决方案。近期,Solon 正式加入 GitCode,为广大 Java 开发者带来全新的开发体验,尤其是在国产应用开发进程中&#xff…

VScode 只能运行c,运行不了c++的解决问题

原文链接:Vscode只能运行c,运行不了c的解决方法 VScode 只能运行c,运行不了c,怎么回事呢,解决问题: 在tasks.json中加上“"-lstdc"”, 这样之后 要重启VScode,点击链接…

SpringCloud系列教程:微服务的未来 (五)枚举处理器、JSON处理器、分页插件实现

在现代 Java 开发中,我们常常需要处理各种通用的功能和需求,诸如枚举的处理、JSON 数据处理,以及分页查询等。这些功能虽然看似简单,但在实际开发中往往涉及到许多细节和优化。为了提高开发效率、减少重复代码的编写,我…

游戏引擎学习第69天

回顾碰撞响应时我们停留的位置 从昨天的讨论开始,我们正准备处理碰撞响应的复杂性。具体来说,我们讨论的是,当两个实体在屏幕上发生碰撞时,如何回应这种情况。碰撞本身并不复杂,但要处理其后的反应和规则则更具挑战性…

MAC环境安装(卸载)软件

MAC环境安装(卸载)软件 jdknode安装node,并实现不同版本的切换背景 卸载node从node官网下载pkg安装的node卸载用 homebrew 安装的node如果你感觉删的不够干净,可以再细分删除验证删除结果 jdk 1.下载jdk 先去官网下载自己需要的版…

本地LLM部署--llama.cpp

–图源GitHub项目主页 概述 llama.cpp是以一个开源项目(GitHub主页:llamma.cpp),也是本地化部署LLM模型的方式之一,除了自身能够作为工具直接运行模型文件,也能够被其他软件或框架进行调用进行集成。 其…

uniapp中使用ruoyiPlus中的加密使用(crypto-js)

package.json中添加 "crypto-js": "^4.2.0", "jsencrypt": "^3.3.2",但是vue2中使用 import CryptoJS from cryptojs; 这一步就会报错 参照 参照这里:vue2使用CryptoJS实现信息加解密 根目录下的js文档中新增一个AESwork.…

go项目使用gentool生成model的gen.go问题

Gen Tool 是一个没有依赖关系的二进制文件,可以用来从数据库生成结构。 使用方法: go install gorm.io/gen/tools/gentoollatest在项目根目录,执行连接的数据库中指定某几张表结构生成数据库model层 gentool -dsn "root:123456tcp(localhost:330…

路由基本配置实验

路由器用于实现不同类型网络之间的互联。 路由器转发ip分组的基础是路由表。 路由表中的路由项分为直连路由项、静态路由项和动态路由项。 通过配置路由器接口的ip地址和子网掩码自动生成直连路由项。 通过手工配置创建静态路由项。 热备份路由器协议允许将由多个路由器组…

产品原型设计

🤣🤣目录🤣🤣 一、Axure原型设计(Axure RP 9 )1.1 软件下载安装1.2 产品原型展示1.3 产品原型下载1.4 视频课程推荐 二、磨刀原型设计2.1 软件下载安装2.2 产品原型展示2.3 产品原型下载2.4 视频课程推荐 什…

Android反编译

安卓反编译要用到三个工具, 工具1:apktool反编译出来资源文件和源码 工具2:d2j-dex2jar生成classes_dex2jar.jar文件工具3:jd-gui.exe 打开classes_dex2jar.jar文件查看java代码一、 反编译得到资源文件(工具1&#xf…

计算机网络 (17)点对点协议PPP

一、PPP协议的基本概念 PPP协议最初设计是为两个对等节点之间的IP流量传输提供一种封装协议,它替代了原来非标准的第二层协议(如SLIP)。在TCP/IP协议集中,PPP是一种用来同步调制连接的数据链路层协议(OSI模式中的第二层…

信息系统项目管理师——第8章章 项目整合管理 笔记

8 项目整合管理(最后反过来看) 项目整合过程:①制定项目章程(启动过程)、②制订项目管理计划(规划过程)、③指导和管理项目工作、管理项目知识(执行过程)、④监控项目工…

WebRTC线程的启动与运行

WebRTC线程运行的基本逻辑: while(true) {…Get(&msg, …);…Dispatch(&msg);… }Dispatch(Message *pmsg) {…pmsg->handler->OnMessage(pmsg);… }在执行函数内部,就是一个while死循环,只做两件事,从队列里Get取…

CSS 学习之 padding 与图形绘制

padding 属性和 background-clip 属性配合,可以在有限的标签下实现一些 CSS 图形绘制效果,我这里举两个小例子,重在展示可行性。 例 1:不使用伪元素,仅一层标签实现大队长的“三道杠”分类图标效果。此效果在移动端比较常见&…

yolov5核查数据标注漏报和误报

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、误报二、漏报三、源码总结 前言 本文主要用于记录数据标注和模型预测之间的漏报和误报思想及其源码 提示:以下是本篇文章正文内容,…