☰
Linux 网络编程进阶:从 Socket基础到TCP/UDP 编程实战
2026/10/13 3:10:29 网站建设 项目流程

1. 引言

Linux 网络编程是后端开发、嵌入式开发和系统编程的核心技能之一。本文面向有一定 C 语言基础、希望系统掌握 Linux 网络编程的读者,从最基本的 socket 接口函数讲起,逐步深入到 TCP/UDP 服务端与客户端实战、UNIX 域协议、套接字选项以及广播与组播编程。全文以函数原型、参数说明、返回值分析和可运行示例为主线,帮助读者建立完整的网络编程知识体系。

2. 基础函数

Socket(套接字)是 Linux 网络编程的基石,它是对 TCP/IP 协议栈的封装,为应用程序提供了一套统一的网络通信接口。下面从最核心的 socket 函数开始讲解。

2.1 socket 函数

函数原型:

#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);

参数说明:

  • domain:协议族,常用取值包括AF_INET(IPv4 网络协议)、AF_INET6(IPv6)、AF_UNIX(UNIX 域套接字)。
  • type:套接字类型,常用取值包括SOCK_STREAM(流式套接字,面向连接,对应 TCP)、SOCK_DGRAM(数据报套接字,无连接,对应 UDP)、SOCK_RAW(原始套接字)。
  • protocol:协议号,通常传 0 表示使用默认协议。也可以指定IPPROTO_TCP或IPPROTO_UDP。

返回值:成功返回一个非负的文件描述符(套接字描述符);失败返回 -1,并设置 errno。

示例:

#include <stdio.h> #include <sys/socket.h> #include <arpa/inet.h> int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket"); return 1; } printf("socket created, fd = %d\n", sockfd); return 0; }

2.2 网络地址结构体

在 Linux 网络编程中,地址结构体用于描述通信双方的地址信息。最常用的是 IPv4 地址结构体sockaddr_in,其定义如下:

#include <netinet/in.h> struct sockaddr_in { sa_family_t sin_family; /* 地址族,AF_INET */ in_port_t sin_port; /* 端口号,网络字节序 */ struct in_addr sin_addr; /* IPv4 地址 */ char sin_zero[8]; /* 填充字段,保持与 sockaddr 大小一致 */ };

其中struct in_addr的定义为:

struct in_addr { in_addr_t s_addr; /* 32 位 IPv4 地址,网络字节序 */ };

通用地址结构体sockaddr用于函数参数的统一类型:

struct sockaddr { sa_family_t sa_family; /* 地址族 */ char sa_data[14]; /* 协议地址 */ };

在实际编程中,通常先填充sockaddr_in,再强制转换为struct sockaddr *传入 bind、connect 等函数。

2.3 IPv4 地址转换函数

IPv4 地址在程序中通常以点分十进制字符串(如 "192.168.1.1")或 32 位二进制整数两种形式存在。地址转换函数用于在这两种形式之间转换。

inet_pton 函数:将点分十进制字符串转换为网络字节序的二进制地址。

#include <arpa/inet.h> int inet_pton(int af, const char *src, void *dst);
  • af:地址族,通常为AF_INET。
  • src:点分十进制字符串,如 "192.168.1.1"。
  • dst:指向存放转换结果的struct in_addr的指针。

返回值:成功返回 1;src 不是有效地址返回 0;af 不支持返回 -1。

inet_ntop 函数:将网络字节序的二进制地址转换为点分十进制字符串。

#include <arpa/inet.h> const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
  • af:地址族,通常为AF_INET。
  • src:指向struct in_addr的指针。
  • dst:存放转换结果的字符串缓冲区。
  • size:缓冲区大小,IPv4 地址至少需要INET_ADDRSTRLEN(16 字节)。

返回值:成功返回指向 dst 的指针;失败返回 NULL。

示例:

#include <stdio.h> #include <arpa/inet.h> int main() { struct in_addr addr; char buf[INET_ADDRSTRLEN]; // 字符串转二进制 if (inet_pton(AF_INET, "192.168.1.100", &addr) != 1) { perror("inet_pton"); return 1; } // 二进制转字符串 if (inet_ntop(AF_INET, &addr, buf, sizeof(buf)) == NULL) { perror("inet_ntop"); return 1; } printf("address: %s\n", buf); return 0; }

