Linux网络编程基础(socket编程)
Socket是网络通信中应用程序进程间双向通信的端点抽象,由IP地址和端口号唯一标识。主要分为流式套接字(TCP)、数据报套接字(UDP)和原始套接字三种类型。采用客户端/服务器模式,服务器端通过socket()、bind()、listen()、accept()等核心API建立连接,客户端通过connect()发起请求。编程中需注意地址结构体转换、网络字节序处理,Windows环境还需WSASta
一、Socket基本概念
1. 什么是Socket?
Socket(套接字)是网络通信中应用程序进程之间进行双向通信的端点的抽象,本质上是通信端点。它提供了一种进程间通信机制,使不同计算机上的程序能够进行数据交换。可以简单理解为两个程序之间进行网络通信的“管道”或“电话机”。
2. Socket的标识方式
在TCP/IP协议中,一个Socket由IP地址和端口号唯一标识,表示为:socket = (IP地址:端口号)。其中:
- IP地址:标识网络中的主机位置
- 端口号:标识主机上的具体进程
3. 半相关与全相关
- 半相关:一个三元组
(协议,本地地址,本地端口号)可以在全局唯一标识一个进程,这称为半相关 - 全相关:一个完整的网间进程通信需要一个五元组
(协议,本地地址,本地端口号,远地地址,远地端口号)来标识,这称为全相关
二、Socket类型
Socket主要分为三种类型:
1. 流式套接字(SOCK_STREAM)
- 使用TCP协议
- 提供面向连接的、可靠的、双向的数据传输服务
- 数据无差错、无重复地发送,且按发送顺序接收
- 适合处理大量数据,如文件传输协议(FTP)
流式Socket的特点:
- 数据在传输过程中不会消失
- 数据按照顺序传输
- 数据的发送和接收不是同步的(内部使用缓冲区)
2. 数据报套接字(SOCK_DGRAM)
- 使用UDP协议
- 提供无连接的、不可靠但高效的数据传输
- 保留了消息边界,传输效率较高
- 适用于对实时性要求高的音视频传输应用
数据报式Socket的特点:
- 强调快速传输而非传输顺序
- 传输的数据可能丢失或损毁
- 限制每一次传输的大小(UDP数据报最大64KB)
- 数据的发送和接收是同步的
3. 原始套接字(SOCK_RAW)
- 允许对较低层协议(如IP、ICMP)进行直接访问
- 常用于网络协议分析或测试
- 例如ping、traceroute等工具使用此类型的套接字
三、客户端/服务器模式
TCP/IP网络应用中,通信的两个进程间相互作用的主要模式是客户端/服务器(C/S)模式。
服务器端流程:
- 调用
socket()创建套接字 - 调用
bind()绑定IP地址和端口号 - 调用
listen()将套接字转为可接收连接状态 - 调用
accept()受理连接请求 - 通过
send()/recv()进行数据传输 - 调用
close()关闭套接字
客户端流程:
- 调用
socket()创建套接字 - 调用
connect()向服务器发送连接请求 - 通过
send()/recv()进行数据传输 - 调用
close()关闭套接字
四、核心API函数详解
1. socket() - 创建套接字
int socket(int domain, int type, int protocol);
domain:指定通信域,如AF_INET表示IPv4协议type:指定套接字类型,如SOCK_STREAM(TCP)或SOCK_DGRAM(UDP)protocol:指定协议,通常设为0表示默认协议
2. bind() - 绑定地址
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
将套接字与特定的IP地址和端口号关联起来。服务器端通常需要调用此函数来绑定固定的网络地址和端口号。
3. listen() - 监听连接
int listen(int sockfd, int backlog);
用于面向连接的服务器,表明它愿意接收连接请求。backlog参数指定等待连接队列的最大长度。
4. accept() - 接受连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
当有连接请求到达时,接受该连接并返回一个新的套接字描述符用于与客户端通信。
5. connect() - 发起连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
客户端使用此函数向服务器发起连接请求。
6. send()/recv() - 数据传输
int send(SOCKET s, const char *buf, int len, int flags);
int recv(SOCKET s, char *buf, int len, int flags);
用于在已连接的套接字上发送和接收数据。
7. close() — 关闭套接字
int close(int fd);
将套接字描述符的引用计数减1,当引用计数为0时,触发TCP的断开连接过程(四次挥手)。
五、地址结构体
在Socket编程中,常用sockaddr_in结构体来表示IPv4地址:
struct sockaddr_in {
short sin_family; // 网络地址类型,必须设为AF_INET
u_short sin_port; // 端口号(网络字节序)
struct in_addr sin_addr; // 32位IP地址
char sin_zero[8]; // 填充字段
};
地址转换函数用于在“点分十进制”字符串和网络字节序整数之间转换:
| 函数 | 作用 | 说明 |
|---|---|---|
inet_addr() |
字符串→32位整数 | 仅IPv4 |
inet_pton() |
字符串→二进制 | 支持IPv4/IPv6,推荐使用 |
inet_ntoa() |
二进制→字符串 | 仅IPv4,线程不安全 |
inet_ntop() |
二进制→字符串 | 支持IPv4/IPv6,线程安全 |
六、网络字节序
TCP/IP协议规定,网络数据流应采用大端字节序(Big-Endian)。为确保程序在不同架构的计算机上都能正常运行,需要使用以下转换函数:
htonl():主机字节序转网络字节序(32位)htons():主机字节序转网络字节序(16位)ntohl():网络字节序转主机字节序(32位)ntohs():网络字节序转主机字节序(16位)
七、Windows环境特殊处理
在Windows下进行Socket编程,需要额外的初始化和清理步骤:
- 初始化:调用
WSAStartup()设置Winsock版本 - 清理:通信结束后调用
WSACleanup()释放资源
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
int err = WSAStartup(wVersionRequested, &wsaData);
// ... 进行Socket操作
WSACleanup(); // 最后清理
八、实际应用场景
Socket编程广泛应用于各种网络应用中:
- Web服务器:HTTP服务
- 即时通讯软件:实时消息传递
- 多人在线游戏:实时交互
- 物联网设备通信:传感器数据上传和控制指令下发
- 音视频传输:视频流、音频流的实时传输
总结:Socket编程是网络编程的基础,掌握Socket的基本概念、编程模型和核心API函数,是进行任何网络应用开发的前提。无论是Web服务、即时通讯还是物联网应用,底层都离不开Socket的支持。建议初学者从简单的TCP/UDP通信示例入手,逐步理解Socket的工作机制和编程要点。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)