Linux--Socket编程基础

一、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 就可以了,当然如果你的 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 地址和端口号)。因为对于客户端来说,它与服务器进行通信,首先需要知道 服务器的 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()关闭套接字。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习之加宽全连接

1.Functional API 搭建神经网络模型 1.1.利用Functional API编写宽深神经网络模型进行手写数字识别 import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.model_selection import train_test_spli…

【免费Web系列】JavaWeb实战项目案例三

这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 部门管理开发 1. 删除部门 1.1 需求分析 删除部门数据。在点击 "删除" 按钮&#xff0c;会根据ID删除部门数据。 了解了需求之后&#xff0c;我们再看看接口文档中&#xff0c;关于删除部门…

还没搞懂作用域、执行上下文、变量提升?看这篇就够啦

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热爱技术和分享&#xff0c;欢迎大家交流&#xff0c;一起学习进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 目录 作用域&#xff08;Scope&#xff09; 全局作用域 函数作用域 块级作用域…

编译选项导致的结构体字节参数异常

文章目录 前言问题描述原因分析问题解决总结 前言 在构建编译工程时&#xff0c;会有一些对应的编译配置选项&#xff0c;不同的编译器&#xff0c;会有对应的配置项。本文介绍GHS工程中编译选项配置不对应导致的异常。 问题描述 在S32K3集成工程中&#xff0c;核1的INP_SWC…

【并发程序设计】15.信号灯(信号量)

15.信号灯(信号量) Linux中的信号灯即信号量是一种用于进程间同步或互斥的机制&#xff0c;它主要用于控制对共享资源的访问。 在Linux系统中&#xff0c;信号灯作为一种进程间通信&#xff08;IPC&#xff09;的方式&#xff0c;与其他如管道、FIFO或共享内存等IPC方式不同&…

c++ 哈希 unordered_map unordered_set 的学习

1. unordered 系列 在 c98 中&#xff0c; STL 提供了底层是红黑树结构的一系列关联式容器&#xff0c;set 和 map 的查询效率可以达到 log2N&#xff0c;红黑树最差的情况也只是需要比较红黑树的高度次&#xff0c;当节点数量非常多时&#xff0c;查找一个节点还需要比较几十…

护肤品美妆商城小程序的作用是什么

经营美妆的方式多种多样&#xff0c;商场街边、电商平台、微商等&#xff0c;无论厂商品牌还是经销商批发零售都有大量目标群体&#xff0c;客户在哪里商家就应该在哪里&#xff0c;私域生意模式&#xff0c;商家需要线上多渠道获客转化和提高营收。 运用【雨科】平台搭建护肤…

PatchEmbed

PatchEmbed 是用于计算机视觉任务的神经网络层&#xff0c;特别是在Vision Transformer (ViT) 模型中使用。它负责将输入的图像分割成固定大小的图像块&#xff08;patches&#xff09;&#xff0c;并将这些图像块线性嵌入到高维空间中。这是Vision Transformer处理图像的方式&…

JVM虚拟机性能监控工具

命令行工具 jps 虚拟机进程状况查询工具 jps(JVM Process Status Tool)&#xff0c;可以列出正在运行的虚拟机进程&#xff0c;并显示虚拟机执行主类名称或者jar文件名&#xff0c;还有这些进程的本地虚拟机唯一ID(LVMID&#xff0c;Local Virtual Machine Identifier)。 # …

Vue.js 与 TypeScript(1) :项目配置、props标注类型、emits标注类型

像 TypeScript 这样的类型系统可以在编译时通过静态分析检测出很多常见错误。这减少了生产环境中的运行时错误&#xff0c;也让我们在重构大型项目的时候更有信心。通过 IDE 中基于类型的自动补全&#xff0c;TypeScript 还改善了开发体验和效率。 一、项目配置 在使用 npm cr…

AI 网页解锁器,用于网页抓取一切 | 最快的验证码解决服务

