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_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_UNIXchar 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.0inet6 addr: fe80::20c:29ff:fe3d:5e80/64 Scope:LinkUP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1RX packets:12345 errors:0 dropped:0 overruns:0 frame:0TX packets:12345 errors:0 dropped:0 overruns:0 carrier:0collisions: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.mzph.cn/web/66436.shtml

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

相关文章

1 行命令引发的 Go 应用崩溃

一、前言 不久前&#xff0c;阿里云 ARMS 团队、编译器团队、MSE 团队携手合作&#xff0c;共同发布并开源了 Go 语言的编译时自动插桩技术。该技术以其零侵入的特性&#xff0c;为 Go 应用提供了与 Java 监控能力相媲美的解决方案。开发者只需将 go build 替换为新编译命令 o…

R语言的并发编程

R语言的并发编程 引言 在现代计算中&#xff0c;如何有效地利用计算资源进行数据处理和分析已成为一个重要的研究方向。尤其在大数据时代&#xff0c;数据量的急剧增加让单线程处理方式显得力不从心。为了解决这一问题&#xff0c;各种编程语言都开展了并发编程的研究和应用。…

Flink(十):DataStream API (七) 状态

1. 状态的定义 在 Apache Flink 中&#xff0c;状态&#xff08;State&#xff09; 是指在数据流处理过程中需要持久化和追踪的中间数据&#xff0c;它允许 Flink 在处理事件时保持上下文信息&#xff0c;从而支持复杂的流式计算任务&#xff0c;如聚合、窗口计算、联接等。状…

C#项目生成时提示缺少引用

问题描述 刚从git或svn拉取下来的C#项目&#xff0c;在VS生成时提示缺少引用 解决方案 1、从“管理NuGet程序包”中下载并安装缺少的引用&#xff0c;如果引用较多逐个下载安装会比较麻烦&#xff0c;建议采用下面第2种方案处理 2、通过命令对所有缺少引用进行安装 &#…

EAMM: 通过基于音频的情感感知运动模型实现的一次性情感对话人脸合成

EAMM: 通过基于音频的情感感知运动模型实现的一次性情感对话人脸合成 1所有的材料都可以在EAMM: One-Shot Emotional Talking Face via Audio-Based Emotion-Aware Motion Model网站上找到。 摘要 尽管音频驱动的对话人脸生成技术已取得显著进展&#xff0c;但现有方法要么忽…

BeanFactory 是什么?它与 ApplicationContext 有什么区别?

谈到Spring&#xff0c;那势必要讲讲容器 BeanFactory 和 ApplicationContext。 BeanFactory是什么&#xff1f; BeanFactory&#xff0c;其实就是 Spring 容器&#xff0c;用于管理和操作 Spring 容器中的 Bean。可能此时又有初学的小伙伴会问&#xff1a;Bean 是什么&#x…

【深度学习】Huber Loss详解

文章目录 1. Huber Loss 原理详解2. Pytorch 代码详解3.与 MSELoss、MAELoss 区别及各自优缺点3.1 MSELoss 均方误差损失3.2 MAELoss 平均绝对误差损失3.3 Huber Loss 4. 总结4.1 优化平滑4.2 梯度较好4.3 为什么说 MSE 是平滑的 1. Huber Loss 原理详解 Huber Loss 是一种结合…

python实现pdf转word和excel

一、引言   在办公中&#xff0c;我们经常遇收到pdf文件格式&#xff0c;因为pdf格式文件不易修改&#xff0c;当我们需要编辑这些pdf文件时&#xff0c;经常需要开通会员或收费功能才能使用编辑功能。今天&#xff0c;我要和大家分享的&#xff0c;是如何使用python编程实现…

【PyCharm】连接Jupyter Notebook

【PyCharm】相关链接 【PyCharm】连接 Git【PyCharm】连接Jupyter Notebook【PyCharm】快捷键使用【PyCharm】远程连接Linux服务器【PyCharm】设置为中文界面 【PyCharm】连接Jupyter Notebook PyCharm连接Jupyter Notebook的过程可以根据不同的需求分为 本地连接 和 远程连…

Java锁 公平锁和非公平锁 ReentrantLock() 深入源码解析

