当前位置:首页>java>从系统调用到TCP通信:Linux网络编程基石探析

从系统调用到TCP通信:Linux网络编程基石探析

  • 2026-02-06 05:19:05
从系统调用到TCP通信:Linux网络编程基石探析

在 Linux 系统的网络架构中,系统调用与 TCP 通信构成了网络编程的核心基石,二者层层衔接、协同运作,支撑起各类网络应用的底层逻辑。系统调用作为用户态与内核态交互的桥梁,为网络操作提供了标准化接口,从 socket 创建、绑定监听,到连接建立、数据收发,每一步网络行为都依赖内核通过系统调用提供的底层支持,是解锁 Linux 网络能力的关键钥匙。而 TCP 协议作为面向连接、可靠传输的核心协议,为网络数据交换搭建了稳定的传输链路,其三次握手、四次挥手的连接机制,以及流量控制、拥塞控制等核心特性,确保了数据在复杂网络环境中的有序、完整传输。

从系统调用发起网络请求,到 TCP 协议完成数据封装与传输,二者形成了“接口调用-协议实现”的完整链路,是理解 Linux 网络编程本质的核心脉络。本文将深入探析系统调用的底层原理与 TCP 协议的核心机制,拆解二者的协同逻辑,揭秘 Linux 网络编程的底层架构,为开发者夯实网络编程基础、突破应用开发瓶颈提供核心指引。

一、Linux TCP 开发与系统调用回顾

1.1 什么是 Linux TCP 开发

Linux TCP 开发,简单来说,就是在 Linux 操作系统环境下,基于 TCP 协议进行网络程序的编写。TCP 作为传输层的重要协议,以其面向连接、可靠传输、字节流处理等特性,在众多网络应用中发挥着关键作用 。在 Linux 平台进行 TCP 开发,开发者可以充分利用 Linux 系统强大的网络功能、高效的资源管理以及丰富的开发工具和库。

从实际应用场景来看,Linux TCP 开发无处不在。像我们日常使用的 Web 服务器,如 NGINX、Apache,它们基于 Linux 系统,通过 TCP 开发实现与客户端的稳定通信,确保网页数据能够准确无误地传输到用户浏览器。

再比如文件传输服务 FTP,在 Linux 服务器上,借助 TCP 开发,保障文件在不同主机之间完整、有序地传输,无论是大文件还是小文件,都能精准送达。还有电子邮件系统,从邮件的发送到接收,TCP 开发保证了邮件内容及相关信息可靠地在邮件服务器与客户端之间传递,让我们的沟通更加顺畅。

TCP 通信的建立过程被称为 “三次握手” ,就像是两个人打电话,在正式聊天之前,得先确认双方都能正常接听和说话。以客户端和服务器通信为例:

  1. 第一步,客户端向服务器发送一个带有 SYN(同步)标志位的数据包,里面包含一个随机生成的初始序列号(Seq=x),此时客户端进入 SYN_SENT 状态,就像你给朋友拨打电话,等待对方接听 ;

  2. 第二步,服务器收到客户端的 SYN 包后,回复一个 SYN+ACK(同步 + 确认)包,其中服务器也有自己的初始序列号(Seq=y),同时确认号(Ack=x+1),表示已收到客户端的 SYN 包,期待下一个序列号是 x+1,服务器进入 SYN_RCVD 状态,这就好比朋友接听电话后,跟你说 “我听到你声音啦,你可以继续说啦”;

  3. 第三步,客户端收到服务器的 SYN+ACK 包后,发送一个 ACK 包,确认号(Ack=y+1),表示已收到服务器的确认,此时双方进入 ESTABLISHED 状态,连接正式建立,相当于你回复朋友 “好的,那我们开始聊天吧” 。

三次握手的设计精妙之处在于,通过这三次交互,双方确认了彼此的发送和接收能力,还同步了初始序列号,为后续可靠的数据传输奠定基础 。

当通信结束,就需要断开连接,这就是 “四次挥手” 。还是以客户端和服务器为例:

  1. 第一步,客户端主动发起关闭请求,向服务器发送一个 FIN(结束)包,进入 FIN_WAIT_1 状态,就像你跟朋友说 “我说完啦,准备挂电话咯”;

  2. 第二步,服务器收到 FIN 包后,回复一个 ACK 包,确认收到关闭请求,此时服务器进入 CLOSE_WAIT 状态,客户端收到 ACK 后进入 FIN_WAIT_2 状态,这相当于朋友说 “我知道你要挂啦,我这边还有点话没说完,等会儿我说完就挂” ;

  3. 第三步,服务器处理完剩余数据后,向客户端发送 FIN 包,进入 LAST_ACK 状态,也就是朋友说 “我也说完啦,可以挂电话啦”;

  4. 第四步,客户端收到 FIN 包后,回复 ACK 包,进入 TIME_WAIT 状态,等待 2 倍的 MSL(最长报文段寿命)时间后关闭连接,服务器收到 ACK 后立即关闭连接,这就好比你回复朋友 “好的,挂啦”,然后稍作等待,确保朋友那边也正常结束通话 。

之所以需要四次挥手,是因为 TCP 是全双工通信,双方都要独立关闭自己的数据传输通道,确保数据全部传输完毕,避免数据丢失 。

1.2 什么是系统调用

系统调用是操作系统提供给应用程序的一组特殊接口,是用户空间与内核空间交互的关键桥梁 。在计算机系统中,内核负责管理硬件资源、调度任务、维护系统安全等核心工作,处于特权级较高的内核态;而应用程序运行在用户态,其对硬件和系统资源的访问受到严格限制。系统调用就像是一个 “翻译官”,应用程序通过它向内核发出请求,让内核执行特定的操作,然后内核将处理结果返回给应用程序。

