详解UDP/TCP套接字

详解UDP/TCP套接字

预备知识

理解源IP地址和目的IP地址

在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址

  • 源IP地址:发送主机的IP地址。
  • 目的IP地址:接收主机的IP地址。

认识端口号

端口号(port)是传输层协议的内容.

  • 端口号是一个2字节16位的整数;
  • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
  • IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
  • 一个端口号只能被一个进程占用

❓我们之前在学习系统编程的时候, 学习了 pid 表示唯一一个进程; 此处我们的端口号也是唯一表示一个进程. 那么还要用端口号呢?

💡1、解耦2、例如客户端需要找到服务器进程,因为进程每次创建销毁pid是会变的,而网络编程弄了个端口号,直接写死了是哪个进程,这样每次都能找到。

UDP/TCP套接字

TCP和UDP的性质

TCP:传输层协议 有连接 可靠传输 面向字节流

UDP:传输层协议 无连接 不可靠传输 面向数据报

网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏
移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

大小端详情请看:C语句:数据存储_说明中的语句找数据存储-CSDN博客

1、TCP/IP协议规定,网络数据流应采用大端字节序,即低地址存高字节,不管这台主机是大端机还是小端机

2、如果当前发送主机是小端, 就需要先将数据转成大端; 反之直接发送即可(不管本机是大端还是小端,最好都转换一下,便于日后跨平台、跨主机的需要)

3、发送主机和接收主机都是按照低地址到高地址发送/接收数据;

网络字节序和主机字节序的转换:

#include <arpa/inet.h>
 
uint32_t htonl(uint32_t hostlong);  //主机转网络
uint16_t htons(uint16_t hostshort); //网络转主机
uint32_t ntohl(uint32_t netlong);   //按4字节为单位主机转网络
uint16_t ntohs(uint16_t netshort);  //按2字节网络转主机

这些函数名很好记,h表示主机,n表示网络,l表示32位长整数,s表示16位短整数。
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回

对ip地址进行转换:

INET(3) 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);//将字符串转uint32_t的同时转为网络字节序
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);//将32位IPv4地址(in_addr结构体)转换成点分十进制字符串形式的IP地址
struct in_addr inet_makeaddr(int net, int host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);

socket编程接口

socket常见API:

创建 socket 套接字 (TCP/UDP, 客户端 + 服务器)

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

绑定端口号 (TCP/UDP, 服务器)

int bind(int socket, const struct sockaddr *address, socklen_t address_len);

开始监听socket (TCP, 服务器)

int listen(int socket, int backlog);

接收请求 (TCP, 服务器)

int accept(int socket, struct sockaddr* address, socklen_t* address_len);

建立连接 (TCP, 客户端)

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

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX Domain
Socket. 然而, 各种网络协议的地址格式并不相同.

套接字不仅支持跨网络的进程间通信(网络套接字),还支持本地的进程间通信(域间套接字)。

为了让套接字的网络通信和本地通信能够使用同一套函数接口,于是就出现了sockeaddr结构体,该结构体与sockaddr_in和sockaddr_un的结构都不相同,但这三个结构体头部的16个比特位都是一样的,这个字段叫做协议家族。

image-20240226194207464

IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16
位端口号和32位IP地址.
IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,
不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好
处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为
参数

sockaddr 结构:

image-20240226194727372

sockaddr_in 结构:

image-20240226194747933

虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主
要有三部分信息: 地址类型, 端口号, IP地址.
in_addr结构

image-20240226194815413

in_addr用来表示一个IPv4的IP地址. 其实就是一个32位的整数;

UDP套接字:

UDP服务器创建流程

1、创建套接字(socket)

2、绑定端口号和IP地址。这个端口号是写死的,这个IP地址0.0.0.0或htonl(INADDR_ANY)两种写法(bind)

3、发送、接收数据,对数据进行处理(recvfrom/sendto)

创建socket套接字:
SOCKET(2)      
#include <sys/types.h> 
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

用途:创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)

参数:

domain:指定套接字的协议族。常见的协议族有AF_INET(IPv4网络通信)、AF_UNIX/AF_LOCAL(本地通信)
type:指定套接字的类型。常见的类型有SOCK_STREAM(流套接字)和SOCK_DGRAM(数据报套接字)。
protocol:给0就行。【指定套接字使用的协议。常见的协议有IPPROTO_TCP(TCP协议)和IPPROTO_UDP(UDP协议)】
调用成功返回值:返回一个文件描述符;

调用失败返回值:返回-1,设置error变量以指示原因。

绑定套接字对应的IP地址、端口号:
BIND(2)        
#include <sys/types.h>       
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

用途:将一个套接字(socket)与一个特定的IP地址和端口号绑定

参数:

sockfd:套接字描述符,即要绑定的套接字。
addr:一个指向 sockaddr 结构体的指针,该结构体包含了IP地址和端口号等信息。
addrlen:sockaddr 结构体的大小
返回值:返回值为0,则表示函数执行成功;否则,返回值为-1,表示函数执行失败。在函数执行失败时,可以通过 errno 全局变量获取错误码,以便进行错误处理。

img

客户端、服务器数据的接收与发送

UDP 套接字是无连接协议,必须使用recvfrom函数接收数据,sendto函数发送数据:

RECV(2)
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

用途:从已连接的socket中接收数据,并将数据存储到指定的缓冲区中。

参数:

sockfd:已经建立好连接的socket。
buf:指向接收数据存放的缓冲区。
len:缓冲区长度。
flags:一般为0,阻塞式读取。
src_addr:(输出参数)返回发送方的地址信息。
addrlen:(输出参数)地址信息的长度。
调用成功返回值:函数成功接收到数据时,它会返回接收到的字节数

调用失败返回值:发生错误,则返回-1,并设置errno变量以指示错误类型。

SEND(2)      
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
          	const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

用途:该函数用于将数据报发送到指定的目的地。

参数:

sockfd:表示要发送数据的套接字文件描述符。
buf:指向要发送的数据缓冲区。
len:表示要发送的数据长度。
flags:表示发送数据的选项,常用的选项有MSG_DONTWAIT和MSG_NOSIGNAL。
dest_addr:(输入参数)指向目的地址的结构体指针,表示要发给谁。
addrlen:(输入参数)表示目的地址结构体的长度。
返回值:成功发送的字节数,如果返回值为-1,则表示发送失败,具体错误码可以通过errno变量获取。

TCP套接字:

TCP服务器创建流程

1、创建监听套接字(socket)

2、绑定端口号和IP地址。这个端口号是写死的,这个IP地址0.0.0.0或htonl(INADDR_ANY)两种写法(bind)

3、服务器设置socket为监听状态(listen)

4、服务器获取客户端连接请求(accept)

5、文件操作进行读写通信(read/write)

TCP客户端创建流程

1、创建套接字(socket)

2、客户端需要bind,但是客户端的绑定不需要我们自己写,操作系统会去绑定;(无需程序员bind)

3、客户端发起连接请求(connect)

4、文件操作进行读写通信(read/write)

创建socket套接字

同UDP创建套接字的方法,只不过在传参时UDP传入SOCK_DGRAM(数据报),而TCP传入SOCK_STREAM(字节流)

_sockfd=socket(AF_INET,SOCK_DGRAM,0);//UDP网络通信+数据报
_listenfd=socket(AF_INET,SOCK_STREAM,0);//TCP网络通信+字节流
绑定套接字对应的IP地址、端口号

同UDP。

服务器设置socket为监听状态

因为TCP是有连接的协议,需要使用listen函数将一个套接字设置为监听状态。

LISTEN(2)   
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);

用途: listen函数用于将一个套接字(socket)设置为监听模式,以便接受客户端的连接请求。

参数:

sockfd:需要设置为监听模式的套接字描述符
backlog:指定等待连接队列的最大长度,即同时能够处理的客户端连接请求的最大数量,超过这个数量的连接请求将被拒绝
调用成功返回值:成功返回0

调用失败返回值:失败返回-1,并设置errno变量以指示错误类型。

