Linux--Socket编程基础

news2024/11/24 15:57:31

一、Socket简介

套接字( socket )是 Linux 下的一种进程间通信机制( socket IPC ), 使用 socket IPC 可以使得在不同主机上的应用程序之间进行通信(网络通信),当然也可以是同一台主机上 的不同应用程序。socket IPC 通常使用客户端 <---> 服务器这种模式完成通信,多个客户端可以同时连接到服务器中,与服务器之间完成数据交互。
内核向应用层提供了 socket 接口,对于应用程序开发人员来说,我们只需要调用 socket 接口开发自己的应用程序即可! socket 是应用层与 TCP/IP 协议通信的中间软件抽象层,它是一组接口。 在设计模式中,socket 其实就是一个门面模式,它把复杂的 TCP/IP 协议隐藏在 socket 接口面,对用户来说,一组简单的接口就是全部,让 socket 去组织数据,以符合指定的协议。所以,我们无需深入的去理解 tcp/udp 等各种复杂的 TCP/IP 协议, socket 已经为我们封装好了,我们只需要遵循 socket 的规定去编程,写出的程序自然遵循 tcp/udp 标准的。
当前网络中的主流程序设计都是使用 socket 进行编程的,因为它简单易用,它还是一个标准( BSD socket),能在不同平台很方便移植,比如你的一个应用程序是基于 socket 接口编写的,那么它可以移植到任何实现 BSD socket 标准的平台,譬如 LwIP ,它兼容 BSD Socket ;又譬如 Windows ,它也实现了一套基于socket 的套接字接口,更甚至在国产操作系统中,如 RT-Thread ,它也实现了 BSD socket 标准的 socket 接口。

二、函数接口

1)建立连接前

1、Socket()函数

socket() 函数原型如下所示:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
socket() 函数类似于 open() 函数,它用于创建一个网络通信端点(打开一个网络通信),如果成功则返回 一个网络文件描述符,通常把这个文件描述符称为 socket 描述符( socket descriptor ),这个 socket 描述符跟 文件描述符一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。
调用 socket() 与调用 open() 函数很类似,调用成功情况下,均会返回用于文件 I/O 的文件描述符,只不 过对于 socket() 来说,其返回的文件描述符一般称为 socket 描述符。当不再需要该文件描述符时,可调用 close()函数来关闭套接字,释放相应的资源。
如果 socket() 函数调用失败,则会返回 -1 ,并且会设置 errno 变量以指示错误类型。
domain
参数 domain 用于指定一个通信域;这将选择将用于通信的协议族。对于 TCP/IP 协议来说,通常选择 AF_INET 就可以了,也就是选择IPv4协议,当然如果你的 IP 协议的版本支持 IPv6,那么可以选择 AF_INET6。
type
参数 type 指定套接字的类型,当前支持的类型有:
protocol
参数 protocol 通常设置为 0 ,表示为给定的通信域和套接字类型选择默认协议。当对同一域和套接字类型支持多个协议时,可以使用 protocol 参数选择一个特定协议。在 AF_INET 通信域中,套接字类型为 SOCK_STREAM 的默认协议是传输控制协议( Transmission Control Protocol TCP 协议)。在 AF_INET 通信域中,套接字类型为 SOCK_DGRAM 的默认协议时 UDP

2、bind()函数

bind() 函数原型如下所示:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind() 函数用于将一个 IP 地址或端口号与一个套接字进行绑定(将套接字与地址进行关联)。将一个客户端的套接字关联上一个地址没有多少新意,可以让系统选一个默认的地址。一般来讲,会将一个服务器的套接字绑定到一个众所周知的地址--- 即一个固定的与服务器进行通信的客户端应用程序提前就知道的地址 (注意这里说的地址包括 IP 地址和端口号,一般来说,80端口是http服务,22端口是ssh服务)。因为对于客户端来说,它与服务器进行通信,首先需要知道 服务器的 IP 地址以及对应的端口号,所以通常服务器的 IP 地址以及端口号都是众所周知的。
调用 bind() 函数将参数 sockfd 指定的套接字与一个地址 addr 进行绑定,成功返回 0 ,失败情况下返回 -1,并设置 errno 以提示错误原因。
Tips:bind()函数并不是总是需要调用的,只有用户进程想与一个具体的 IP 地址或端口号相关联的时候 才需要调用这个函数。如果用户进程没有这个必要,那么程序可以依赖内核的自动的选址机制来完成自动地址选择,通常在客户端应用程序中会这样做。

3、Listen()函数

listen() 函数只能在服务器进程中使用,让服务器进程进入监听状态,等待客户端的连接请求, listen() 函数在一般在 bind() 函数之后调用,在 accept() 函数之前调用,它的函数原型是:
int listen(int sockfd, int backlog);