例如,当我们在应用程序中想要读取一个文件时,就会调用像 read 这样的系统调用函数。应用程序将文件描述符、读取缓冲区、读取字节数等参数传递给 read 函数,read 函数通过系统调用进入内核态,内核根据这些参数找到对应的文件,从磁盘中读取数据,再将数据填充到应用程序提供的缓冲区中,最后将读取的字节数返回给应用程序。

这个过程中,系统调用确保了应用程序在安全、受控的环境下访问磁盘资源,避免了应用程序直接操作硬件可能带来的混乱和安全风险。常见的系统调用还有用于创建进程的 fork、用于内存分配的 malloc(底层依赖系统调用)、用于网络通信的 socket 相关调用等,它们为应用程序提供了丰富的功能支持。

那系统调用具体是怎么工作的呢?当应用程序发起系统调用时,就像触发了一个特殊机关。以 x86 架构为例,应用程序通过执行陷入指令(如 int 0x80 ),这个指令会让 CPU 从用户态切换到内核态。这就好比从普通市民身份(用户态,权限有限)瞬间变成城堡里的特权人员(内核态,拥有高权限)。进入内核态后,操作系统内核会根据系统调用号,在系统调用表中找到对应的内核函数,然后执行该函数,完成应用程序请求的操作。比如读取文件内容,内核就会找到负责文件读取的函数,从磁盘中读取数据。等操作完成后,内核再将结果返回给应用程序,同时 CPU 从内核态切换回用户态,应用程序继续执行后续代码 。

1.3 为什么系统调用是 TCP 开发的基石

在 Linux TCP 开发中,系统调用扮演着不可或缺的基石角色,贯穿于 TCP 开发的各个关键环节。

  1. 创建套接字是 TCP 开发的起始步骤,而 socket 系统调用就是实现这一操作的关键。通过 socket 系统调用,我们可以指定协议族(如 AF_INET 表示 IPv4)、套接字类型(如 SOCK_STREAM 表示 TCP 套接字)和协议(通常为 0),从而创建一个用于网络通信的套接字文件描述符。这个描述符就像是一个通信端点,后续的连接建立、数据传输等操作都围绕它展开。如果没有 socket 系统调用,我们就无法在程序中建立起与网络交互的入口。
  2. 建立连接时,客户端使用 connect 系统调用,向服务器指定的 IP 地址和端口发起连接请求;服务器则通过 bind 系统调用将套接字绑定到特定的 IP 地址和端口,再使用 listen 系统调用开始监听客户端的连接请求,最后用 accept 系统调用接受客户端的连接,返回一个新的套接字用于与客户端通信。这些系统调用协同工作,完成了 TCP 连接的三次握手过程,为可靠的数据传输搭建起通道。缺少任何一个环节的系统调用,连接都无法成功建立,数据传输也就无从谈起。
  3. 数据传输阶段,send 和 recv 系统调用承担着数据发送和接收的重任。应用程序通过 send 系统调用将数据从用户空间复制到内核空间的网络缓冲区,再由内核负责将数据发送到网络中;recv 系统调用则相反,它从内核空间的网络缓冲区中读取数据到用户空间,供应用程序处理。这两个系统调用确保了数据在应用程序与网络之间的高效、准确传输。
  4. 当通信结束,需要关闭连接时,close 系统调用发挥作用,它释放套接字相关的资源,关闭连接,确保系统资源的合理回收,避免资源泄漏。

系统调用在 Linux TCP 开发中就像是大厦的基石,每一个关键步骤都依赖于它,为 TCP 开发提供了最基础、最核心的支持,是实现高效、稳定 TCP 网络应用的根本保障。

二、系统调用函数详解

2.1 socket:创建网络通信的大门

在 Linux TCP 开发中,socket 函数是开启网络通信的第一把钥匙,其作用是创建一个套接字,为后续的网络连接和数据传输奠定基础 。该函数的原型如下:

#include <sys/socket.h>intsocket(int domain, int type, int protocol);
  1. domain 参数指定协议族,常见的有 AF_INET(IPv4 协议)、AF_INET6(IPv6 协议)等。例如,当我们要进行 IPv4 网络通信时,就会将 domain 设为 AF_INET 。这就好比我们要选择一条道路,AF_INET 就是 IPv4 这条 “道路”,规定了通信的地址类型和基本规则。
  2. type 参数指定套接字类型,对于 TCP 开发,通常使用 SOCK_STREAM,它表示流式套接字,提供面向连接、可靠的字节流服务。这就像是建立一条稳定的管道,数据在其中有序、可靠地传输 。
  3. protocol 参数通常设置为 0,表示选择默认协议。在指定了 domain 和 type 的情况下,操作系统能够自动推导出合适的协议 。

socket 函数成功时,会返回一个小的非负整数值,即套接字描述符,它就像是这个套接字的 “身份证”,后续对该套接字的所有操作都通过这个描述符来进行 。若创建失败,函数返回 - 1,并设置 errno 以指示错误原因,比如 EACCES 表示权限不足,无法创建套接字。

下面通过一个简单的代码示例来展示 socket 函数的基本用法:

#include <stdio.h>#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>intmain(){    // 创建 TCP 套接字    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    if (sockfd == -1) {        perror(「socket creation failed」);        return 1;    }    printf(「Socket created successfully, sockfd: %d\n」, sockfd);    // 后续可以进行绑定、监听等操作    // 关闭套接字    close(sockfd);    return 0;}

在这个示例中,我们调用 socket 函数创建了一个基于 IPv4 的 TCP 套接字。如果创建成功,会打印出套接字描述符;若失败,则通过 perror 函数打印错误信息。

2.2 bind:绑定地址与端口