服务器获取客户端连接请求
ACCEPT(2) 
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

用途:accept函数用于从处于监听状态的套接字队列中取出一个已经完成了三次握手的连接请求,并创建一个新的套接字用于与客户端进行通信。

参数:

sockfd:处于监听状态的套接字描述符
addr:指向一个sockaddr结构体的指针,用于存储客户端的地址信息
addrlen:addr结构体的长度,需要在调用前初始化为sizeof(struct sockaddr)
调用成功返回值:返回一个新的套接字描述符,用于与客户端进行通信,这个新的套接字描述符是唯一的,只能用于与这个客户端进行通信。

调用失败返回值:失败返回-1,并设置errno变量以指示错误类型。

客户端发起连接请求
CONNECT(2)   
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

用途:connect函数用于客户端与服务器建立连接,自动帮客户端的套接字与其ip、port进行绑定。

参数:

sockfd:需要连接的套接字描述符。
addr:指向目标地址的指针,包括目标计算机的IP地址和端口号。
addrlen:addr结构体的长度,需要在调用前初始化为sizeof(struct sockaddr)
调用成功返回值:返回0

调用失败返回值:失败返回-1,并设置errno变量以指示错误类型。

守护进程:

守护进程也称精灵进程,本质上是一个孤儿进程。

​ 一个会话只有一个前台程序和n个后台程序,作业之间可以前后台转换。这样的任务可能会收到用户登录和注销而被清理。

​ 如果想让服务端不受用户登录注销的影响,就必须让服务端自成会话,自成进程组,使其与终端设备的状态无关,可以一直运行的进程。这样的进程就被称为守护进程。

img

手搓用setsid

SETSID(2)      
#include <unistd.h>
pid_t setsid(void);

用途:使用setsid可以使调用进程成为一个新的会话领导者,并且不会受到终端的控制。这对于守护进程和后台进程非常有用,因为它们需要在后台运行,并且不希望受到终端的影响。调用setsid后,调用进程的进程组ID将变为新会话的组ID,该进程成为新会话的领导进程,并且不再有控制终端。进程组的组长不能调用setsid()函数来创建一个新的会话

参数:无

调用成功返回值:返回新会话的会话ID

调用失败返回值:失败返回-1

当然Linux自带生成守护进程的接口:

DAEMON(3)  
#include <unistd.h>
int daemon(int nochdir, int noclose);

用途:该函数的意义在于将进程转变为守护进程,守护进程是在后台运行的进程,通常不与控制台交互,而是在后台执行某些任务,如网络服务器等。通过调用该函数,可以实现以下功能:

将当前进程的父进程置为init进程(进程id为1),从而脱离原有的进程组和会话。
将当前进程的工作目录切换到根目录下,以避免守护进程因为当前工作目录被卸载等原因导致崩溃。
关闭标准输入、输出和错误输出,以避免守护进程输出信息到控制台,从而影响用户体验。
参数:

nochdir:是否改变当前工作目录。如果为0,则将当前工作目录切换到根目录下,否则保持不变。
noclose:是否关闭标准输入、标准输出、标准错误的文件描述符。如果为0,则不关闭,否则关闭。
调用成功返回值:返回0

调用失败返回值:失败返回-1,并设置errno变量。


>  用途:该函数的意义在于将进程转变为守护进程,守护进程是在后台运行的进程,通常不与控制台交互,而是在后台执行某些任务,如网络服务器等。通过调用该函数,可以实现以下功能:
>
> 将当前进程的父进程置为init进程(进程id为1),从而脱离原有的进程组和会话。
> 将当前进程的工作目录切换到根目录下,以避免守护进程因为当前工作目录被卸载等原因导致崩溃。
> 关闭标准输入、输出和错误输出,以避免守护进程输出信息到控制台,从而影响用户体验。
> 参数:
>
> nochdir:是否改变当前工作目录。如果为0,则将当前工作目录切换到根目录下,否则保持不变。
> noclose:是否关闭标准输入、标准输出、标准错误的文件描述符。如果为0,则不关闭,否则关闭。
> 调用成功返回值:返回0
>
> 调用失败返回值:失败返回-1,并设置errno变量。

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

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

