一、Linux网络io
Linux中的网络I/O(输入/输出)指的是计算机系统通过网络与外部设备或其他计算机进行数据交换的过程。这包括网络数据的发送和接收。
Linux通过网络协议栈来处理数据包的传输,常见的协议有TCP/IP。网络I/O通常通过套接字(socket)进行管理,支持非阻塞I/O、异步I/O等方式来提高效率。
简言之,网络I/O是操作系统与外部网络进行交互、传递信息的机制。
二、代码实操
1.单个FD
代码
int main(){
//创建TCP Server
int sockfd=sockfd(AF_INNT, SOCK_STREAM, 0);
//安排位置(端口)
struct sockaddr_in servaddr;
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= htonl(INADDR_ANY);
servaddr.sin_port= htons(2000);
//登记绑定
if(-1==bind(sockfd,(struct sockaddr*)&servaddr, sizeof(struct sockaddr))){
printf("bind failed:%s\n", strerror(errno));
}
//上岗运行
listen(sockfd,10); //listen完成后,端口2000已被分配
return 0;
}
代码功能
- 创建一个TCP服务器套接字。
- 设置服务器的IP地址和端口为2000。
- 绑定套接字与服务器地址。
- 将套接字设置为监听状态,准备接受客户端的连接。
整个过程就像酒店前台的入住登记,通过listenfd监听客户端发送的消息请求,为其分配唯一的Server端TCP网络端口。
代码解析
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
socket(): 用于创建一个新的套接字。这个套接字会用来进行网络通信。
AF_INET: 表示使用IPv4协议族。
SOCK_STREAM: 表示套接字的类型是面向连接的流(即TCP连接)。
0: 协议类型,0表示默认协议(对于TCP来说,通常是IPPROTO_TCP)。
返回的 sockfd 是一个整型文件描述符,用于引用新创建的套接字。
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(2000);
struct sockaddr_in: 专门用于存储IPv4地址信息的结构体,包括地址族、IP地址和端口号。
sin_family = AF_INET: 设置地址族为IPv4。
sin_addr.s_addr = htonl(INADDR_ANY): 设置服务器的IP地址为INADDR_ANY,即让操作系统自动选择可用的本地IP地址。htonl将32位的地址从主机字节顺序转换为网络字节顺序。
sin_port = htons(2000): 设置服务器端口为2000,htons用于将端口号从主机字节顺序转换为网络字节顺序。
if (-1 == bind(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr))) {
printf("bind failed: %s\n", strerror(errno));
}
bind(): 绑定一个IP地址和端口到套接字。这样,系统会将这个套接字与指定的地址和端口关联起来。
sockfd: 套接字描述符。
(struct sockaddr*)&servaddr: 指向地址信息的指针。
sizeof(struct sockaddr): 地址结构的大小。
如果 bind() 返回 -1,表示绑定失败,错误信息通过 strerror(errno) 输出。
P.S. bind() 失败常见原因:端口已经被占用或权限不足等。
listen(sockfd, 10);
listen(): 将套接字转换为监听状态,准备接收客户端连接请求。
sockfd: 套接字描述符。
10: 指定等待连接的最大队列长度,即最多允许10个客户端请求排队。
listen() 完成后,端口 2000 已经被服务器“占用”,并准备接收来自客户端的连接请求。
2.(单个)Client与Server的消息接发
代码
#include <errno.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
printf("Socket creation failed: %s\n", strerror(errno));
return 1;
}
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 0.0.0.0
servaddr.sin_port = htons(2000); // 0-1023
if (-1 == bind(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr))) {
printf("bind failed: %s\n", strerror(errno));
return 1;
}
if (listen(sockfd, 10) == -1) {
printf("listen failed: %s\n", strerror(errno));
return 1;
}
printf("listen finished\n");
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
while (1) {
printf("accept\n");
int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);
if (clientfd == -1) {
printf("accept failed: %s\n", strerror(errno));
continue;
}
printf("accept finished\n");
char buffer[1024] = {0};
int count = recv(clientfd, buffer, 1024, 0);
if (count == 0) {
printf("Client closed connection.\n");
close(clientfd);
continue;
} else if (count < 0) {
printf("recv error: %s\n", strerror(errno));
close(clientfd);
continue;
}
printf("RECV: %s\n", buffer);
count = send(clientfd, buffer, count, 0);
printf("SEND: %d\n", count);
close(clientfd);
}
return 0;
}
代码运行过程与结果
从上图可以看出,Server成功接收TCP Client发来的消息,并且为sockfd和clientfd分配了各自对应的TCP。
结论:一条TCP接收信息和一个FD是一对一的关系。
P.S. FD(File Descriptor)代表的是文件描述符,是操作系统内核用来标识和访问各种输入/输出(I/O)资源的一个整数。在这里,可以近似地等效为网络I/O
总结
文章总结
本文介绍了Linux网络I/O的基本概念及其在网络编程中的实际应用。Linux通过套接字(socket)来管理网络I/O操作,支持TCP/IP协议,并提供非阻塞I/O、异步I/O等机制以提高效率。文章通过具体代码示例,展示了如何在Linux环境中创建TCP服务器、绑定端口、监听连接以及与客户端进行消息交互。
讲解了文件描述符(FD)的概念,阐明了文件描述符在网络I/O中的作用,它用于标识和操作网络套接字。每个套接字都会分配一个文件描述符,在网络通信中通过该描述符进行数据的发送和接收。