创建好套接字后,接下来就需要使用 bind 函数将套接字与特定的 IP 地址和端口号绑定,使服务器能够在指定的地址和端口上监听客户端的连接请求 。bind 函数的原型如下:

#include <sys/socket.h>intbind(int sockfd, conststruct sockaddr *addr, socklen_t addrlen);
  1. sockfd 是通过 socket 函数创建的套接字描述符,它标识了我们要绑定地址的套接字 。
  2. addr 是一个指向 sockaddr 结构体的指针,该结构体中包含了要绑定的 IP 地址和端口号等信息 。在 IPv4 编程中,常用 sockaddr_in 结构体来填充地址信息,如下所示:
struct sockaddr_in {    sa_family_t sin_family; /* 地址族,如 AF_INET */    in_port_t sin_port;     /* 端口号,使用网络字节序 */    struct in_addr sin_addr; /* IP 地址,使用网络字节序 */    char sin_zero[8];       /* 填充字段,使 sockaddr_in 与 sockaddr 大小相同 */};

在填充 sockaddr_in 结构体时,sin_family 应设置为 AF_INET;sin_port 要使用 htons 函数将主机字节序的端口号转换为网络字节序;sin_addr.s_addr 则使用 htonl 函数将主机字节序的 IP 地址转换为网络字节序 。如果要绑定到本机所有 IP 地址,可以将 sin_addr.s_addr 设为 INADDR_ANY 。

addrlen 参数表示 addr 结构体的长度 。

bind 函数成功时返回 0,失败时返回 - 1,并设置 errno 指示错误原因,例如 EADDRINUSE 表示地址已被占用,无法绑定 。

在实际应用场景中,服务器端开发时经常会用到 bind 函数。比如搭建一个 Web 服务器,我们需要将服务器的套接字绑定到 80 端口(HTTP 默认端口)或 443 端口(HTTPS 默认端口),这样客户端才能通过这些标准端口访问服务器 。下面是一个简单的绑定示例:

#include <stdio.h>#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>intmain(){    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    if (sockfd == -1) {        perror(「socket creation failed」);        return 1;    }    struct sockaddr_in serv_addr;    serv_addr.sin_family = AF_INET;    serv_addr.sin_port = htons(8888); // 绑定到 8888 端口    serv_addr.sin_addr.s_addr = INADDR_ANY; // 绑定到本机所有 IP 地址    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {        perror(「bind failed」);        close(sockfd);        return 1;    }    printf(「Bind successful\n」);    // 后续可以进行监听等操作    close(sockfd);    return 0;}

在这个示例中,我们创建了一个套接字,并将其绑定到本机所有 IP 地址的 8888 端口。如果绑定成功,会打印 “Bind successful”;若失败,则打印错误信息并关闭套接字 。

2.3 listen:监听连接请求

服务器完成套接字的绑定后,需要调用 listen 函数使套接字进入监听状态,等待客户端的连接请求 。listen 函数的原型如下:

#include <sys/socket.h>intlisten(int sockfd, int backlog);
  • sockfd 是之前创建并绑定了地址的套接字描述符 。
  • backlog 参数是一个比较关键的参数,它表示内核为该监听套接字维护的未完成连接队列和已完成连接队列的最大长度总和 。简单来说,当客户端发送连接请求(SYN 包)时,如果服务器还没有调用 accept 函数来处理这个连接,这个连接请求就会被放入未完成连接队列中;当三次握手完成后,连接会被移到已完成连接队列中 。backlog 的值决定了这两个队列总共能容纳多少个连接请求 。

在实际应用中,backlog 的值不能随意设置。如果设置过小,当并发连接请求较多时,可能会导致部分客户端连接被拒绝,因为队列已满;如果设置过大,可能会消耗过多的系统资源 。通常,backlog 会设置为一个合理的值,如 128 。在 Linux 系统中,也可以使用 SOMAXCONN 宏来表示系统建议的最大值 。

listen 函数成功时返回 0,失败时返回 - 1,并设置 errno 指示错误原因,比如 EADDRINUSE 表示地址正在被使用,无法监听 。下面是一个使用 listen 函数的示例:

#include <stdio.h>#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>int main() {    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    if (sockfd == -1) {        perror(「socket creation failed」);        return 1;    }    struct sockaddr_in serv_addr;    serv_addr.sin_family = AF_INET;    serv_addr.sin_port = htons(8888);    serv_addr.sin_addr.s_addr = INADDR_ANY;    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {        perror(「bind failed」);        close(sockfd);        return 1;    }    if (listen(sockfd, 128) == -1) {        perror(「listen failed」);        close(sockfd);        return 1;    }    printf(「Listening on port 8888...\n」);    // 后续可以进行 accept 操作    close(sockfd);    return 0;}

在这个示例中,我们在绑定套接字后,调用 listen 函数使其开始监听 8888 端口,backlog 设置为 128 。如果监听成功,会打印 “Listening on port 8888...”;若失败,则打印错误信息并关闭套接字 。

3.4 accept:接受客户端连接

当服务器处于监听状态时,通过 accept 函数从监听队列中接受客户端的连接请求 。accept 函数的原型如下:

#include <sys/socket.h>intaccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • sockfd 是处于监听状态的套接字描述符 。
  • addr 是一个传出参数,是指向 sockaddr 结构体的指针,用于获取客户端的地址信息,包括客户端的 IP 地址和端口号 。在实际使用时,通常会先定义一个 sockaddr_in 结构体来接收地址信息 。
  • addrlen 也是一个传出参数,在传入时,它表示 addr 所指向结构体的大小;在函数返回时,它会被设置为实际接收到的客户端地址结构体的大小 。

