【linux网络编程】文件描述符
1. 什么是文件描述符?
文件描述符(FD) 是操作系统用来管理打开的文件或 I/O 资源(如套接字、管道、设备文件等)的整数索引。
📌 在 Linux 中,一切皆文件:
文件类型 | 文件描述符 |
---|---|
普通文件(文本、二进制) | open("file.txt") 返回 FD |
标准输入 (stdin ) | FD = 0 |
标准输出 (stdout ) | FD = 1 |
标准错误 (stderr ) | FD = 2 |
套接字(Socket) | socket() 返回 FD |
2. 套接字是文件描述符
当你使用 socket()
创建一个监听套接字时,操作系统会返回一个文件描述符,用于标识这个套接字。
📌 监听套接字创建示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
int main() {
int server_fd; // 监听套接字的文件描述符
struct sockaddr_in server_addr;
// 1. 创建 TCP 套接字(返回文件描述符)
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
printf("Socket created with file descriptor: %d\n", server_fd);
// 2. 绑定 IP 和端口
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
// 3. 监听端口
listen(server_fd, 5);
printf("Listening on port %d...\n", PORT);
// 4. 关闭套接字
close(server_fd);
return 0;
}
📌 输出示例
Socket created with file descriptor: 3
Listening on port 8080...
socket()
返回的server_fd = 3
,表示监听套接字的文件描述符。- 这个
server_fd
可以像普通文件一样read()
或write()
。
3. 监听套接字 vs. 客户端套接字
在服务器程序中:
socket()
创建监听套接字,返回 FD(如server_fd=3
)。accept()
接受客户端连接,返回新的 FD(如client_fd=4
)。client_fd
用于与客户端通信,而server_fd
继续监听新的连接。
📌 服务器 accept()
连接示例
int client_fd;
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
// 等待客户端连接
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len);
printf("Accepted connection, new socket fd: %d\n", client_fd);
📌 示例输出
Socket created with file descriptor: 3
Listening on port 8080...
Accepted connection, new socket fd: 4
server_fd=3
(监听套接字) 继续等待新连接。client_fd=4
(客户端套接字) 用于和客户端通信。
4. 使用 select()
监听多个文件描述符
Linux 提供 select()
允许同时监听多个文件描述符(包括套接字、标准输入、文件等)。
📌 监听 server_fd
和 stdin
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(server_fd, &read_fds);
FD_SET(STDIN_FILENO, &read_fds); // 监听键盘输入
int max_fd = (server_fd > STDIN_FILENO) ? server_fd : STDIN_FILENO;
select(max_fd + 1, &read_fds, NULL, NULL, NULL);
if (FD_ISSET(server_fd, &read_fds)) {
client_fd = accept(server_fd, NULL, NULL);
printf("New client connected: %d\n", client_fd);
}
if (FD_ISSET(STDIN_FILENO, &read_fds)) {
char buffer[1024];
read(STDIN_FILENO, buffer, sizeof(buffer));
printf("You typed: %s\n", buffer);
}
📌 总结
server_fd
监听网络连接。STDIN_FILENO
监听键盘输入。select()
允许多个文件描述符同时监听。
5. 文件描述符的生命周期
-
创建
socket()
返回文件描述符,如server_fd=3
。accept()
返回新的连接文件描述符,如client_fd=4
。
-
使用
read() / write()
或send() / recv()
进行通信。- 服务器使用
select()
或poll()
监听多个套接字。
-
关闭
close(server_fd)
释放监听套接字。close(client_fd)
释放客户端套接字。
注意
- 服务器不能关闭
server_fd
,否则无法接受新连接! - 客户端断开连接时,服务器应关闭
client_fd
,避免文件描述符泄露。
6. 如何查看打开的文件描述符
Linux 提供 lsof
和 netstat
命令查看进程的打开套接字:
lsof -i :8080 # 查看监听 8080 端口的进程
netstat -anp | grep 8080 # 查看 8080 端口连接状态
示例输出:
COMMAND PID FD TYPE DEVICE SIZE/OFF NODE NAME
server 1234 3u IPv4 12345 0t0 TCP *:8080 (LISTEN)
server 1234 4u IPv4 12346 0t0 TCP 192.168.1.10:8080->192.168.1.20:50000 (ESTABLISHED)
server 1234 3u
:进程server
监听8080
端口,文件描述符3
。server 1234 4u
:建立了 TCP 连接,文件描述符4
。
7. 总结
✔ 监听套接字(server_fd)是文件描述符(FD),它在 Linux 中就像一个文件一样被管理。
✔ socket()
创建的监听套接字有一个 FD,accept()
返回的客户端连接也有自己的 FD。
✔ 服务器必须管理多个文件描述符(监听、客户端连接、标准输入等),可以使用 select()
监听多个 FD。
✔ 关闭 client_fd
释放客户端连接,但服务器 server_fd
必须保持打开以接受新连接。
✔ lsof -i
和 netstat
命令可查看监听端口和文件描述符信息。
📌 如果你在做 Linux 网络编程,理解“套接字 = 文件描述符” 是最核心的概念之一! 🚀