Linux网络_套接字_UDP网络_TCP网络

一.UDP网络

1.socket()创建套接字

复制代码
#include<sys/socket.h>
int socket(int domain, int type, int protocol);
  1. domain (地址族): AF_INET网络 AF_UNIX本地

    • AF_INET :IPv4 地址族,适用于 IPv4 协议。用于网络通信
    • AF_INET6:IPv6 地址族,适用于 IPv6 协议。
    • AF_UNIXAF_LOCAL:Unix 域套接字,用于本地通信。
    • AF_PACKET:用于与链路层通信(如以太网接口)。
    • 还有其他的地址族,例如 AF_APPLETALK, AF_NETLINK 等,但这些较少使用。
  2. type (套接字类型):

    • SOCK_STREAM :流式套接字,表示TCP 协议,面向连接,提供可靠的数据传输。
    • SOCK_DGRAM :数据报套接字,表示UDP 协议,无连接的、不可靠的数据传输。
    • SOCK_RAW:原始套接字,允许直接操作底层协议,如 IP、ICMP 等(通常需要管理员权限)。
    • SOCK_SEQPACKET:顺序数据包套接字,适用于某些特定的协议。
  3. protocol (协议类型):

    • 通常设置为 0,系统会根据地址族和套接字类型自动选择合适的协议。
    • 也可以显式指定某个协议,例如:IPPROTO_TCP(TCP协议)或 IPPROTO_UDP(UDP协议)。

返回值

  • 如果调用成功,返回一个套接字描述符(一个非负整数),该描述符是后续与套接字进行交互的标识。
  • 如果调用失败,返回 -1,并设置 errno 来指示错误原因。

错误代码

  • EAFNOSUPPORT:不支持指定的地址族。
  • EINVAL:无效的套接字类型或协议。
  • ENFILE:系统中可用的文件描述符已用尽。
  • ENOMEM:系统内存不足。

2.bind()绑定

在 Linux 中,bind 系统调用用于将一个 套接字(socket)与一个本地地址(IP 地址和端口号)绑定。这个操作通常用于服务器端,目的是让服务器的套接字可以接收来自特定地址和端口的网络数据。

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

参数:

  • sockfd :要绑定的套接字描述符 ,通常是通过 socket() 系统调用创建的。
  • addr :指向一个 struct sockaddr****或其派生结构体的指针 ,用于指定要绑定的地址和端口。这个结构体的内容取决于地址族(AF_INETAF_INET6 等)。
  • addrlenaddr 指向的地址结构的大小,通常是 sizeof(struct sockaddr_in)sizeof(struct sockaddr_in6)

返回值:

  • 成功 :返回 0
  • 失败 :返回 -1,并设置 errno 以指示错误。

sockaddr 结构

socket API 是一层抽象的网络编程接口,适用于各种底层网络协议,如 IPv4、IPv6,以及

后面要讲的 UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同.
#include<netinet/in.h>

#include<arpa/inet.h>

sockaddr:一个通用的地址结构体,所有网络地址结构体(如 sockaddr_in 和 sockaddr_un)都可以通过它来表示。它为不同协议族提供了统一的接口。

sockaddr_in :用于表示 IPv4 地址的结构体。它扩展了 sockaddr,包括端口号、IP 地址等信息,常用于网络通信/本地。

sockaddr_un :用于表示 UNIX 域套接字地址的结构体,主要用于进程间通信(本地),套接字地址基于文件路径。

struct sockaddr 结构

复制代码
#include <netinet/in.h>
struct sockaddr {
    sa_family_t sa_family; // 地址族,例如 AF_INET 或 AF_UNIX
    char sa_data[14];      // 用于存储地址信息的字符数组
};

strcuct sockaddr_in 结构

复制代码
struct sockaddr_in {
    sa_family_t    sin_family; // 地址族,通常为 AF_INET(网络通信)
    in_port_t      sin_port;   // 端口号,使用网络字节序(大端)
    struct in_addr sin_addr;   // IP 地址
    unsigned char  sin_zero[8]; // 填充字节,保持结构体大小一致
};
struct in_addr {
    in_addr_t s_addr; // 32 位的 IP 地址
};

strcuct sockaddr_un 结构

复制代码
struct sockaddr_un {
    sa_family_t sun_family;  // 地址族,通常是 AF_UNIX(本地通信)
    char sun_path[108];      // UNIX 域套接字路径,最大长度为 108 字节
};