accept 函数成功时,会返回一个新的套接字描述符,这个新的套接字专门用于与该客户端进行通信,而原来的监听套接字 sockfd 仍然继续监听其他客户端的连接请求 。这就好比一个总机(监听套接字)负责接听所有来电,当有客户打进电话(连接请求)时,总机转接到一个分机(新套接字)与该客户进行具体沟通 。如果 accept 函数失败,返回 - 1,并设置 errno 指示错误原因,例如在非阻塞模式下没有连接时,会返回 EAGAIN 或 EWOULDBLOCK 。

通过下面的示例可以更好地理解 accept 函数的使用:

#include <stdio.h>#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>int main() {    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    if (sockfd == -1) {        perror(「socket creation failed」);        return 1;    }    struct sockaddr_in serv_addr, cli_addr;    serv_addr.sin_family = AF_INET;    serv_addr.sin_port = htons(8888);    serv_addr.sin_addr.s_addr = INADDR_ANY;    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {        perror(「bind failed」);        close(sockfd);        return 1;    }    if (listen(sockfd, 128) == -1) {        perror(「listen failed」);        close(sockfd);        return 1;    }    printf(「Listening on port 8888...\n」);    socklen_t cli_addr_len = sizeof(cli_addr);    int connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_addr_len);    if (connfd == -1) {        perror(「accept failed」);        close(sockfd);        return 1;    }    printf(「Client connected: %s:%d\n」, inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));    // 后续可以通过 connfd 与客户端进行数据传输    close(connfd);    close(sockfd);    return 0;}

在这个示例中,服务器监听 8888 端口,当有客户端连接时,accept 函数接受连接,并打印出客户端的 IP 地址和端口号 。然后可以通过返回的 connfd 与客户端进行数据传输 。

2.5 connect:发起连接

在客户端的 TCP 开发中,使用 connect 函数主动发起与服务器的连接 。connect 函数的原型如下:

#include <sys/socket.h>intconnect(int sockfd, conststruct sockaddr *addr, socklen_t addrlen);
  • sockfd 是客户端通过 socket 函数创建的套接字描述符 。
  • addr 是一个指向 sockaddr 结构体的指针,其中包含了要连接的服务器的 IP 地址和端口号等信息 。同样,在 IPv4 编程中,通常使用 sockaddr_in 结构体来填充服务器地址信息 。
  • addrlen 表示 addr 结构体的长度 。

connect 函数的工作过程涉及到 TCP 的三次握手原理 。当客户端调用 connect 函数时,会向服务器发送一个 SYN(同步)包,服务器收到后回复一个 SYN + ACK(同步确认)包,客户端再发送一个 ACK 包,完成三次握手,从而建立起可靠的连接 。这个过程就像是两个人打电话,一方先拨通电话(发送 SYN 包),另一方接听并回应(发送 SYN + ACK 包),然后先拨通的一方确认连接(发送 ACK 包),这样就可以开始通话(数据传输)了 。

connect 函数成功时返回 0,表明连接建立成功;失败时返回 - 1,并设置 errno 指示错误原因,比如 ECONNREFUSED 表示服务器拒绝连接,可能是服务器未运行或端口未监听 。下面是一个客户端使用 connect 函数连接服务器的示例:

#include <stdio.h>#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>intmain(){    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    if (sockfd == -1) {        perror(「socket creation failed」);        return 1;    }    struct sockaddr_in serv_addr;    serv_addr.sin_family = AF_INET;    serv_addr.sin_port = htons(8888);    inet_pton(AF_INET, 「127.0.0.1」, &serv_addr.sin_addr);    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {        perror(「connect failed」);        close(sockfd);        return 1;    }    printf(「Connected to server\n」);    // 后续可以进行数据传输    close(sockfd);    return 0;}

在这个示例中,客户端创建套接字后,尝试连接本地服务器(IP 地址为 [127.0.0.1](127.0.0.1))的 8888 端口 。如果连接成功,会打印 “Connected to server”;若失败,则打印错误信息并关闭套接字 。

2.6 read/write/recv/send:数据传输

在 TCP 连接建立后,就需要进行数据传输。在 Linux 中,有 read、write、recv、send 等函数用于数据的读写操作 。

read 和 write 函数是比较基础的 I/O 操作函数,它们也可以用于套接字的读写 。read 函数从文件描述符(对于套接字来说就是套接字描述符)中读取数据,原型为:

#include <unistd.h>ssize_tread(int fd, void *buf, size_t count);
  • fd 是要读取数据的文件描述符,即套接字描述符 。
  • buf 是用于存储读取数据的缓冲区 。
  • count 是期望读取的字节数 。

write 函数则是将数据写入到文件描述符中,原型为:

#include <unistd.h>ssize_twrite(int fd, constvoid *buf, size_t count);
  • fd 是要写入数据的文件描述符,即套接字描述符 。
  • buf 是包含要写入数据的缓冲区 。
  • count 是要写入的字节数 。

recv 和 send 函数是专门为套接字设计的数据传输函数,它们比 read 和 write 函数多了一些参数来控制数据传输的方式 。recv 函数用于从套接字接收数据,原型为:

#include <sys/socket.h>ssize_trecv(int sockfd, void *buf, size_t len, int flags);
  • sockfd 是接收数据的套接字描述符 。
  • buf 是用于存储接收数据的缓冲区 。
  • len 是期望接收的最大字节数 。
  • flags 是一组标志位,可以用于设置一些特殊的接收方式,比如 MSG_DONTWAIT 表示非阻塞接收 。

send 函数用于向套接字发送数据,原型为:

#include <sys/socket.h>ssize_tsend(int sockfd, constvoid *buf, size_t len, int flags);
  • sockfd 是发送数据的套接字描述符 。
  • buf 是包含要发送数据的缓冲区 。
  • len 是要发送的字节数 。
  • flags 是控制发送行为的标志位,常见取值有 MSG_NOSIGNAL(发送时不产生 SIGPIPE 信号)、MSG_DONTWAIT(非阻塞发送)等,设为 0 时表示默认阻塞发送。

