📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨
文章目录
🏳️🌈一、socket
1.1、理解源 IP 和目的 IP
- IP 在网络中,用来标识主机的唯一性
- 注意:后面我们会讲 IP 的分类,后面会详细阐述 IP 的特点
但是这里要思考一个问题:将数据传输到主机是目的吗?
不是的。因为数据是给人用的。比如:聊天是人在聊天,下载是人在下载,浏览网页是人在浏览?但是人是怎么看到聊天信息的呢?怎么执行下载任务呢?怎么浏览网页信息呢?通过启动的 qq,迅雷,浏览器。而启动的 qq,迅雷,浏览器都是进程。换句话说,进程是人在系统中的代表,只要把数据给进程,人就相当于就拿到了数据。
所以:数据传输到主机不是目的,而是手段。到达主机内部,在交给主机内的进程,才是目的。但是系统中,同时会存在非常多的进程,当数据到达目标主机之后,怎么转发给目标进程?
这就要在网络的背景下,在系统中,标识主机的唯一性。
1.2 认识端口号
- 端口号(port)是传输层协议的内容.
- 端口号是一个 2 字节 16 位的整数;
- 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
- IP 地址 + 端口号能够标识网络上的某一台主机的某一个进程;
- 一个端口号只能被一个进程占用.
1.2.1 端口号范围划分
0 - 1023
: 知名端口号, HTTP, FTP, SSH 等这些广为使用的应用层协议, 他们的端口号都是固定的.1024 - 65535
: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的.
1.2.2 理解 “端口号” 和 “进程 ID”
我们之前在学习系统编程的时候, 学习了 pid 表示唯一一个进程; 此处我们的端口号也是唯一表示一个进程. 那么这两者之间是怎样的关系?
另外, 一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定;
1.2.3 理解源端口号和目的端口号
传输层协议(TCP 和 UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 “数据是谁发的, 要发给谁”;
1.2.4 理解 socket
- 综上,
IP
地址用来标识互联网中唯一的一台主机,port
用来标识该主机上唯一的一个网络进程 IP + Port
就能表示 互联网中唯一的一个进程- 所以,通信的时候,本质是两个互联网进程代表人来进行通信,{srcIp,srcPort,dstIp,dstPort}这样的 4 元组就能标识互联网中唯二的两个进程
- 所以,网络通信的本质,也是进程间通信
- 我们把
ip + port
叫做套接字socket
1.3 网络字节序
我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分 . 那么如何定义网络数据流的地址呢?
- 发送主机 通常将发送缓冲区中的数据按内存地址 从低到高 的顺序发出;
- 接收主机 把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址 从低到高 的顺序保存;
因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
TCP/IP 协议规定,网络数据流应采用大端字节序,即低地址高字节.
- 不管这台主机是大端机还是小端机, 都会按照这个 TCP/IP 规定的网络字节序来发送/接收数据;
- 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;
为使网络程序具有可移植性,使同样的 C 代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
#include <arpa/inet.h>
// 将32位无符号整数从主机字节顺序转换为网络字节顺序。
uint32_t htonl(uint32_t hostlong);
// 将16位无符号整数从主机字节顺序转换为网络字节顺序。
uint16_t htons(uint16_t hostshort);
// 将32位无符号整数从网络字节顺序转换为主机字节顺序。
uint32_t ntohl(uint32_t netlong);
// 将16位无符号整数从网络字节顺序转换为主机字节顺序。
uint16_t ntohs(uint16_t netshort);
- h 表示
host
- n 表示
network
- l 表示
32 位长整数
- s 表示
16 位短整数
1.4 Socket 编程接口
1.4.1 socket 常见 API
1. socket() - 创建一个套接字(socket)(TCP/UDP, 客户端 + 服务器)
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
参数
- domain:指定协议族(Protocol Family),也称为协议域。常用的协议族有:
AF_INET
:IPv4 Internet协议。AF_INET6
:IPv6 Internet协议。AF_LOCAL(或AF_UNIX)
:本地通信协议,用于同一台机器上的进程间通信。- 其他协议族还包括
AF_ROUTE、AF_AX25、AF_ATMPVC
等,但它们在网络通信中较为少见。
- type:指定套接字类型(Socket Type)。常见的套接字类型有:
SOCK_STREAM
:流式套接字,提供序列化的、可靠的、双向连接的字节流。通常使用TCP协议。SOCK_DGRAM
:数据报套接字,支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务。通常使用UDP协议。SOCK_RAW
:原始套接字,允许对较低层协议(如IP、ICMP)直接访问。常用于检验新的协议实现或访问现有服务中配置的新设备。- 其他类型还包括
SOCK_SEQPACKET、SOCK_RDM、SOCK_PACKET
等,但它们在特定场景下使用。
- protocol:指定某个协议的特定类型
- 通常,当protocol参数为0时,系统会自动选择type类型对应的默认协议。但有些协议可能有多种特定的类型,此时需要设置这个参数来选择特定的类型。
返回值
- 如果socket函数调用成功,它会返回一个非负的文件描述符(File Descriptor),该描述符唯一标识一个套接字。
- 如果调用失败,socket函数会返回-1,并设置errno来指示错误原因。
2. bind() 将一个套接字(socket)与一个特定的IP地址和端口号绑定在一起。
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd
:要绑定的套接字的文件描述符。addr
:指向包含地址信息的结构体的指针。通常使用struct sockaddr_in表示IPv4地址,或struct sockaddr_in6表示IPv6地址。addrlen
:地址结构体的大小。
返回值:
- 如果bind函数调用成功,返回0。
- 如果调用失败,返回-1,并设置errno来指示错误原因。
1.5 sockaddr 结构
socket API 是一层抽象的网络编程接口,适用于各种底层网络协议 ,如 IPv4、IPv6,以及后面要讲的 UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同.
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 结构体指针做为参数;
虽然 socket api
的接口是 sockaddr,
但是我们真正在基于 IPv4 编程时, 使用的数据结构是 sockaddr_in这个结构里主要有三部分信息: 地址类型, 端口号, IP 地址.
in_addr 用来表示一个 IPv4 的 IP 地址. 其实就是一个 32 位的整数
👥总结
本篇博文对 【LInux网络】socket 编程 - 从ip端口到接口详解 做了一个较为详细的介绍,不知道对你有没有帮助呢
觉得博主写得还不错的三连支持下吧!会继续努力的~