相关文章

NVMFS5113PLWFT1G汽车级功率MOSFET 60V 10A/64A满足AEC-Q101标准

AEC-Q101认证标准详细解读&#xff1a; AEC-Q101是一种汽车电子元件可靠性标准&#xff0c;由汽车电子委员会&#xff08;Automotive Electronics Council&#xff0c;简称AEC&#xff09;制定。该标准旨在确保在汽车环境中使用的电子元件具有足够的可靠性和耐久性。 AEC-Q10…

Docker Compose实战指南:让容器管理变得简单而强大

&#x1f9e8;个人主页&#xff1a;明明跟你说过 &#x1f6a9;欢迎&#x1f397;️点赞&#x1f638;关注❤️分享 &#x1f638;希望本文能够对您有所帮助&#xff0c;如果本文有不足之处&#xff0c;或您有更好的建议、见解&#xff0c;欢迎在评论区留下您的看法&#xff0c…

力扣hot100题解(python版29-32题)

29、删除链表的倒数第N个结点 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&a…

苍穹外卖Day03——总结3

前期文章 文章标题地址苍穹外卖Day01——总结1https://lushimeng.blog.csdn.net/article/details/135466359苍穹外卖Day01——解决总结1中存在的问题https://lushimeng.blog.csdn.net/article/details/135473412苍穹外卖Day02——总结2https://lushimeng.blog.csdn.net/articl…

Node.js中的并发和多线程处理

在Node.js中&#xff0c;处理并发和多线程是一个非常重要的话题。由于Node.js是单线程的&#xff0c;这意味着它在任何给定时间内只能执行一个任务。然而&#xff0c;Node.js的事件驱动和非阻塞I/O模型使得处理并发和多线程变得更加高效和简单。在本文中&#xff0c;我们将探讨…

gRPC知识归档

文章目录 gRPC知识归档gRPC原理什么是gRPCgRPC的特性gRPC支持语言gRPC使用场景gRPC设计的动机和原则 数据封装和数据传输问题网络传输中的内容封装和数据体积问题JSONProtobuf&#xff08;微服务之间的服务器调用&#xff0c;一般采用二进制序列化&#xff0c;比如protobuf&…

TVM 和模型优化的概述(1)

文章目录 1. 从 Tensorflow、PyTorch 或 Onnx 等框架导入模型&#xff08;model&#xff09;。2.翻译成 Relay3. lower 到 张量表达式。4. 使用 auto-tuning 模块 AutoTVM 或 AutoScheduler 搜索最佳 schedule。5. 选择最佳配置进行模型编译。6. lower 到 TIR。7. 编译成机器码…

计算机网络:数据链路层知识点汇总

文章目录 一、数据链路层功能概述二、封装成帧和透明传输三、差错控制&#xff08;检错编码&#xff09;四、差错控制&#xff08;纠错编码&#xff09;五、流量控制与可靠传输机制六、停止-等待协议七、后退N帧协议&#xff08;GBN&#xff09;八、选择重传协议&#xff08;SR…

SAP PP学习笔记04 - BOM1 - BOM创建,用途,形式,默认值,群组BOM等

本章开始讲BOM的内容。 1&#xff0c;BOM的定义 &#xff08;Bill of Materials&#xff09; 物料清单&#xff08;Bill of Materials&#xff0c;简称BOM&#xff09;是描述企业产品组成的技术文件。在加工资本式行业&#xff0c;它表明了产品的总装件、分装件、组件、部件、…

小程序固定头部实现:van-nav-bar插件

用的是Vant的NavBar插件&#xff1a; https://youzan.github.io/vant-weapp/#/nav-bar#wai-bu-yang-shi-lei 效果图 页面使用&#xff0c;放开注释的地方就可以显示左边按钮 <van-nav-bar title"精益成本核算" fixed"true" placeholder"true&qu…

Vmware Esxi 部署Mac OS虚拟机

