阅读导航
引言
在上一篇文章中,我们深入探讨了Linux网络的基础知识和它的发展历史,为读者揭开了Linux网络技术演变的序幕。我们了解到,Linux网络技术的发展不仅促进了操作系统本身的成熟,还对整个互联网的进步产生了深远的影响。随着网络技术的不断进步,Linux系统在网络通信方面的应用也变得日益重要,尤其是网络编程领域。因此,继续沿着这一主题深入,本篇文章将专注于Linux网络编程中的一个核心概念——套接字(Socket)。
通过本文的学习,读者不仅能够掌握Linux网络编程的核心技术——套接字,还能深化对Linux网络技术运作机制的理解,为后续深入研究Linux网络编程打下坚实的基础。我们期待本篇文章能为您在Linux网络编程旅程中提供有价值的指导和帮助。
一、套接字基本概念
套接字是网络软件开发中不可或缺的一部分,它为进程间通信提供了一种抽象的接口。在Linux系统中,套接字编程尤为重要,因为它是实现各种网络应用,如Web服务器、邮件服务器和文件传输等功能的基础。通过对套接字的深入理解和应用,开发人员可以设计出高效、稳定且安全的网络应用程序。
使用套接字进行网络通信的基本步骤包括创建套接字、绑定地址、监听连接(对于服务器端)、连接远程主机(对于客户端)、发送和接收数据等操作。套接字编程涉及到一系列系统调用和函数,如socket()
、bind()
、listen()
、connect()
、send()
、recv()
等,开发人员可以利用这些接口实现各种网络应用,下一篇文章会详细介绍相关函数。
二、源IP地址和目的IP地址
源IP地址和目的IP地址是在网络通信中非常重要的概念,顾名思义它们用于标识数据包的来源和目的地。下面将详细介绍这两个概念:
-
源IP地址(Source IP Address):
- 源IP地址指的是数据包的发送方的IP地址,用于标识数据包的来源。在IPv4协议中,源IP地址由32位二进制数表示,通常以点分十进制的格式显示,如
xxx.xxx.xxx.xxx
。在IPv6协议中,源IP地址由128位二进制数组成。 - 源IP地址的作用是确保接收方可以知道数据包的来源,从而能够进行响应或者建立回复连接。此外,源IP地址也被用来进行路由,帮助网络设备确定数据包的传输路径。
- 源IP地址指的是数据包的发送方的IP地址,用于标识数据包的来源。在IPv4协议中,源IP地址由32位二进制数表示,通常以点分十进制的格式显示,如
-
目的IP地址(Destination IP Address):
- 目的IP地址指的是数据包的接收方的IP地址,用于标识数据包的目的地。与源IP地址类似,目的IP地址也由32位二进制数(IPv4)或128位二进制数(IPv6)表示。
- 目的IP地址的作用是指示网络设备将数据包传送到相应的目标主机,确保数据包能够到达正确的接收方。
三、端口号
1. “端口号” 和 “进程ID”
我们之前在学习系统编程的时候, 学习了 pid
表示唯一一个进程; 此处我们的端口号也是唯一表示一个进程. 那么这两者之间是怎样的关系呢?
-
端口号(Port Number):
- 端口号是网络通信中用于标识特定应用程序或服务的数字标识符。在TCP/IP协议中,端口号被分为两类:知名端口和动态端口。
- 知名端口(Well-known Ports):指范围从0到1023的端口号,通常用于标识一些常见的网络应用和服务,如HTTP(端口号80)、FTP(端口号21)、SSH(端口号22)等。
- 动态端口(Dynamic Ports):指范围从1024到65535的端口号,用于临时分配给客户端应用程序或由操作系统自动生成。
-
进程ID(PID):
- 进程ID是操作系统中用于唯一标识每个正在运行的进程的数字标识符。每个进程都有一个独一无二的进程ID,用于操作系统管理和调度进程。
- 进程ID在操作系统中扮演着重要的角色,可以用于查找和识别特定的进程,监控进程的状态,以及进行进程间的通信和管理。
在网络通信中,端口号和进程ID常常联系在一起,因为网络通信是通过端口号来标识目标应用程序的。当应用程序启动并监听特定端口时,操作系统会为该应用程序分配一个进程ID,并将该端口与该进程关联起来,从而实现数据的发送和接收。
🚨🚨注意:一个进程可以绑定多个端口号, 但是一个端口号不能被多个进程绑定;
2. 源端口号和目的端口号
在本质上“源端口号和目的端口号”跟“源IP地址和目的IP地址”是相同的。源端口号(Source Port Number)和目的端口号(Destination Port Number)是在网络通信中用于标识数据包发送方和接收方应用程序或服务的端口号。
-
源端口号:
- 源端口号是指发送数据包的应用程序或服务所使用的端口号。它是在数据包从发送方(客户端)发出时指定的。
- 源端口号帮助接收方识别数据包的来源,并将响应数据包发送回正确的源端口。
-
目的端口号:
- 目的端口号是指接收数据包的应用程序或服务所监听的端口号。它是在数据包到达接收方(服务器)时用于标识目标应用程序或服务的。
- 目的端口号帮助操作系统将接收到的数据包传递给正确的应用程序或服务。
在网络通信中,源端口号和目的端口号共同构成一个套接字(Socket),用于唯一标识一个网络连接。通过使用不同的源端口号和目的端口号,可以同时建立多个网络连接,并确保数据包能够被正确地路由到相应的应用程序或服务。
⭕总的来说:传输层协议( TCP
和 UDP
)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 “数据是谁发的, 要发给谁”
四、网络字节序
我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网络数据流同样有大端小端之分。那么如何定义网络数据流的地址呢?
网络字节序通常采用大端序(Big-Endian)表示,即数据的高位字节存储在低地址,低位字节存储在高地址。这种字节序是网络协议规定的标准字节序,确保了在不同主机之间的数据交换时能够正确地解释数据。
在网络编程中,将数据转换为网络字节序的方式是使用标准的网络字节序函数来进行转换,如htons()
(Host to Network Short)、htonl()
(Host to Network Long)等函数。这些函数可以确保数据在发送和接收时都按照网络字节序进行处理,从而保证不同主机之间的数据通信的正确性和可靠性。
总的来说,网络字节序是一种用于在网络通信中统一表示数据的字节顺序的规范,通过使用网络字节序,可以确保在不同主机之间的数据交换时能够正确地解释数据,避免因字节序不同而导致的数据解释错误问题。
五、sockaddr结构
1. sockaddr 结构
sockaddr
结构在网络编程中扮演着重要的角色,它用于表示各种类型的套接字地址,包括 IPv4、IPv6 和 UNIX Domain Socket 地址等。sockaddr
结构是一个通用的地址结构,通常会根据具体的协议和地址族进行类型转换为更具体的结构,例如 sockaddr_in
用于表示 IPv4 地址,sockaddr_in6
用于表示 IPv6 地址。
下面是 sockaddr
结构的定义:
struct sockaddr {
sa_family_t sa_family; // 地址族,如 AF_INET、AF_INET6
char sa_data[14]; // 实际存储地址信息的地方,通常不直接使用
};
sockaddr
结构中主要包含两个字段:
sa_family
:表示地址族,即地址的类型,可以是AF_INET
(IPv4)、AF_INET6
(IPv6)等。sa_data
:实际存储地址信息的数组,通常不直接使用,而是通过类型转换为更具体的结构体来操作。
在实际编程中,通常会将 sockaddr
结构转换为特定协议对应的结构体,例如 sockaddr_in
或 sockaddr_in6
,以便更方便地操作地址信息。
如果需要处理多种类型的套接字地址,可以使用 sockaddr
结构作为统一的表示方式,这样可以更灵活地处理不同类型的地址。
2. sockaddr_in 结构
sockaddr_in
结构是用于表示 IPv4 地址的套接字地址结构,它是在 sockaddr
结构的基础上针对 IPv4 地址进行了扩展和特化。在网络编程中,我们经常使用 sockaddr_in
结构来表示和操作 IPv4 地址信息。
下面是 sockaddr_in
结构的定义:
struct sockaddr_in {
sa_family_t sin_family; // 地址族,始终设置为 AF_INET
in_port_t sin_port; // 16 位端口号,网络字节顺序
struct in_addr sin_addr; // IPv4 地址
char sin_zero[8]; // 用于填充,使该结构和 sockaddr 兼容
};
在 sockaddr_in
结构中,常用的字段包括:
sin_family
:始终设置为AF_INET
,表示地址族为 IPv4。sin_port
:16 位端口号,存储网络字节顺序(Big-Endian)的端口号。sin_addr
:struct in_addr
类型,用于存储 IPv4 地址。sin_zero
:用于填充,使sockaddr_in
结构和通用的sockaddr
结构大小一致。
在实际编程中,当需要使用 IPv4 地址时,可以将 sockaddr_in
结构用于存储和传递地址信息,同时通过相关函数进行地址信息的设置和获取。
以下是一个示例,演示如何使用 sockaddr_in
结构表示 IPv4 地址:
#include <arpa/inet.h>
#include <netinet/in.h>
// 创建并初始化 sockaddr_in 结构体
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(8080); // 将端口号转换为网络字节顺序
inet_pton(AF_INET, "127.0.0.1", &server_address.sin_addr); // 设置 IPv4 地址
// 使用 server_address 结构体进行 Socket 编程操作
3. in_addr结构
in_addr
结构是用于表示 IPv4 地址的结构体,它通常作为 sockaddr_in
结构中的一个字段来存储 IPv4 地址信息。in_addr
结构定义如下:
struct in_addr {
in_addr_t s_addr; // 存储 32 位的 IPv4 地址
};
主要字段:
s_addr
:一个 32 位整数,用于存储 IPv4 地址,通常以网络字节顺序(Big-Endian)表示。
在进行 Socket 编程时,in_addr
结构通常与 sockaddr_in
结构一起使用,用于存储和传递 IPv4 地址信息。
以下是一个示例,演示如何使用 in_addr
结构表示 IPv4 地址:
#include <arpa/inet.h>
#include <netinet/in.h>
// 创建并初始化 in_addr 结构体
struct in_addr ipv4_address;
inet_pton(AF_INET, "127.0.0.1", &ipv4_address);
// 将 in_addr 结构体的地址设置给 sockaddr_in 结构体
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(8080);
server_address.sin_addr = ipv4_address;
// 使用 server_address 结构体进行 Socket 编程操作
4. 使用场景
不同的网络协议拥有不同的地址格式,例如IPv4和IPv6具有不同的地址结构。在使用Socket API时,需要根据不同的网络协议来使用相应的地址结构和函数进行处理。
-
对于IPv4地址,通常使用
struct sockaddr_in
结构体,并且相关函数如inet_pton()
用于IPv4地址的转换。 -
而对于IPv6地址,可以使用
struct sockaddr_in6
结构体,并且相关函数也有所不同,比如inet_pton()
可以用于IPv6地址的转换。
此外,UNIX Domain Socket(也称为本地套接字)使用文件系统路径作为地址,因此其地址格式与IPv4和IPv6不同。
因此,在实际编程中,需要根据具体的网络协议选择合适的地址结构和相应的函数来处理网络通信。这样可以确保程序能够正确地解析和处理各种网络协议的地址格式,从而实现跨网络的通信。
温馨提示
感谢您对博主文章的关注与支持!如果您喜欢这篇文章,可以点赞、评论和分享给您的同学,这将对我提供巨大的鼓励和支持。另外,我计划在未来的更新中持续探讨与本文相关的内容。我会为您带来更多关于Linux以及C++编程技术问题的深入解析、应用案例和趣味玩法等。如果感兴趣的话可以关注博主的更新,不要错过任何精彩内容!
再次感谢您的支持和关注。我们期待与您建立更紧密的互动,共同探索Linux、C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!