在套接字编程中,我们通常会使用特定的结构体(如 sockaddr_in 或 sockaddr_un)来处理网络地址 ,而 sockaddr 作为通用结构体,通常在系统调用(如 bind(), connect() 等)中使用,要求通过类型转换将具体的地址结构体转为 sockaddr 类型。

htons() 主机字节序转网络字节序

因为TCP/IP 协议规定,网络数据流应采用大端字节序。

所以向网络发数据应改位大端。

htons()返回转换后的 16 位无符号整数,这个整数是网络字节序(大端字节序)下的值。

htonl() 回转换后的32 位无符号整数,这个整数是网络字节序(大端字节序)下的值。

ntohs()用于将网络字节序(大端字节序)转换回主机字节序。16位无符号整数

inet_addr() 字符型ip地址->32位网络字节序

inet_addr 是一个用于将 IPv4 地址从点分十进制字符串表示(如 "192.168.1.1")转换为网络字节序的 in_addr_t 类型(通常是一个 32 位的无符号整数)的方法。

复制代码
#include <arpa/inet.h>  // 包含 inet_addr 的定义
in_addr_t inet_addr(const char *cp);

字符转整型+改大端

inet_pton() 字符型ip地址->32位网络字节序

复制代码
int inet_pton(int af, const char *src, void *dst);

参数:

  • af: 地址族,指定了 IP 地址的类型。常见的值有:

    • AF_INET:表示 IPv4 地址。
    • AF_INET6:表示 IPv6 地址。
  • src : 输入的地址字符串 ,表示待转换的 IP 地址。例如,IPv4 地址是 "192.168.1.1",IPv6 地址可能是 "2001:0db8:85a3:0000:0000:8a2e:0370:7334"

  • dst : 指向用于存储转换后的二进制地址的缓冲区 。对于 IPv4,通常是 struct in_addr 类型,对于 IPv6,通常是 struct in6_addr 类型。

返回值:

  • 成功 :返回 1,表示地址转换成功。
  • 失败
    • 如果输入的 IP 地址格式无效(例如,IPv4 地址包含不合法的数字),返回 0
    • 如果出现其他错误,返回 -1,并且可以通过 errno 获取详细错误信息。

inet_ntoa() 32位网络字节序->字符型ip地址

inet_ntoa 是一个用于将 IPv4 地址(以网络字节顺序存储的二进制格式)转换为点分十进制

格式的函数。这个函数通常在 C 语言中使用,用于将 struct in_addr 结构中的二进制形式的 IPv4 地址转换为标准的文本表示形式。

复制代码
<arpa/inet.h>
char *inet_ntoa(struct in_addr in);

inet_ntop() 32位网络字节序->字符型ip地址(多线程)

inet_ntop() 是一个更通用的函数,支持 IPv4 和 IPv6 地址。把转换的字符串地址放在提供的栈空间中,防止被覆盖。

inet_ntoa() 函数返回的是一个静态的缓冲区 ,**该缓冲区在函数调用之间会被复用,因此在多次调用 inet_ntoa() 时,返回的字符串会被覆盖。**这使得在 多线程环境 中使用 inet_ntoa() 可能导致线程之间共享同一个静态缓冲区,进而引发 数据竞争 或 意外的覆盖问题。

复制代码
#include <stdio.h>
#include <arpa/inet.h>

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
  • af:地址族,指定是 IPv4 还是 IPv6。IPv4 使用 AF_INET ,IPv6 使用 AF_INET6
  • src:指向网络字节顺序的地址结构(struct in_addrstruct in6_addr)。
  • dst:用于存储结果字符串的缓冲区,必须足够大以容纳结果字符串(IPv4 地址需要 16 字节,IPv6 地址需要 46 字节)。
  • sizedst缓冲区的大小。

返回值

  • 如果成功,返回 dst 指向的字符串。
  • 如果出错,返回 NULL,并设置 errno

3.recvfrom()接收数据

recvfrom 是一个用于接收数据的系统调用,它通常用于 UDP 套接字,或者在某些情况下,用于接收来自不同主机的 TCP 数据。它的功能是从指定的套接字中接收数据,并且可以获取远程主机的地址信息。

复制代码
#include <arpa/inet.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 
                 struct sockaddr *src_addr, socklen_t *addrlen);

//socklen_t unsigned int 无符号整型