想象一下&#xff0c;解锁互联网的全部潜力&#xff0c;数据自由流动&#xff0c;没有任何障碍阻挡你获取所需信息。在网络爬虫的世界里&#xff0c;这个梦想常常会遇到障碍&#xff1a;CAPTCHA和反机器人措施&#xff0c;这些措施旨在保护网站免受自动化访问的侵害。但如果有一…

蓝桥杯软件测试-十五届模拟赛2期题目解析

十五届蓝桥杯《软件测试》模拟赛2期题目解析 PS 需要第十五界蓝桥杯模拟赛2期功能测试模板、单元测试被测代码、自动化测试被测代码请加&#x1f427;:1940787338 备注&#xff1a;15界蓝桥杯省赛软件测试模拟赛2期 题目1&#xff1a;功能测试题目 1&#xff08;测试用例&…

60 关于 SegmentFault 的一些场景 (1)

前言 呵呵 此问题主要是来自于 帖子 月经结贴 -- 《Segmentation Fault in Linux》 这里主要也是 结合了作者的相关 case, 来做的一些 调试分享 当然 很多的情况还是 蛮有意思 本文主要问题如下 1. 访问可执行文件中的 只读数据 2. 访问不存在的虚拟地址 3. 访问内核地址…

【机器学习】基于OpenCV和TensorFlow的MobileNetV2模型的物种识别与个体相似度分析

在计算机视觉领域&#xff0c;物种识别和图像相似度比较是两个重要的研究方向。本文通过结合深度学习和图像处理技术&#xff0c;基于OpenCV和TensorFlow的MobileNetV2的预训练模型模&#xff0c;实现物种识别和个体相似度分析。本文详细介绍该实验过程并提供相关代码。 一、名…

Python代码:二十六、反转列表

1、题目 描述 小明有一个列表记录了各个朋友的喜欢的数字&#xff0c;num [3, 5, 9, 0, 1, 9, 0, 3]&#xff0c;请你帮他创建列表&#xff0c;然后使用reverse函数将列表反转输出。 输入描述&#xff1a; 无 输出描述&#xff1a; 第一行输出创建好的原始的列表&#x…

typescript --object对象类型

ts中的object const obj new Object()Object 这里的Object是Object类型&#xff0c;而不是JavaScript内置的Object构造函数。 这里的Object是一种类型&#xff0c;而Object()构造函数表示一个值。 Object()构造函数的ts代码 interface ObjectConstructor{readonly prototyp…

【JavaEE】JVM中垃圾回收机制详解

一.垃圾回收的基本概念 1.什么是垃圾回收机制. JVM&#xff08;Java虚拟机&#xff09;垃圾回收机制是Java内存管理的重要组成部分&#xff0c;它负责自动回收程序中不再使用的对象所占用的内存空间。这样可以有效地防止内存泄漏和内存溢出问题&#xff0c;提高程序的稳定性和…

电脑死机问题排查

情况描述&#xff1a;2024年6月2日下午16&#xff1a;04分电脑突然花屏死机&#xff0c;此情况之前遇到过三次&#xff0c;认为是腾讯会议录屏和系统自带录屏软件冲突导致。 报错信息&#xff1a;应用程序-特定 权限设置并未向在应用程序容器 不可用 SID (不可用)中运行的地址…

GPT-4o有点坑

GPT-4o有点坑 0. 前言1. GPT-4o简介2. GPT-4o带来的好处2.1 可以上传图片和文件2.2 更丰富的功能以及插件 3. "坑"的地方3.1 使用时间短3.2 GPT-4o变懒了 4. 总结 0. 前言 原本不想对GPT-4o的内容来进行评论的&#xff0c;但是看了相关的评论一直在说&#xff1a;技…

全国水系数据(更新到2024年5月)

上海市水系数据地图可视化 水系数据线图层&#xff08;小河/溪流、江/河、运河、下水道/排水管&#xff09; 水系数据面数据&#xff08;水域、水库、河岸、湿地&#xff09; 水系数据字段说明 可视化预览 北京市水系可视化 上海市水系可视化 广州市水系可视化 深圳市水系可视化…