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_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_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.pswp.cn/web/66436.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/66436.shtml
英文地址,請注明出處:http://en.pswp.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作…