无法在一个已经连接的套接字(即已经成功执行 connect()的套接字或由 accept()调用返回的套接字)上执行 listen()

backlog

参数 backlog 用来描述 sockfd 的等待连接队列能够达到的最大值。在服务器进程正处理客户端连接请求的时候,可能还存在其它的客户端请求建立连接,因为 TCP 连接是一个过程,由于同时尝试连接的用户过多,使得服务器进程无法快速地完成所有的连接请求,那怎么办呢?直接丢掉其他客户端的连接肯定不是一个很好的解决方法。因此内核会在自己的进程空间里维护一个队列,这些连接请求就会被放入一个队列中,服务器进程会按照先来后到的顺序去处理这些连接请求,这样的一个队列内核不可能让其任意大,所以必须有一个大小的上限,这个 backlog 参数告诉内核使用这个数值作为队列的上限。而当一个客户端的连接请求到达并且该队列为满时,客户端可能会收到一个表示连接失败的错误,本次请求会被丢弃不作处理。

4、accept()函数

服务器调用 listen() 函数之后,就会进入到监听状态,等待客户端的连接请求,使用 accept() 函数获取客户端的连接请求并建立连接。函数原型如下所示:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

为了能够正常让客户端能正常连接到服务器,服务器必须遵循以下处理流程:

①、调用 socket() 函数打开套接字;
②、调用 bind() 函数将套接字与一个端口号以及 IP 地址进行绑定;
③、调用 listen() 函数让服务器进程进入监听状态,监听客户端的连接请求;
④、调用 accept() 函数处理到来的连接请求。
accept() 函数通常只用于服务器应用程序中,如果调用 accept() 函数时,并没有客户端请求连接(等待连 接队列中也没有等待连接的请求),此时 accept() 会进入阻塞状态,直到有客户端连接请求到达为止。当有 客户端连接请求到达时,accept() 函数与远程客户端之间建立连接, accept() 函数返回一个新的套接字。这个 套接字与 socket() 函数返回的套接字并不同, socket() 函数返回的是服务器的套接字(以服务器为例),而 accept()函数返回的套接字连接到调用 connect() 的客户端,服务器通过该套接字与客户端进行数据交互,譬 如向客户端发送数据、或从客户端接收数据。
所以,理解 accept() 函数的关键点在于它会创建一个新的套接字,其实这个新的套接字就是与执行
connect() (客户端调用 connect() 向服务器发起连接请求)的客户端之间建立了连接,这个套接字代表了服务器与客户端的一个连接。如果 accept() 函数执行出错,将会返回 -1 ,并会设置 errno 以指示错误原因。

5、connect()函数

connect() 函数原型如下所示:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
该函数用于客户端应用程序中,客户端调用 connect()函数将套接字 sockfd 与远程服务器进行连接,参数 addr 指定了待连接的服务器的 IP 地址以及端口号等信息,参数 addrlen 指定了 addr 指向的 struct sockaddr对象的字节大小。
客户端通过 connect() 函数请求与服务器建立连接,对于 TCP 连接来说,调用该函数将发生 TCP 连接的 握手过程,并最终建立一个 TCP 连接,而对于 UDP 协议来说,调用这个函数只是在 sockfd 中记录服务器 IP 地址与端口号,而不发送任何数据。
函数调用成功则返回 0 ,失败返回 -1 ,并设置 errno 以指示错误原因。

2)建立连接后

发送和接收函数
一旦客户端与服务器建立好连接之后,我们就可以通过套接字描述符来收发数据了(对于客户端使用 socket()返回的套接字描述符,而对于服务器来说,需要使用 accept() 返回的套接字描述符),这与我们读写普通文件是差不多的操作,譬如可以调用 read() recv() 函数读取网络数据,调用 write() send() 函数发送数据。

read()函数

read() 函数大家都很熟悉了,通过 read() 函数从一个文件描述符中读取指定字节大小的数据并放入到指 定的缓冲区中,read() 调用成功将返回读取到的字节数,此返回值受文件剩余字节数限制,当返回值小于指定的字节数时并不意味着错误;这可能是因为当前可读取的字节数小于指定的字节数(比如已经接近文件结尾,或者正在从管道或者终端读取数据,或者 read() 函数被信号中断等),出错返回 -1 并设置 errno ,如果在调 read 之前已到达文件末尾,则这次 read 返回 0
套接字描述符也是文件描述符,所以使用 read() 函数读取网络数据时, read() 函数的参数 fd 就是对应的套接字描述符。

recv()函数

recv() 函数原型如下所示:
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
不论是客户端还是服务器都可以通过 revc() 函数读取网络数据,它与 read() 函数的功能是相似的。参数sockfd 指定套接字描述符,参数 buf 指向了一个数据接收缓冲区,参数 len 指定了读取数据的字节大小,参数 flags 可以指定一些标志用于控制如何接收数据。

