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

news2025/1/16 9:38:02

一.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_UNIX 或 AF_LOCAL:Unix 域套接字,用于本地通信。
    • AF_PACKET:用于与链路层通信(如以太网接口)。
    • 还有其他的地址族,例如 AF_APPLETALKAF_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_addr 或 struct 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,以保证字符串正确终止。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2277478.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Go】Go Gorm 详解

1. 概念 Gorm 官网&#xff1a;https://gorm.io/zh_CN/docs/ Gorm&#xff1a;The fantastic ORM library for Golang aims to be developer friendly&#xff0c;这是官网的介绍&#xff0c;简单来说 Gorm 就是一款高性能的 Golang ORM 库&#xff0c;便于开发人员提高效率 那…

51单片机 AT24C02(I2C总线)

存储器 随机存储 RAM 只读存储 ROM AT24C02芯片 是一种可以实现掉电不丢失的存储器&#xff0c;可用于保存单片机运行时想要永久保存的数据信息 存储材质&#xff1a;E2PROM 通讯接口&#xff1a;I2C总线 容量&#xff1a;256字节 I2C总线 一种通用的数据总线 两根通信线…

再见IT!

再见IT 学了三年半前端&#xff0c;今天可能真的要和我最爱的前端说拜拜了&#xff01;没办法大局为重&#xff01; 在这个AI乱飞和短视频风口的时代&#xff0c;只能说当下学习任何一个技术远比2020年学习起来要简单的多。往后技术的发展无疑是飞速的&#xff0c;智能的&…

【开源免费】基于Vue和SpringBoot的人口老龄化社区服务与管理平台(附论文)

本文项目编号 T 140 &#xff0c;文末自助获取源码 \color{red}{T140&#xff0c;文末自助获取源码} T140&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

回归预测 | MATLAB实SVM支持向量机多输入单输出回归预测

效果一览 基本介绍 回归预测 | MATLAB实SVM支持向量机多输入单输出回归预测 …………训练集误差指标………… 1.均方差(MSE)&#xff1a;166116.6814 2.根均方差(RMSE)&#xff1a;407.5741 3.平均绝对误差&#xff08;MAE&#xff09;&#xff1a;302.5888 4.平均相对百分误…

系统学习算法:专题四 前缀和

题目一&#xff1a; 算法原理&#xff1a; 这道题是一维前缀和的模板题&#xff0c;通过这道题我们可以了解什么是前缀和 题意很简单&#xff0c;就是先输入数组个数和查询次数&#xff0c;然后将数组的值放进数组&#xff0c;每次查询给2个数&#xff0c;第一个是起点&#x…

智能科技与共情能力加持,哈曼重新定义驾乘体验

2025年1月6日&#xff0c;拉斯维加斯&#xff0c;2025年国际消费电子展——想象一下&#xff0c;当您步入一辆汽车&#xff0c;它不仅能响应您的指令&#xff0c;更能理解您的需求、适应您的偏好&#xff0c;并为您创造一个独特且专属的交互环境。作为汽车科技领域的知名企业和…

[java基础-集合篇]LinkedBlockingQueue源码解析

关联较强的上一篇&#xff1a;[java基础-集合篇]有界阻塞队列ArrayBlockingQueue源码解析-CSDN博客 总的来说。LinkedBlockingQueue 是一个基于链表节点的自定大小的线程安全的阻塞队列。遵循FIFO&#xff0c;结构上一端进一端出的单向队列。 源码注释 翻译 An optionally-boun…

从论文到实践:Stable Diffusion模型一键生成高质量AI绘画

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;编程探索专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年12月24日10点02分 神秘男子影, 秘而不宣藏。 泣意深不见, 男子自持重, 子夜独自沉。 AI绘画一键生成美图-变成画家 本地部…

业务幂等性技术架构体系之消息幂等深入剖析

在系统中当使用消息队列时&#xff0c;无论做哪种技术选型&#xff0c;有很多问题是无论如何也不能忽视的&#xff0c;如&#xff1a;消息必达、消息幂等等。本文以典型的RabbitMQ为例&#xff0c;讲解如何保证消息幂等的可实施解决方案&#xff0c;其他MQ选型均可参考。 一、…