参数说明:

  • sockfd :目标套接字的文件描述符 ,通常是通过 socket() 函数返回的套接字描述符。
  • buf :指向接收数据的缓冲区
  • len :缓冲区的长度,即可以接收的最大字节数
  • flags :接收的标志位,通常为 0 ,或者使用一些标志,如 MSG_PEEK 等。
  • src_addr :指向 struct sockaddr 结构的指针,用于存放发送方的地址信息。对于 UDP 来说,这将包含发送方的 IP 地址和端口号。
  • addrlen指向一个 socklen_t 类型的变量表示 src_addr 缓冲区的大小recvfrom 调用返回后,这个变量将被更新为实际存放的地址长度。

返回值:

  • 成功时,返回实际接收到的字节数。
  • 出错时,返回 -1,并设置 errno
    在使用 recvfrom 时,addrlen 初始时需要包含 src_addr 缓冲区的大小。 recvfrom 在接收数据时,会根据实际填充的地址长度来更新 addrlen。

**这种机制主要是为了处理不同类型的地址结构,它们可能有不同的大小。**例如,IPv4 和 IPv6 的地址结构有不同的大小,因此 addrlen 必须传入一个适当的初始值,并且 recvfrom 会修改它,以便传出实际的地址长度。
如果 addrlen 不包含缓冲区的大小,系统无法确定在接收数据时应该使用多大的内存空间来存储地址信息,这可能导致:

无法获取正确的地址: recvfrom 在更新 addrlen 时,必须知道地址结构的空间。没有正确的大小,recvfrom 可能不会正确填充地址信息,或者根本无法获取发送方的地址。

4.sendto()发送数据

sendto 是一个用于发送数据报文(datagram)的系统调用函数,通常在基于 UDP 的网络编程中使用。它允许应用程序将数据发送到指定的目标地址,而无需先建立连接。它是 sockets API 的一部分,适用于无连接的协议,如 UDP。

复制代码
#include <sys/types.h>
#include <sys/socket.h>

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);

参数说明:

  1. sockfd

    一个已打开的 socket 描述符。它是通过 socket() 函数返回的,并且该 socket 必须是使用无连接协议(如 UDP)创建的。

  2. buf

    指向包含要发送数据的缓冲区的指针 。数据的大小由 len 参数指定。

  3. len

    发送的数据的长度(字节数)。

  4. flags

    发送的标志,通常为 0 。可以使用一些额外的标志,如 MSG_CONFIRMMSG_DONTWAITMSG_NOSIGNAL 等,具体使用取决于需求。

  5. dest_addr

    指向一个 struct sockaddr 类型的结构体,表示目标地址。 对于 UDP,这通常是一个 struct sockaddr_in 类型的结构,包含目标主机的 IP 地址和端口号。

  6. addrlen

    dest_addr 结构的长度 ,通常是 sizeof(struct sockaddr_in)

返回值:

  • 成功时,返回发送的字节数(即 len)。
  • 失败时,返回 -1,并设置 errno 以指示错误。

netstat命令查看网络服务是否启动

netstat -nuap

-u 查看UDP连接

-a 查看所有网络连接

-p 查看每个连接的 PID(进程标识符)

-n 以数字形式显示地址和端口号

ifconfig命令查看网络接口(ip mac)

ifconfig 是一个用于在 Unix-like 操作系统(如 Linux 和 macOS)中查看或配置网络接口的命令。它通常用于显示当前的网络接口配置,启用或禁用网络接口,配置 IP 地址,子网掩码,广播地址等。

复制代码
eth0      Link encap:Ethernet  HWaddr 00:0c:29:3d:5e:80  
          inet addr:192.168.1.10  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe3d:5e80/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:12345 errors:0 dropped:0 overruns:0 frame:0
          TX packets:12345 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:9876543 (9.8 MB)  TX bytes:9876543 (9.8 MB)

在上面的输出中,eth0 是网络接口的名称,inet addr 显示的是接口的 IPv4 地址。
在一些现代的 Linux 发行版中,ifconfig 已被 ip 命令(由 iproute2 提供)所取代,ip 命令提供了更多的功能和更细粒度的控制。例如,使用以下命令查看网络接口:
ip a

INADDR_ANY宏 人员地址绑定

它是一个 in_addr_t 类型的值,通常用于网络编程中的 sockaddr_in 结构体,表示一个可以匹配所有网络接口的地址。