各函数返回值含义:这四个函数的返回值规则一致,需重点掌握以处理异常情况

  • 成功时,返回实际读取/发送的字节数(注意:read/recv 返回 0 表示对方关闭连接,而非读取失败);
  • 失败时,返回-1 并设置 errno,例如 EAGAIN/EWOULDBLOCK 表示非阻塞模式下无数据可读写,EBADF 表示文件描述符无效。

函数异同与适用场景:read/write 是通用 I/O 函数,适用于所有文件描述符(包括套接字、普通文件、管道等),兼容性强,但缺乏网络通信专属控制能力;recv/send 是套接字专用函数,核心优势在于 flags 参数,可灵活控制传输行为,更适配网络编程场景。具体选用建议:

  1. 简单 TCP 数据传输,无需特殊控制时,read/write 即可满足需求,代码简洁通用;
  2. 需要非阻塞读写、禁止 SIGPIPE 信号、带外数据传输(MSG_OOB)等场景时,必须使用 recv/send;
  3. 注意:TCP 是字节流协议,这四个函数均不保证“一次读写完成所有数据”,例如调用 send 发送 1024 字节,实际可能仅发送 512 字节,需循环读写并结合返回值判断是否完成传输。

下面通过示例展示如何正确使用这些函数实现循环数据传输,避免因字节流特性导致的数据收发不完整:

#include <stdio.h>#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#define MAX_BUFFER_SIZE 1024// 循环发送数据,确保所有字节发送完成ssize_tsend_all(int sockfd, constvoid *buf, size_t len){    if (buf == NULL || len == 0return 0// 空数据处理,避免异常    size_t total_sent = 0;    const char *ptr = (const char *)buf;    while (total_sent < len) {        ssize_t sent = send(sockfd, ptr + total_sent, len - total_sent, 0);        if (sent == -1) {            perror(「send failed in send_all」);            return -1// 发送失败        }        total_sent += sent;    }    return total_sent;}// 循环接收数据,直到读取到指定长度或对方关闭连接ssize_trecv_all(int sockfd, void *buf, size_t len){    if (buf == NULL || len == 0return 0// 空缓冲区处理    size_t total_recv = 0;    char *ptr = (char *)buf;    while (total_recv < len) {        ssize_t recved = recv(sockfd, ptr + total_recv, len - total_recv, 0);        if (recved == -1) {            perror(「recv failed in recv_all」);            return -1// 接收失败        } else if (recved == 0) {            printf(「Peer closed connection during recv_all\n」);            break// 对方关闭连接        }        total_recv += recved;    }    // 若接收字符串,手动添加结束符(需确保缓冲区有额外空间)    if (len > total_recv) {        ((char *)buf)[total_recv] = 『\0』;    }    return total_recv;}intmain(){    // 假设已建立 TCP 连接,sockfd 为连接套接字    int sockfd = /* 已通过 accept 或 connect 获取 */;    const char *send_buf = 「Hello, TCP! This is a test message for loop send/recv.」;    size_t send_len = strlen(send_buf);    // 循环发送    if (send_all(sockfd, send_buf, send_len) == -1) {        close(sockfd);        return 1;    }    printf(「All data sent successfully\n」);    // 循环接收    char recv_buf[MAX_BUFFER_SIZE] = {0};    ssize_t recv_len = recv_all(sockfd, recv_buf, MAX_BUFFER_SIZE - 1);    if (recv_len == -1) {        close(sockfd);        return 1;    } else if (recv_len == 0) {        printf(「Client closed the connection\n」);    } else {        printf(「Received data: %s\n」, recv_buf);    }    close(sockfd);    return 0;}

上述示例中,send_all 和 recv_all 函数通过循环调用 send 和 recv,确保数据完整收发,这是 TCP 字节流编程中必须掌握的核心技巧,避免因单次调用返回值小于期望长度导致的数据丢失。

2.7 close:关闭连接

close 函数用于关闭套接字描述符,释放与该套接字相关的内核资源,终止 TCP 连接。其原型如下:

#include <unistd.h>intclose(int fd);

其中 fd 为要关闭的套接字描述符。函数成功返回 0,失败返回-1 并设置 errno。

  1. 半关闭与全关闭:close 会关闭套接字的读端和写端,即全关闭。若需仅关闭读端或写端(半关闭),需使用 shutdown 函数。例如,服务器发送完响应后,可调用 shutdown(sockfd, SHUT_WR)关闭写端,告知客户端不再发送数据,同时仍可通过读端接收客户端数据。
  2. 多进程/多线程场景:若多个进程/线程共享同一个套接字描述符,close 仅会减少描述符引用计数,直到引用计数为 0 时才会真正关闭连接。因此,在多线程服务中,需妥善管理套接字描述符的生命周期,避免提前关闭导致其他线程通信异常。
  3. 资源泄漏防范:每次创建套接字后,必须在通信结束或程序退出时调用 close 关闭,否则会导致文件描述符泄漏,耗尽系统可用资源,最终使程序无法创建新的套接字。

半关闭示例(shutdown 用法)