inet_aton 函数:将点分十进制字符串转换为网络字节序的二进制地址,是早期常用的转换函数。

#include <arpa/inet.h> int inet_aton(const char *cp, struct in_addr *inp);
  • cp:点分十进制字符串,如 "192.168.1.1"。
  • inp:指向存放转换结果的struct in_addr的指针。

返回值:成功返回非 0;失败返回 0。

inet_ntoa 函数:将网络字节序的二进制地址转换为点分十进制字符串,返回的字符串存放在静态缓冲区中。

#include <arpa/inet.h> char *inet_ntoa(struct in_addr in);
  • in:struct in_addr结构体,包含 32 位网络字节序地址。

返回值:成功返回指向点分十进制字符串的指针;失败返回 NULL。注意该函数返回的字符串存放在静态缓冲区,多次调用会覆盖之前的结果。

inet_addr 函数:将点分十进制字符串转换为网络字节序的 32 位二进制地址,已废弃,建议使用 inet_pton 替代。

#include <arpa/inet.h> in_addr_t inet_addr(const char *cp);
  • cp:点分十进制字符串,如 "192.168.1.1"。

返回值:成功返回网络字节序的 32 位地址;失败返回INADDR_NONE(即 0xFFFFFFFF)。

inet_network 函数:将点分十进制字符串转换为主机字节序的 32 位地址,不包含网络字节序转换。

#include <arpa/inet.h> in_addr_t inet_network(const char *cp);
  • cp:点分十进制字符串,如 "192.168.1.1"。

返回值:成功返回主机字节序的 32 位地址;失败返回INADDR_NONE。

示例:

#include <stdio.h> #include <arpa/inet.h> int main() { struct in_addr addr; char buf[INET_ADDRSTRLEN]; // inet_aton:字符串转二进制 if (inet_aton("192.168.1.100", &addr) == 0) { perror("inet_aton"); return 1; } printf("inet_aton: %s\n", inet_ntoa(addr)); // inet_ntoa:二进制转字符串 printf("inet_ntoa: %s\n", inet_ntoa(addr)); // inet_addr:字符串转网络字节序整数 in_addr_t net_addr = inet_addr("192.168.1.100"); if (net_addr == INADDR_NONE) { perror("inet_addr"); return 1; } printf("inet_addr: 0x%x\n", net_addr); // inet_network:字符串转主机字节序整数 in_addr_t host_addr = inet_network("192.168.1.100"); if (host_addr == INADDR_NONE) { perror("inet_network"); return 1; } printf("inet_network: 0x%x\n", host_addr); return 0; }

2.4 网络字节序函数

不同主机对多字节整数的存储方式不同(大端或小端),为保证网络通信的一致性,TCP/IP 协议规定使用大端字节序(网络字节序)。Linux 提供了一组字节序转换函数:

#include <arpa/inet.h> uint32_t htonl(uint32_t hostlong); /* 主机字节序转网络字节序(32 位) */ uint16_t htons(uint16_t hostshort); /* 主机字节序转网络字节序(16 位) */ uint32_t ntohl(uint32_t netlong); /* 网络字节序转主机字节序(32 位) */ uint16_t ntohs(uint16_t netshort); /* 网络字节序转主机字节序(16 位) */

参数说明:传入待转换的数值,返回转换后的数值。

示例:

#include <stdio.h> #include <arpa/inet.h> int main() { uint16_t port = 8080; uint16_t net_port = htons(port); printf("host port: %u, network port: %u\n", port, net_port); printf("back to host: %u\n", ntohs(net_port)); return 0; }

3. 核心函数

在 socket 创建完成后,服务端和客户端需要调用一系列函数建立连接并传输数据。本节详细讲解 bind、listen、accept、connect 四个核心函数。

3.1 bind

函数原型:

#include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明:

  • sockfd:socket 函数返回的套接字描述符。
  • addr:指向地址结构体的指针,通常为struct sockaddr_in *强制转换而来。
  • addrlen:地址结构体的大小,可用sizeof(struct sockaddr_in)。

返回值:成功返回 0;失败返回 -1,并设置 errno。

示例:

#include <stdio.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket"); return 1; } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* 绑定所有本地地址 */ if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_a

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询