INADDR_ANY 是一个常量,通常被定义为 0.0.0.0。

在绑定套接字时使用 INADDR_ANY,服务器可以接收来自本机所有接口的连接请求。

复制代码
#define INADDR_ANY ((in_addr_t) 0x00000000)

UPD网络流程

1. socket()创建套接字

客户端和服务器端都需要使用 socket() 函数 创建一个 UDP 套接字。UDP 套接字使用 SOCK_DGRAM类型。

复制代码
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);  // 创建 UDP 套接字

2. bind()设置服务器地址(服务器端)

服务器需要绑定一个本地的地址和端口,以便能够接收来自客户端的数据。

void *memset(void *ptr, int value, size_t num);把定义的sockaddr_in结构体清零。

INADDR_ANY 0.0.0.0 服务器可以接收来自本机所有接口的连接请求。

htons 转网络字节序

复制代码
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;  // 绑定到本机所有接口
server_addr.sin_port = htons(PORT);  // 绑定指定端口

bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));  // 绑定端口

3. sendto()发送数据(客户端)

客户端可以直接通过 sendto() 函数将数据发送到服务器的 IP 地址和端口,无需建立连接。

inet_pton() 用于将 IP 地址从文本表示(如点分十进制的字符串)转换为二进制形式

复制代码
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
inet_pton(AF_INET, "192.168.1.100", &server_addr.sin_addr);  // 服务器地址

char message[] = "Hello, Server!";
sendto(sockfd, message, sizeof(message), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));
//client不需要bind端口号吗?
//1.系统自动给客户端bind端口号 为什么?
//我客户端打开进程,只要保证进程的端口号有唯一性,能运行 连上服务端就行。没人会主动连我,所以没必 
  要知道端口号。
//同一个进程的端口号每次启动不一定是固定的,但一定唯一
//2.为什么服务端要显示bind端口号呢?
//因为服务端的端口号是不能随便改变的。不同的客户端访问到服务端就是靠唯一的端口号找到的。所以我们自 
  己定义端口号,固定端口号。

4. recvfrom() 接收数据(服务器端)

服务器端使用 recvfrom() 函数接收客户端发送的数据。该函数会返回数据并填充客户端的地址信息。

一定要设置对struct sockaddr结构体的大小addr_len

复制代码
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
char buffer[1024];

int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &addr_len);
buffer[n] = '\0';  // 添加字符串结束符
printf("Received from client: %s\n", buffer);

5. sendto() 发送响应(服务器端)

服务器端可以通过 sendto() 函数发送响应给客户端。

复制代码
char response[] = "Hello, Client!";
sendto(sockfd, response, sizeof(response), 0, (struct sockaddr*)&client_addr, addr_len);

6.关闭套接字

完成数据通信后,客户端和服务器都需要关闭套接字。

复制代码
close(sockfd);

二.TCP网络

1.listen() 监听套接字

listen() 将套接字设置为监听模式,准备接收来自客户端的连接请求。

复制代码
int listen(int sockfd, int backlog);

参数说明:

  • sockfd :这是一个已创建并绑定的套接字文件描述符,通常通过 socket()bind() 函数获得。
  • backlog定义了等待连接队列的最大长度。 这个队列用于存放尚未被 accept() 接受的连接请求。队列中的连接数量可能会有所限制,具体值由操作系统决定。backlog 的大小决定了可以挂起多少个连接请求。

返回值:

  • 成功 :返回 0,表示监听成功。
  • 失败 :返回 -1,并设置 errno 来指示错误原因。

2.accept() 获取套接字 客户端信息

accept 是一个用于处理网络连接的系统调用,通常与套接字(socket)编程一起使用。它的作用是从一个已建立的连接的监听套接字队列中接受一个连接,并为该连接创建一个新的套接字。

最后两个输出型参数 获取客户端信息ip+port