write()函数

通过 write() 函数可以向套接字描述符中写入数据,函数调用成功返回写入的字节数,失败返回 -1 ,并设置 errno 变量。

send()函数

函数原型如下所示:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

send 和 write 很相似,但是 send 可以通过参数 flags 指定一些标志,来改变处理传输数据的方式。

close()函数

当不再需要套接字描述符时,可调用 close() 函数来关闭套接字,释放相应的资源。

3)IP 地址格式转换函数

对于人来说,我们更容易阅读的是点分十进制的 IP 地址,譬如 192.168.1.110 192.168.1.50 ,这其实是一种字符串的形式,但是计算机所需要理解的是二进制形式的 IP 地址,所以我们就需要在点分十进制字符串和二进制地址之间进行转换。

三、总结

1、编写服务器程序

编写服务器应用程序的流程如下:
①、调用 socket() 函数打开套接字,得到套接字描述符;
②、调用 bind() 函数将套接字与 IP 地址、端口号进行绑定;
③、调用 listen() 函数让服务器进程进入监听状态;
④、调用 accept() 函数获取客户端的连接请求并建立连接;
⑤、调用 read/recv write/send 与客户端进行通信;
⑥、调用 close() 关闭套接字。

2、编写客户端程序

①、调用 socket()函数打开套接字,得到套接字描述符;

②、调用 bind()函数将套接字与 IP 地址、端口号进行绑定;

③、调用 connect()连接远端服务器;

④、调用 read/recv、write/send 与客户端进行通信;

⑤、调用 close()关闭套接字。

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

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

相关文章

【C++课程学习】:C++入门(输入输出,缺省参数)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;C课程学习 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 &#x1f369;1.关于C输入输出&#xff1a; &#x1f369;2.缺省参数函数&#xff1a; 缺省参数的概…

手上3个offer怎么选,深信服的技服、奇安信的安全服务、绿盟的渗透工程师

我是一个应届生&#xff0c;目前手上收到了3个offer比较纠结选哪个&#xff1f; 1、深信服技术服务工程师&#xff0c;base 西安&#xff0c;月薪 14k 据说还有些异地补助 2、奇安信安全服务工程师&#xff0c;base 重庆&#xff0c;月薪 12k 3、绿盟科技的渗透工程师&#…

轻松掌握PDF文本内容修改技巧,让文档编辑更高效便捷,职场工作从此得心应手!

在快节奏的职场生活中&#xff0c;PDF文档已经成为我们工作中不可或缺的一部分。然而&#xff0c;如何高效、便捷地修改PDF文本内容&#xff0c;一直是许多职场人士头疼的问题。今天&#xff0c;就让我来为你介绍一款神奇的编辑工具——首助编辑高手&#xff0c;它将帮助你轻松…

http://app.hrss.xm.gov.cn/xmggfw/index

厦门市人力资源和社会保障局 厦门市国际职业资格比照认定职称审批表

【Cesium4UE】使用问题及解法统计

本期作者&#xff1a;尼克 易知微3D引擎技术负责人 1.加载3dtiles模型很慢 1.3dtiles是否做了重建顶层处理。如果3dtiles的tiles块太多使用CesiumLab重建顶层。 2.将3dtiles模型放置到固态硬盘中 3.如果有多块3dtiles&#xff0c;考虑使用CesiumLab合并3dtiles处理 4.如果不需…

14本剔除!Scopus目录第四次更新,Hindawi期刊再次上榜

【SciencePub学术】近期&#xff0c;Scopus数据库迎来本年度第四次更新&#xff01;此次更新后&#xff0c;有89本期刊发生变动&#xff1a; 变动详情 •【新增】75本新增期刊进入Scopus数据库 •【剔除】14本期刊被Scopus数据库剔除 目前Scopus 来源出版物列表&#xff08;…

Apple - Image I/O Programming Guide

翻译自&#xff1a;Image I/O Programming Guide&#xff08;更新时间&#xff1a;2016-09-13 https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/ImageIOGuide/imageio_intro/ikpg_intro.html#//apple_ref/doc/uid/TP40005462 文章目录 …

关于教务排课的那些事

在办学过程中&#xff0c;你是否被如下问题困扰&#xff1f; 1、排课功率低&#xff1a; 为了确保师资资源得到充分利用&#xff0c;教务教师排课要求了解每一个全职和兼职教师&#xff0c;了解每一个人的时刻组织和带班状况&#xff0c;因而在排课的时分需求处理很多的信息&a…

InnoDB存储引擎非常重要的一个机制--MVCC(多版本并发控制)