【2024年华为OD机试】 (B卷,100分)- 跳房子I(Java JS PythonC/C++)

一、问题描述 题目描述 跳房子&#xff0c;也叫跳飞机&#xff0c;是一种世界性的儿童游戏。 游戏参与者需要分多个回合按顺序跳到第1格直到房子的最后一格。 跳房子的过程中&#xff0c;可以向前跳&#xff0c;也可以向后跳。 假设房子的总格数是count&#xff0c;小红每…

鸿蒙打包发布

HarmonyOS应用/元服务发布&#xff08;打包发布&#xff09; https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/ide-publish-app-V13?catalogVersionV13 密钥&#xff1a;包含非对称加密中使用的公钥和私钥&#xff0c;存储在密钥库文件中&#xff0c;格式…

JAVA:在IDEA引入本地jar包的方法(不读取maven目录jar包)

问题&#xff1a; 有时maven使用的jar包版本是最新版&#xff0c;但项目需要的是旧版本&#xff0c;每次重新install会自动将mavan的jar包覆盖到项目的lib目录中&#xff0c;导致项目报错。 解决&#xff1a; 在IDEA中手动配置该jar包对应的目录。 点击菜单File->Projec…

Mac上安装Label Studio

在Mac上安装Anaconda并随后安装Label Studio&#xff0c;可以按照以下步骤进行&#xff1a; 1. 在Mac上安装Anaconda 首先&#xff0c;你需要从Anaconda的官方网站下载适用于Mac的安装程序。访问Anaconda官网&#xff0c;点击“Download Anaconda”按钮&#xff0c;选择适合M…

docker-compose和docker仓库

一、docker-compose 1.概述 docker-compose是一个自动编排工具&#xff0c;可以根据dockerfile自动化部署docker容器。 主要功能 配置定义 使用YAML文件&#xff08;通常命名为docker - compose.yml&#xff09;来描述应用程序的服务、网络和卷等配置。 容器编排 可以同时…

了解linux中的“of_property_read_u32()”

of_property_read_u32(node, "post-pwm-on-delay-ms",&data->post_pwm_on_delay); /*根据"post-pwm-on-delay-ms"&#xff0c;从属性中查找并读取一个32位整数*/ /*读到一个32位整数,保存到data->post_pwm_on_delay中*/ of_property_read_u32…

nodejs 037: 前端新手教程使用引导库 Intro.js

Intro.js简介 Intro.js 是一个流行的引导库&#xff0c;用于提供步进式的新手教程。它可以帮助你创建用户引导&#xff0c;展示一些步骤和提示&#xff0c;逐步引导用户了解应用程序的功能。 安装方法&#xff1a; npm install intro.js使用方法&#xff1a; import introJ…

Vue篇-07

Vue UI组件库 一、移动端常用的UI组件库 1.1、Vant 1.2、Cube UI 1.3、Mint UI 二、PC端常用的UI组件库 2.1、Element UI Element - The worlds most popular Vue UI framework 安装&#xff1a; 按需引入&#xff1a; 135_尚硅谷Vue技术_element-ui按需引入_哔哩哔哩_b…

适配器模式案例

如果在这样的结构中 我们在Controller中注入&#xff0c;但我们后续需要修改Oss时&#xff0c;比如从minioService改成AliyunService时&#xff0c;需要改动的代码很多。于是我们抽象出一个FileService&#xff0c;让controller只跟fileservice耦合&#xff0c;这样我没只需要在…

鸿蒙UI开发——键盘弹出避让模式设置

1、概 述 我们在鸿蒙开发时&#xff0c;不免会遇到用户输入场景&#xff0c;当用户准备输入时&#xff0c;会涉及到输入法的弹出&#xff0c;我们的界面针对输入法的弹出有两种避让模式&#xff1a;上抬模式、压缩模式。 下面针对输入法的两种避让模式的设置做简单介绍。 2、…