复制代码
int accept(int listen_sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:

  • sockfd:这是监听套接字的文件描述符 ,通常是通过 socket() 函数创建的套接字,并且通过 bind()listen() 函数绑定和监听。
  • addr:一个指向 struct sockaddr 结构体的指针,用来存储客户端的地址信息
  • addrlen:这个参数是一个指向**socklen_t 类型的指针** ,指示 addr 所指向的结构体的长度。在调用 accept 之后,它会被更新为实际存储的地址信息长度。

返回值:

  • 成功时,返回一个新的套接字描述符,这个套接字可以用来与客户端进行通信。
  • 如果失败,返回 -1,并设置 errno 以指示错误。

telnet命令

Linux 中使用 telnet 客户端进行网络连接或远程管理是非常常见的操作。虽然 Telnet 不提供加密,因此现在更多推荐使用 SSH(Secure Shell),但它仍然可以用于某些简单的远程连接和测试任务。

复制代码
telnet <hostname> <port>
<hostname>:目标主机的域名或 IP 地址。
<port>:目标主机上的端口号,Telnet 默认使用 端口 23。

在 Telnet 会话中,按下 Ctrl + ],进入命令模式后,输入 quit 或 exit 来退出。

3.connect()建立与服务端的连接

在网络编程中,connect 是一个系统调用,用于建立客户端与远程服务器之间的连接 。它通常用于TCP /IP 套接字编程,用于向服务器发起连接请求。

复制代码
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数

  • sockfd:一个已经通过 socket() 函数创建的套接字描述符。
  • addr:一个指向 sockaddr 结构的指针,该结构包含服务器的地址信息(如 IP 地址和端口)。
  • addrlenaddr 结构的大小,通常是 sizeof(struct sockaddr_in)sizeof(struct sockaddr_in6),取决于 IPv4 或 IPv6。

返回值

  • 成功时,返回 0。
  • 失败时,返回 -1,并设置 errno 来指示错误原因。

工作原理

  1. connect 调用会向目标主机发送连接请求(对于 TCP 是三次握手过程)。
  2. 如果连接成功,客户端与服务器之间就建立了连接,之后可以进行数据的发送和接收。
  3. 如果连接失败(例如目标主机不可达或端口不可用),connect 会返回 -1,并设置 errno,指示错误原因。

错误码(常见)

  • ECONNREFUSED:目标服务器拒绝连接。
  • ETIMEDOUT:连接超时。
  • EADDRINUSE:本地地址已经在使用。
  • EHOSTUNREACH:目标主机不可达。

4.recv() send() 收发消息

在网络编程中,recv 和 send 是两个常用的函数,它们分别用于接收和发送数据。这些函数通常与套接字(socket)一起使用,用于在客户端和服务器之间进行数据交换。

复制代码
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  • sockfd:是套接字的文件描述符,表示一个有效的已连接套接字。
  • buf:指向缓冲区的指针,recv 会将接收到的数据存放在这里。
  • len:缓冲区的大小,即 buf 能够存储的最大字节数。
  • flags:操作标志,通常为 0 或 MSG_WAITALL(等待接收指定字节数)。

返回值

  • 成功时,返回接收到的字节数。
  • 如果连接已关闭,返回 0。
  • 如果发生错误,返回 -1,并设置 errno
复制代码
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • sockfd:是套接字的文件描述符,表示一个有效的已连接套接字。
  • buf:指向包含要发送数据的缓冲区。
  • len:要发送的数据字节数。
  • flags:操作标志,通常为 0。

返回值

  • 成功时,返回实际发送的字节数。
  • 如果发生错误,返回 -1,并设置 errno
    recv 用于从网络上接收数据,它可以读取指定大小的字节。

send 用于向网络上发送数据,数据的大小由你提供。

read 和 write 没有套接字特有的标志控制功能,因此它们在某些情况下缺乏灵活性。例如,在高效处理网络数据时,可能需要精确控制数据接收量、非阻塞模式等,而 recv 和 send 可以通过特定标志轻松实现。

flags 参数:recv 和 send 都可以接受一个 flags 参数,这个参数可以控制一些特定的行为,例如 MSG_DONTWAIT(设为非阻塞状态) 或 MSG_WAITALL(接收指定数量的字节),但通常情况下我们将其设置为 0。

eg.

  • recv(sockfd, buffer, 512, MSG_WAITALL):此函数调用会阻塞直到至少接收到 512 字节的数据(或者发生错误)。
  • 如果没有收到足够的字节,recv 会继续等待,直到数据完整接收或遇到错误。

5.popen() pclose()父子进程间通信

我们之前怎么完成父子进程间通信的?

1.创建管道 int pipe(int pipefd[2]);pipefd[0] 是读取端,pipefd[1] 是写入端。

2.创建子进程 fork()

3.父进程关闭写端 子进程关闭读端 close()