#include <stdio.h>#include <sys/socket.h>#include <unistd.h>#include <string.h>int main() {    // 假设已建立连接的套接字    int sockfd = /* 已通过 accept 或 connect 获取 */;    const char *response = 「Server finished sending data.」;    // 发送数据时增加错误处理    if (send(sockfd, response, strlen(response), 0) == -1) {        perror(「send failed before shutdown」);        close(sockfd);        return 1;    }    // 半关闭:关闭写端,保留读端    if (shutdown(sockfd, SHUT_WR) == -1) {        perror(「shutdown failed」);        close(sockfd);        return 1;    }    printf(「Write end closed, waiting for client data...\n」);    // 仍可通过读端接收客户端数据    char buf[1024] = {0};    ssize_t recv_len = recv(sockfd, buf, sizeof(buf) - 10);    if (recv_len == -1) {        perror(「recv failed after shutdown」);        close(sockfd);        return 1;    } else if (recv_len > 0) {        buf[recv_len] = 『\0』; // 确保字符串结束符        printf(「Received from client after shutdown%s\n」, buf);    } else {        printf(「Client closed connection after server shutdown\n」);    }    close(sockfd);    return 0;}

该示例中,shutdown(sockfd, SHUT_WR) 仅关闭套接字的写端,服务器无法再发送数据,但仍可通过读端接收客户端消息,实现半双工通信。相比 close 全关闭,半关闭能更灵活地控制连接状态,适用于需要先结束发送、再等待接收反馈的场景。

三、实战案例:搭建简单的 TCP 服务器与客户端

3.1 服务器端实现步骤与代码解析

现在,让我们通过一个具体的实战案例,深入理解 Linux TCP 开发中系统调用的实际应用。我们将搭建一个简单的 TCP 服务器与客户端,通过它们之间的通信来展示各个系统调用的协同工作。

首先是服务器端的实现。服务器端的主要工作是创建套接字、绑定地址和端口、监听连接请求,以及接受连接并进行数据交互。以下是详细的实现步骤和代码解析:

#include <stdio.h>#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#define PORT 8888#define MAX_BUFFER_SIZE 1024int main() {    // 创建套接字    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    if (sockfd == -1) {        perror(「socket creation failed」);        return 1;    }    printf(「Socket created successfully, sockfd: %d\n」, sockfd);    // 绑定地址和端口    struct sockaddr_in serv_addr;    serv_addr.sin_family = AF_INET;    serv_addr.sin_port = htons(PORT);    serv_addr.sin_addr.s_addr = INADDR_ANY;    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {        perror(「bind failed」);        close(sockfd);        return 1;    }    printf(「Bind successful\n」);    // 监听连接请求    if (listen(sockfd, 128) == -1) {        perror(「listen failed」);        close(sockfd);        return 1;    }    printf(「Listening on port %d...\n」, PORT);    // 接受客户端连接    struct sockaddr_in cli_addr;    socklen_t cli_addr_len = sizeof(cli_addr);    int connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_addr_len);    if (connfd == -1) {        perror(「accept failed」);        close(sockfd);        return 1;    }    printf(「Client connected: %s:%d\n」, inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));    // 数据交互(替换 read 为 recv,统一接口并增加错误处理)    char buffer[MAX_BUFFER_SIZE] = {0};    ssize_t valread = recv(connfd, buffer, MAX_BUFFER_SIZE - 10); // 留 1 字节存结束符    if (valread == -1) {        perror(「recv failed」);        close(connfd);        close(sockfd);        return 1;    } else if (valread == 0) {        printf(「Client closed connection during receive\n」);        close(connfd);        close(sockfd);        return 1;    }    buffer[valread] = 『\0』; // 确保字符串结束符,避免乱码    printf(「Received from client: %s\n」, buffer);    const char *response = 「Message received successfully!」;    if (send(connfd, response, strlen(response), 0) == -1) {        perror(「send failed」);        close(connfd);        close(sockfd);        return 1;    }    printf(「Response sent to client\n」);    // 关闭连接    close(connfd);    close(sockfd);    return 0;}
  1. 创建套接字:使用 socket 系统调用创建一个基于 IPv4 的 TCP 套接字。AF_INET 指定协议族为 IPv4,SOCK_STREAM 指定套接字类型为 TCP,0 表示使用默认协议。如果创建失败,通过 perror 函数打印错误信息并退出程序。
  2. 绑定地址和端口:定义一个 sockaddr_in 结构体 serv_addr,填充服务器的地址信息。sin_family 设为 AF_INET,sin_port 使用 htons 函数将端口号转换为网络字节序,sin_addr.s_addr 设为 INADDR_ANY 表示绑定到本机所有 IP 地址。然后使用 bind 系统调用将套接字 sockfd 与 serv_addr 绑定。若绑定失败,打印错误信息并关闭套接字。
  3. 监听连接请求:调用 listen 系统调用,使套接字 sockfd 进入监听状态,等待客户端的连接请求。backlog 参数设为 128,表示内核为该监听套接字维护的未完成连接队列和已完成连接队列的最大长度总和。如果监听失败,打印错误信息并关闭套接字。
  4. 接受客户端连接:通过 accept 系统调用从监听队列中接受客户端的连接请求。如果有客户端连接,accept 会返回一个新的套接字描述符 connfd,用于与该客户端进行通信。同时,cli_addr 和 cli_addr_len 用于获取客户端的地址信息。若接受连接失败,打印错误信息并关闭监听套接字。
  5. 数据交互:创建一个缓冲区 buffer,使用 read 系统调用从客户端套接字 connfd 中读取数据。如果读取成功,打印接收到的数据。然后构造一个响应消息,使用 send 系统调用将响应发送回客户端。若发送失败,打印错误信息并关闭相关套接字。
  6. 关闭连接:通信结束后,使用 close 系统调用关闭与客户端通信的套接字 connfd 和监听套接字 sockfd,释放资源。

3.2 客户端实现步骤与代码解析

接下来是客户端的实现。客户端的主要任务是创建套接字,连接到服务器,然后进行数据传输。以下是客户端的实现步骤和代码解析:

#include <stdio.h>#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#define SERVER_IP 「127.0.0.1#define PORT 8888#define MAX_BUFFER_SIZE 1024intmain(){    // 创建套接字    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    if (sockfd == -1) {        perror(「socket creation failed」);        return 1;    }    printf(「Socket created successfully, sockfd: %d\n」, sockfd);    // 连接服务器    struct sockaddr_in serv_addr;    serv_addr.sin_family = AF_INET;    serv_addr.sin_port = htons(PORT);    // 优化 IP 转换错误处理    if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {        perror(「invalid server IP address」);        close(sockfd);        return 1;    }    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {        perror(「connect failed」);        close(sockfd);        return 1;    }    printf(「Connected to server\n」);    // 数据传输    const char *message = 「Hello, server!」;    if (send(sockfd, message, strlen(message), 0) == -1) {        perror(「send failed」);        close(sockfd);        return 1;    }    printf(「Message sent to server\n」);    char buffer[MAX_BUFFER_SIZE] = {0};    ssize_t valread = recv(sockfd, buffer, MAX_BUFFER_SIZE - 10); // 留 1 字节存结束符    if (valread == -1) {        perror(「recv failed」);        close(sockfd);        return 1;    } else if (valread == 0) {        printf(「Server closed connection during receive\n」);        close(sockfd);        return 1;    }    buffer[valread] = 『\0』; // 确保字符串结束符    printf(「Received from server: %s\n」, buffer);    // 关闭连接    close(sockfd);    return 0;}
  1. 创建套接字:与服务器端类似,使用 socket 系统调用创建一个基于 IPv4 的 TCP 套接字。若创建失败,打印错误信息并退出程序。
  2. 连接服务器:定义一个 sockaddr_in 结构体 serv_addr,填充服务器的地址信息,包括 IP 地址和端口号。使用 inet_pton 函数将点分十进制格式的 IP 地址转换为网络字节序的二进制形式。然后调用 connect 系统调用,向服务器发起连接请求。如果连接失败,打印错误信息并关闭套接字。
  3. 数据传输:构造一个要发送给服务器的消息,使用 send 系统调用将消息发送到服务器。若发送成功,打印提示信息。接着创建一个缓冲区 buffer,使用 read 系统调用从服务器接收响应数据。如果读取成功,打印接收到的响应。若读取或发送失败,打印错误信息并关闭套接字。
  4. 关闭连接:通信完成后,使用 close 系统调用关闭套接字,释放资源。

3.3 运行与测试

完成服务器端和客户端代码的编写后,我们来进行编译、运行和测试。

(1)编译:在终端中,使用 gcc 编译器分别编译服务器端和客户端代码。假设服务器端代码文件名为 server.C,客户端代码文件名为 client.C,则编译命令如下:

gcc -o server server.Cgcc -o client client.C

这两条命令会分别生成可执行文件 server 和 client

(2)运行:首先运行服务器端程序,在终端中输入:

./server

服务器端程序启动后,会创建套接字、绑定地址和端口、开始监听连接请求,并等待客户端连接。然后在另一个终端中运行客户端程序,输入:

./client

客户端程序会创建套接字并尝试连接服务器。如果连接成功,客户端会发送消息给服务器,并等待服务器的响应。

(3)测试:当客户端成功连接到服务器并发送消息后,服务器会接收消息并返回响应。我们可以在服务器端和客户端的终端中看到相应的输出信息,确认数据的发送和接收是否正确。例如,在服务器端终端会显示:

Socket created successfully, sockfd: 3Bind successfulListening on port 8888...Client connected: 127.0.0.1:54321Received from client: Hello, server!Response sent to client

在客户端终端会显示:

Socket created successfully, sockfd3Connected to serverMessage sent to serverReceived from serverMessage received successfully!

通过这些输出信息,可以验证服务器端和客户端之间的 TCP 连接建立成功,并且数据能够正确传输。此外,我们还可以使用一些网络工具,如 netstat 命令来查看当前的网络连接状态,进一步确认服务器和客户端之间的连接情况。例如,使用 netstat -anp | grep 8888 命令可以查看与 8888 端口相关的网络连接信息,包括连接的 IP 地址、端口号、状态等。

四、常见问题与解决方案

4.1 端口冲突问题

在 Linux TCP 开发过程中,端口冲突是一个较为常见的问题。当多个应用程序尝试绑定到同一个端口时,就会发生端口冲突,导致其中一个应用程序无法正常启动或运行。例如,我们在开发一个 Web 服务器时,将其端口设置为 8080,但系统中已经有另一个服务在监听 8080 端口,此时就会出现端口冲突。

端口冲突产生的原因主要是由于端口资源的有限性和独占性。在 Linux 系统中,每个端口在同一时刻只能被一个进程占用。如果有多个进程试图绑定到相同的端口,就会引发冲突。常见的情况包括:之前启动的服务没有正常关闭,导致端口被残留进程占用;或者在开发和测试过程中,不同的应用程序配置了相同的默认端口。

要解决端口冲突问题,首先需要确定是哪个进程占用了目标端口。在 Linux 系统中,可以使用多种工具来查看端口占用情况。例如,使用 netstat 命令,通过 netstat -tulnp | grep 端口号,可以列出所有正在监听的 TCP 和 UDP 端口,并显示对应的进程 ID(PID)和程序名称。其中,-t 表示 TCP 协议,-u 表示 UDP 协议,-l 表示仅显示监听套接字,-n 表示不进行 DNS 轮询,直接显示 IP 和端口号,-p 表示显示进程标识符和程序名称。比如要查看 8080 端口的占用情况,命令为 netstat -tulnp | grep 8080。