Vmware Esxi在创建虚拟机的时候是有Mac OS选项的&#xff0c;但是实际创建时&#xff0c;选择ISO开机后一直反复引导&#xff0c;是有问题的&#xff0c;原因是需要先解锁&#xff0c;需要在ESXI主机上修改配置并重启。 首先找到管理-服务-TSM-ssh&#xff0c;点击启动&#x…

SecureCRT for Mac/win:保障数据安全的专业终端SSH工具软件

SecureCRT for Mac/win是一款广受欢迎的专业终端SSH工具软件&#xff0c;为用户提供了强大的加密通信和数据安全功能&#xff0c;使其成为网络管理人员、系统管理员和开发人员的首选工具。无论是在Mac还是Windows操作系统下&#xff0c;SecureCRT都能够帮助用户轻松地进行远程访…

数字生活的未来:Web3如何改变我们的日常

随着技术的飞速发展&#xff0c;我们的生活正变得日益数字化。而Web3作为一种新型的互联网模式&#xff0c;正以前所未有的方式改变着我们的日常生活。在本文中&#xff0c;我们将深入探讨Web3技术的特点以及它如何改变我们的数字生活。 1. Web3的特点 Web3是基于区块链技术和…

uniapp 部署h5,pdf预览

1.hubuilderx 打包h5。 2.上传部署包到服务器。 解压部署包&#xff1a;unzip h5.zip 。 3.nginx配置。 user root; worker_processes 1; #worker_cpu_affinity 0001 0010 0100 1000; #error_log logs/error.log; #error_log logs/error.log notice; error_log /var/l…

ChatGPT-4 AI 绘图魔力释放

最近刚开通了 ChatGPT4&#xff0c;正好要设计一个网站图标&#xff0c;想测试一下它AI绘图的能力&#xff0c;让它根据文字描述生成一个想象中的图标 &#xff08;PS&#xff1a;如果想体验 GPT4 文生图&#xff0c;可以看这个教程 如何升级 ChatGPT 4.0&#xff09; 第1次交…

nginx使用详解--动静分离

什么是动静分离&#xff1f; 为了提高网站的响应速度&#xff0c;减轻程序服务器&#xff08;Tomcat&#xff0c;Jboss等&#xff09;的负载&#xff0c;对于静态资源&#xff0c;如图片、js、css等文件&#xff0c;可以在反向代理服务器中进行缓存&#xff0c;这样浏览器在请…

react使用@reduxjs/toolkit和react-redux实现store状态管理

一、概述 reduxjs/toolkit和react-redux是用于在React应用中管理全局状态的工具库 1、reduxjs/toolkit&#xff1a; reduxjs/toolkit是Redux官方推荐的工具库&#xff0c;是对 Redux 的二次封装&#xff0c;它提供了一些便捷的API和工具&#xff0c;帮助开发者更快速地编写R…

喜迎乔迁,开启新章 ▏易我科技新办公区乔迁庆典隆重举行

2024年1月18日&#xff0c;易我科技新办公区乔迁庆典在热烈而喜庆的氛围中隆重举行。新办公区的投入使用&#xff0c;标志着易我科技将以崭新姿态迈向新的发展阶段。 ▲ 易我科技新办公区 随着公司业务的不断发展和壮大&#xff0c;为了更好地适应公司发展的需要&#xff0c;…

mysql python学习笔记

mysql 基础概念 1.一个表格一般包含一个主建 2.可有多个主见,叫组合主见 3.可以有foreign key 用于链接外部表格的主建 外键目的&#xff1a; 这个约束的目的是确保当前表中的外键列&#xff08;JNO列&#xff09;的值必须存在于另一个表&#xff08;J’表&#xff09;的主键…

kswapd0挖矿病毒攻击记录

文章目录 一、起因与病毒分析1、起因2、阿里云告警2.1 恶意脚本代码执行12.2 恶意脚本代码执行22.3恶意脚本代码执行32.4 恶意脚本代码执行4 3、病毒简单分析3.1 病毒的初始化3.2 病毒本体执行 4、总结 二、ubuntu自救指南1、病毒清理2、如何防御 一、起因与病毒分析 1、起因 …