4.子进程输出重定向到管道写端 dup2(pipefds[1], 1) 标志输出文件描述符是1

5.父进程从管道读端进行读取

下面popen pclose可以代替以上操作

FILE *popen(const char *command, const char *mode);

command:要执行命令的字符串

mode:"w" "r",读还是写

子进程执行完命令会把结果放在文件中并返回,pipe fork失败返回NULL

父进程接收文件并读取,最后关闭。
popen 用于打开一个进程,并将其标准输入(stdin)或标准输出(stdout)与父进程连接,返回一个文件指针,使得父进程可以通过该指针与子进程进行数据交互。

复制代码
FILE *popen(const char *command, const char *mode);

command: 要执行的命令字符串。它是你希望启动的子进程命令。

mode: 访问模式。常见的有:

"r":读取模式,父进程从子进程读取输出(即子进程的标准输出)。popen 会创建一个管道,将子进程的标准输出与父进程连接。父进程可以通过 fgets、fread 等读取子进程的输出。

"w":写入模式,父进程向子进程写入输入(即将数据传递到子进程的标准输入)。popen 会创建一个管道,将父进程的标准输入与子进程连接。父进程可以通过 fputs、fprintf 等向子进程传递数据。

eg.FILE* p=popen("ls","r");

复制代码
#include <stdio.h>

int main() {
    FILE *fp;
    char buffer[128];

    // 打开子进程,执行命令并读取输出
    fp = popen("ls", "r");
    if (fp == NULL) {
        perror("popen");
        return 1;
    }

    // 读取子进程输出并打印
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("%s", buffer);
    }

    // 关闭文件指针
    pclose(fp);
    return 0;
}

pclose 用于关闭由 popen 打开的文件指针,并等待子进程的终止。

复制代码
int pclose(FILE *fp);
  • fp: 是由 popen 返回的文件指针。

返回值:

  • 如果子进程正常退出,pclose 返回子进程的退出状态(通常是 0)。
  • 如果子进程异常退出,返回一个负值。

fgets() 读文件

fgets 是 C 语言标准库中的一个函数,用于从文件或其他输入流中读取一行数据 。它通常用于从文件、标准输入(如键盘输入)等地方读取字符串。与 gets 函数相比,fgets 更安全,因为它允许指定最大读取字符数,避免缓冲区溢出问题。

复制代码
char *fgets(char *str, int num, FILE *stream);

参数说明:

  • str :用于存储读取数据的字符数组。fgets 会将从流中读取的字符存储到 str 中。
  • num :要读取的最大字符数,包括结束符 \0。一般而言,num 应该是你希望读取的最大字节数 + 1,以便为字符串结尾的空字符(\0)留出空间。
  • stream :指向文件流的指针。它可以是:
    • stdin,用于从标准输入读取数据(通常是键盘)。
    • 任何通过 fopen 或其他方式打开的文件指针。

返回值:

  • 如果成功,fgets 返回 str,即读取的字符串。
  • 如果发生错误或到达文件末尾(EOF),返回 NULL

特点:

  • fgets 会读取指定数量的字符,直到遇到换行符 \n 或文件结尾(EOF),并将换行符包括在内。
  • fgets自动添加字符串结束符 \0,以保证字符串正确终止。
相关推荐
每天都要写算法(努力版)6 分钟前
【服务器操作指南】从 Hugging Face 上下载文件 | 从某一个网址上下载文件到 Linux 服务器的指定目录
linux·运维·服务器
天狼12221 小时前
第5章-1 优化服务器设置
运维·服务器·adb
傻啦嘿哟2 小时前
Python正则表达式:用“模式密码“解锁复杂字符串
linux·数据库·mysql
浪裡遊4 小时前
Linux常用指令
linux·运维·服务器·chrome·功能测试
SugarPPig4 小时前
PowerShell 查询及刷新环境变量
服务器
段ヤシ.5 小时前
银河麒麟(内核CentOS8)安装rbenv、ruby2.6.5和rails5.2.6
linux·centos·银河麒麟·rbenv·ruby2.6.5·rails 5.2.6
深夜情感老师6 小时前
centos离线安装ssh
linux·centos·ssh
我的作业错错错7 小时前
搭建私人网站
服务器·阿里云·私人网站
王景程7 小时前
如何测试短信接口
java·服务器·前端
微网兔子8 小时前
伺服器用什么语言开发呢?做什么用什么?
服务器·c++·后端·游戏