也可以使用 lsof 命令,通过 lsof -i:端口号来实现相同的功能。lsof(list open files)用于列出系统中所有打开的文件,由于网络端口在 Linux 中被视为特殊文件,因此可以通过该命令识别监听或已连接的端口及其对应进程 。例如 lsof -i:8080,就能显示 8080 端口相关的进程信息。

当确定了占用端口的进程后,如果该进程是不必要的,可以使用 kill 命令终止该进程,从而释放端口。例如,若通过 netstat 或 lsof 命令查找到占用 8080 端口的进程 ID 为 1234,那么可以使用 kill 1234 命令来终止该进程 。但在终止进程前,一定要确保该进程的终止不会对系统其他部分造成不可预料的损害。如果不能终止占用端口的进程,或者不想终止该进程,可以考虑修改应用程序的配置文件,将其绑定到其他未被占用的端口。在修改配置文件后,需要重启应用程序,使新的端口配置生效。

4.2 连接超时问题

连接超时是指在与服务器或其他设备建立网络连接时,系统规定的连接时间到达而无法建立连接的情况。在 Linux TCP 开发中,连接超时问题可能会出现在客户端尝试连接服务器的过程中,例如使用 connect 函数时,如果在规定时间内无法完成连接,就会返回连接超时错误。

连接超时的原因较为复杂,主要包括网络延迟和服务器负载过高两个方面。网络延迟可能是由于网络拥堵、路由器故障、网络线路质量不佳等原因导致数据包传输缓慢,从而使连接建立的时间延长,最终超过了设置的超时时间。当服务器负载过高时,大量的请求同时到达服务器,服务器忙于处理其他任务,无法及时响应新的连接请求,也会导致客户端连接超时。

为了解决连接超时问题,首先要设置合理的超时时间。在编写代码时,可以根据实际应用场景和网络状况,设置合适的连接超时时间。例如,在高并发系统中,为了快速释放线程资源,可以将连接超时时间设置为 100ms 到 1 秒;对于一般的网络应用,可以设置为几秒到几十秒 。同时,也可以考虑动态调整超时时间,根据系统运行情况,结合监控数据分析超时原因,并逐步优化超时时间的设置。

优化网络配置也是解决连接超时问题的重要措施。可以检查网络连接是否正常,使用 ping 命令测试目标主机的连通性,判断是否存在网络故障。如果 ping 某个目标时出现超时现象,说明网络连接可能存在问题,可以尝试重新连接网络或者联系网络管理员解决问题。检查本地防火墙和路由设置,确保没有规则阻止出站连接,同时确认路由表正确,默认网关设置无误。若使用代理,要确认环境变量 http_proxy、https_proxy 已正确配置,或命令行工具是否绕过代理。还可以通过调整系统的网络参数,如增加文件句柄限制、调整端口范围、启用连接复用等,来提高网络性能,减少连接超时的发生。

4.3 数据收发异常

在 Linux TCP 开发中,数据收发异常也是一个需要关注的问题。数据收发异常可能表现为数据丢失、数据乱序、接收的数据不完整等情况。例如,在文件传输应用中,如果出现数据收发异常,可能导致文件传输不完整,无法正常使用。

数据收发异常的原因有很多,缓冲区溢出和网络中断是较为常见的因素。当应用程序向套接字发送数据时,如果发送缓冲区已满,而新的数据又不断写入,就会导致缓冲区溢出,从而造成数据丢失或损坏。在接收数据时,如果接收缓冲区设置过小,无法容纳接收到的数据,也会出现数据丢失的情况。网络中断可能是由于网络故障、信号不稳定等原因导致,当网络中断时,正在进行的数据传输就会被迫中断,可能会导致数据收发异常。

为了解决数据收发异常问题,合理使用错误处理函数至关重要。在进行数据发送和接收操作时,要及时检查函数的返回值,判断是否发生错误。例如,send 和 recv 函数在发生错误时会返回 - 1,并设置 errno 来指示错误原因。可以根据 errno 的值来判断具体的错误类型,并进行相应的处理。如果是因为缓冲区溢出导致的错误,可以调整缓冲区大小;如果是网络中断导致的错误,可以尝试重新连接并重新发送或接收数据。

优化缓冲区设置也是解决数据收发异常的关键。在发送数据时,可以根据数据量的大小,合理设置发送缓冲区的大小,避免缓冲区溢出。在接收数据时,要确保接收缓冲区足够大,能够容纳可能接收到的最大数据量。也可以采用动态调整缓冲区大小的策略,根据实际的数据传输情况,动态调整缓冲区的大小,以提高数据传输的效率和稳定性。还可以使用一些高级的技术,如零拷贝技术,减少数据在用户空间和内核空间之间的拷贝次数,提高数据传输的性能,减少数据收发异常的发生。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-07 15:15:53 HTTP/2.0 GET : https://f.mffb.com.cn/a/469932.html
  2. 运行时间 : 0.128084s [ 吞吐率:7.81req/s ] 内存消耗:4,626.59kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=de1de6d37462ce839e5aab3afc2e8049
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000511s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000594s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.008929s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.001230s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000543s ]
  6. SELECT * FROM `set` [ RunTime:0.000324s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000577s ]
  8. SELECT * FROM `article` WHERE `id` = 469932 LIMIT 1 [ RunTime:0.003631s ]
  9. UPDATE `article` SET `lasttime` = 1770448553 WHERE `id` = 469932 [ RunTime:0.005061s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.004655s ]
  11. SELECT * FROM `article` WHERE `id` < 469932 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000453s ]
  12. SELECT * FROM `article` WHERE `id` > 469932 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000411s ]
  13. SELECT * FROM `article` WHERE `id` < 469932 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.003726s ]
  14. SELECT * FROM `article` WHERE `id` < 469932 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.008581s ]
  15. SELECT * FROM `article` WHERE `id` < 469932 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.010534s ]
0.129785s