卖票问题 我们现在有五个售票员 五个线程分别卖票 卖票 ReentrantLock(); 运行后全是 a 对象获取 非公平锁缺点之一 容易出现锁饥饿 默认是使用的非公平锁 也可以传入一个 true 参数 使其变成公平锁 生活中排队讲求先来后到 视为公平 程序中的公平性也是符合请求锁的绝对…

「刘一哥GIS」系列专栏《GRASS GIS零基础入门实验教程(配套案例数据)》专栏上线了

「刘一哥GIS」系列专栏《GRASS GIS零基础入门实验教程》全新上线了&#xff0c;欢迎广大GISer朋友关注&#xff0c;一起探索GIS奥秘&#xff0c;分享GIS价值&#xff01; 本专栏以实战案例的形式&#xff0c;深入浅出地介绍了GRASS GIS的基本使用方法&#xff0c;用一个个实例讲…

企业级NoSQL数据库Redis

1.浏览器缓存过期机制 1.1 最后修改时间 last-modified 浏览器缓存机制是优化网页加载速度和减少服务器负载的重要手段。以下是关于浏览器缓存过期机制、Last-Modified 和 ETag 的详细讲解&#xff1a; 一、Last-Modified 头部 定义&#xff1a;Last-Modified 表示服务器上资源…

使用Flask和Pydantic实现参数验证

使用Flask和Pydantic实现参数验证 1 简介 Pydantic是一个用于数据验证和解析的 Python 库&#xff0c;版本2的性能有较大提升&#xff0c;很多框架使用Pydantic做数据校验。 # 官方参考文档 https://docs.pydantic.dev/latest/# Github地址 https://github.com/pydantic/pyd…

ScratchLLMStepByStep:训练自己的Tokenizer

1. 引言 分词器是每个大语言模型必不可少的组件&#xff0c;但每个大语言模型的分词器几乎都不相同。如果要训练自己的分词器&#xff0c;可以使用huggingface的tokenizers框架&#xff0c;tokenizers包含以下主要组件&#xff1a; Tokenizer: 分词器的核心组件&#xff0c;定…

C# OpenCvSharp 部署3D人脸重建3DDFA-V3

目录 说明 效果 模型信息 landmark.onnx net_recon.onnx net_recon_mbnet.onnx retinaface_resnet50.onnx 项目 代码 下载 参考 C# OpenCvSharp 部署3D人脸重建3DDFA-V3 说明 地址&#xff1a;https://github.com/wang-zidu/3DDFA-V3 3DDFA_V3 uses the geometri…

从零开始学数据库 day2 DML

从零开始学数据库&#xff1a;DML操作详解 在今天的数字化时代&#xff0c;数据库的使用已经成为了各行各业的必备技能。无论你是想开发一个简单的应用&#xff0c;还是想要管理复杂的数据&#xff0c;掌握数据库的基本操作都是至关重要的。在这篇博客中&#xff0c;我们将专注…

Java 8 Stream API

文章目录 Java 8 Stream API1. Stream2. Stream 的创建3. 常见的 Stream 操作3.1 中间操作3.2 终止操作 4. Stream 的并行操作 Java 8 Stream API Java 8 引入了 Stream API&#xff0c;使得对集合类&#xff08;如 List、Set 等&#xff09;的操作变得更加简洁和直观。Stream…

运行fastGPT 第五步 配置FastGPT和上传知识库 打造AI客服

运行fastGPT 第五步 配置FastGPT和上传知识库 打造AI客服 根据上一步的步骤&#xff0c;已经调试了ONE API的接口&#xff0c;下面&#xff0c;我们就登陆fastGPT吧 http://xxx.xxx.xxx.xxx:3000/ 这个就是你的fastGPT后台地址&#xff0c;可以在configer文件中找到。 账号是…

第4章 Kafka核心API——Kafka客户端操作

Kafka客户端操作 一. 客户端操作1. AdminClient API 一. 客户端操作 1. AdminClient API

【王树森搜索引擎技术】相关性02:评价指标(AUC、正逆序比、DCG)

相关性的评价指标 Pointwise评价指标&#xff1a;Area Under the Curve&#xff08;AUC&#xff09;Pairwise评价指标&#xff1a;正逆序比&#xff08;Positive to Negative Ratio, PNR&#xff09;Listwise评价指标&#xff1a;Discounted Cumulative Gain(DCG)用AUC和PNR作…