Mysql是如何实现隔离性的&#xff1f;&#xff08;锁MVCC&#xff09; 隔离性是指一个事务内部的操作以及操作的数据对正在进行的其他事务是隔离的&#xff0c;并发执行的各个事务之间不能相互干扰。隔离性可以防止多个事务并发执行时&#xff0c;可能存在交叉执行导致数据的不…

Sketch语言设置指南:将英文版改成中文版的教程

Sketch版本的转换一直是困扰大家的关键问题。如今UI设计领域的UI设计软件很多&#xff0c;但大部分都是英文版。对于国内英语基础差的设计师来说&#xff0c;使用这样的软件无形中增加了工作量&#xff0c;往往需要在设计编辑的同时查阅翻译。即时设计详细介绍了Sketch英文版如…

夏季高温来袭|危化品如何安全储存?

《危险化学品安全管理条例》第三条 本条例所称危险化学品&#xff0c;是指具有毒害、腐蚀、爆炸、燃烧、助燃等性质&#xff0c;对人体、设施、环境具有危害的剧毒化学品和其他化学品。 随着夏天高温的来袭&#xff0c;炎热的天气对危化品储存威胁巨大&#xff0c;危化品事故也…

3. QGis二次开发项目实践一之解决“无法定位程序输入点“

前言 本章讲述实现本项目实现过程中遇到的QGis二次开发库版本和Qt以及其他动态库的版本匹配问题问题复现 本项目是要作为一个子模块集成到用户的项目中本项目最初的开发环境为QGis3.28+Qt5.15.2,而当时并未问清楚用户开发环境所以交付给用户之后,出现了类似下图的问题 出现该…

m1系列芯片aarch64架构使用docker-compose安装seata

之前看到 DockerHub 上发布了 m1 芯片 aarch64 架构的 seata 镜像, 所以就尝试的安装了下, 亲测可用: 使用该命令查看正在运行的 seata 容器 docker ps | grep seata 一. docker-compose.yml 命令编写 volumes 命令所指定的宿主机映射地址, 需要根据自己的电脑环境更换 环…

windows设置网络共享目录

在一个局域网内的计算机&#xff0c;我们想要共享一些资料&#xff0c;如何操作呢&#xff1f;下面我将用windows系统中的网络共享功能实现。 设置共享目录 首先在一台服务器或者计算机上新建一个文件夹&#xff08;或者直接找一个空的分区&#xff09;作为共享的目录&#x…

神经网络 torch.nn---Non-Linear Activations (ReLU)

ReLU — PyTorch 2.3 documentation torch.nn - PyTorch中文文档 (pytorch-cn.readthedocs.io) 非线性变换的目的 非线性变换的目的是为神经网络引入一些非线性特征&#xff0c;使其训练出一些符合各种曲线或各种特征的模型。 换句话来说&#xff0c;如果模型都是直线特征的…

【云原生】Kubernetes----RBAC用户资源权限

目录 引言 一、Kubernetes安全机制概述 二、认证机制 &#xff08;一&#xff09;认证方式 1.HTTPS证书认证 1.1 证书颁发 1.2 config文件 1.3 认证类型 1.4 Service Account 1.4.1 作用 1.4.2 包含内容 1.4.3 与Secret的关系 2.Bearer Tokens 3.基本认证 三、鉴…

poweroff, reboot流程

poweroff /halt /reboot操作通常由用户空间的systemd或其他初始化系统通过sys_reboot()系统调用触发 sys_reboot() 在内核中定义&#xff0c;通常位于kernel/reboot.c文件中。当传递特定的magic值如 LINUX_REBOOT_CMD_POWER_OFF时&#xff0c;内核会执行关机并尝试触发硬件层面…

修改缓存供应商--EhCache

除了我们默认的缓存形式simlpe之外, 我们其实还有许多其他种类的缓存供应 Ehcache就是其中的一种形式 Ehcache在SpringBoot当中的使用: 其实跟我们之前整合第三方的资源是一样的形式 1>导入依赖: <!-- 更换缓存, 将默认使用的 Simple 更换为Ehcache--> <depe…

低代码专题 | 低代码开发平台一般都有哪些功能和模块?

在上一篇文章中&#xff0c;我们已经对低代码开发平台的概念进行了初步的探讨&#xff0c;认识到了低代码开发平台提高开发效率、降低技术门槛方面的巨大潜力。 然而&#xff0c;要真正掌握并应用低代码开发平台&#xff0c;还需要深入了解其背后的功能与模块构成。这篇就对低…

从零开始:如何用Electron将chatgpt-plus.top 打包成EXE文件

文章目录 从零开始&#xff1a;如何用Electron将chatgpt-plus.top 打包成EXE文件准备工作&#xff1a;Node.js和npm国内镜像加速下载初始化你的Electron项目创建你的Electron应用运行你的Electron应用为你的应用设置图标打包成EXE文件结语 从零开始&#xff1a;如何用Electron将…