【Linux網絡編程學習】socket API(socket、bind、listen、accept、connect)及簡單應用

此為牛客Linux C++課程和黑馬Linux系統編程筆記。

1. 什么是socket

所謂 socket(套接字),就是對網絡中不同主機上的應用進程之間進行雙向通信的端點的抽象。
一個套接字就是網絡上進程通信的一端,提供了應用層進程利用網絡協議交換數據的機制。從所處
的地位來講,套接字上聯應用進程,下聯網絡協議棧,是應用程序通過網絡協議進行通信的接口,
是應用程序與網絡協議根進行交互的接口。

socket 可以看成是兩個網絡應用程序進行通信時,各自通信連接中的端點,這是一個邏輯上的概
念。它是網絡環境中進程間通信的 API,也是可以被命名和尋址的通信端點,使用中的每一個套接
字都有其類型和一個與之相連進程。通信時其中一個網絡應用程序將要傳輸的一段信息寫入它所在
主機的 socket 中,該 socket 通過與網絡接口卡(NIC)相連的傳輸介質將這段信息送到另外一臺
主機的 socket 中,使對方能夠接收到這段信息。socket 是由 IP 地址和端口結合的,提供向應用
層進程傳送數據包的機制。

socket 本身有“插座”的意思,在 Linux 環境下,用于表示進程間網絡通信的特殊文件類型。本質為
內核借助緩沖區形成的偽文件。既然是文件,那么理所當然的,我們可以使用文件描述符引用套接
。與管道類似的,Linux 系統將其封裝成文件的目的是為了統一接口,使得讀寫套接字和讀寫文
件的操作一致。區別是管道主要應用于本地進程間通信,而套接字多應用于網絡進程間數據的傳
遞。

套接字的內核實現較為復雜,不宜在學習初期深入學習。

在TCP/IP協議中,“IP地址+TCP或UDP端口號”唯一標識網絡通訊中的一個進程。“IP地址+端口號”就對應一個socket。欲建立連接的兩個進程各自有一個socket來標識,那么這兩個socket組成的socket pair就唯一標識一個連接。因此可以用Socket來描述網絡連接的一對一關系。
套接字通信原理如下圖所示:
在這里插入圖片描述
在網絡通信中,套接字一定是成對出現的。一端的發送緩沖區對應對端的接收緩沖區。我們使用同一個文件描述符索發送緩沖區和接收緩沖區。

2. socket相關函數

2.1 socket( )函數

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> // 包含了這個頭文件,上面兩個就可以省略
int socket(int domain, int type, int protocol);

功能:創建一個套接字

參數:

  • domain: 協議族:
    AF_INET : ipv4
    AF_INET6 : ipv6
    AF_UNIX, AF_LOCAL : 本地套接字通信(進程間通信)

  • type: 通信過程中使用的協議類型:
    SOCK_STREAM : 流式協議
    SOCK_DGRAM : 報式協議

  • protocol : 具體的一個協議。一般寫0:
    SOCK_STREAM : 流式協議默認使用 TCP
    SOCK_DGRAM : 報式協議默認使用 UDP

  • 返回值: 成功:返回文件描述符,操作的就是內核緩沖區。失敗:-1

socket( )打開一個網絡通訊端口,如果成功的話,就像open()一樣返回一個文件描述符,應用程序可以像讀寫文件一樣用read/write在網絡上收發數據,如果socket()調用出錯則返回-1。對于IPv4,domain參數指定為AF_INET。對于TCP協議,type參數指定為SOCK_STREAM,表示面向流的傳輸協議。如果是UDP協議,則type參數指定為SOCK_DGRAM,表示面向數據報的傳輸協議。protocol參數的介紹從略,指定為0即可。

2.2 bind( )函數

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> // 包含了這個頭文件,上面兩個就可以省略
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // socket命名

功能:綁定,將文件描述符fd和本地的<IP , 端口>進行綁定

參數:

  • sockfd : 通過socket()函數得到的文件描述符
  • addr : 需要綁定的socket地址,這個地址封裝了ip和端口號的信息
  • addrlen : 第二個參數結構體占的內存大小

返回值:成功返回0,失敗返回-1, 設置errno

服務器程序所監聽的網絡地址和端口號通常是固定不變的,客戶端程序得知服務器程序的地址和端口號后就可以向服務器發起連接,因此服務器需要調用bind綁定一個固定的網絡地址和端口號。

bind()的作用是將參數sockfd和addr綁定在一起,使sockfd這個用于網絡通訊的文件描述符監聽addr所描述的地址和端口號。上一篇講過,struct sockaddr *是一個通用指針類型,addr參數實際上可以接受多種協議的sockaddr結構體,而它們的長度各不相同,所以需要第三個參數addrlen指定結構體的長度。

2.3 listen( )函數

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> // 包含了這個頭文件,上面兩個就可以省略
int listen(int sockfd, int backlog);

功能:監聽這個socket上的連接

參數:

  • sockfd : 通過socket()函數得到的文件描述符
  • backlog : 未連接的和已經連接的和的最大值(排隊建立3次握手隊列和剛剛建立3次握手隊列的鏈接數和的最大值)

典型的服務器程序可以同時服務于多個客戶端,當有客戶端發起連接時,服務器調用的accept()返回并接受這個連接,如果有大量的客戶端發起連接而服務器來不及處理,尚未accept的客戶端就處于連接等待狀態,listen()聲明sockfd處于監聽狀態,并且最多允許有backlog個客戶端處于連接待狀態,如果接收到更多的連接請求就忽略。listen()成功返回0,失敗返回-1。

2.4 accept( )函數

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> // 包含了這個頭文件,上面兩個就可以省略
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

功能:接收客戶端連接,默認是一個阻塞的函數,阻塞等待客戶端連接

參數:

  • sockfd : 用于監聽的文件描述符
  • addr : 傳出參數,記錄了連接成功后客戶端的地址信息(ip,port)
  • addrlen : 傳入傳出參數(值-結果), 指定第二個參數的對應的內存大小,傳出第二個參數傳出時的內存大小

返回值:
成功:返回一個新的socket文件描述符,用于和客戶端通信,失敗返回-1,設置errno

三次握手完成后,服務器調用accept()接受連接,如果服務器調用accept()時還沒有客戶端的連接請求,就阻塞等待直到有客戶端連接上來。addr是一個傳出參數,accept()返回時傳出客戶端的地址和端口號。addrlen參數是一個傳入傳出參數(value-result argument),傳入的是調用者提供的緩沖區addr的長度以避免緩沖區溢出問題,傳出的是客戶端地址結構體的實際長度(有可能沒有占滿調用者提供的緩沖區)。如果給addr參數傳NULL,表示不關心客戶端的地址。

一般來說,我們的服務器程序結構是這樣的:

while (1) {cliaddr_len = sizeof(cliaddr);connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);n = read(connfd, buf, MAXLINE);......close(connfd);
}

整個是一個while死循環,每次循環處理一個客戶端連接。由于cliaddr_len是傳入傳出參數,每次調用accept()之前應該重新賦初值。accept()的參數listenfd是先前的監聽文件描述符,而accept()的返回值是另外一個文件描述符connfd,之后與客戶端之間就通過這個connfd通訊,最后關閉connfd斷開連接,而不關閉listenfd,再次回到循環開頭listenfd仍然用作accept的參數。accept()成功返回一個文件描述符,出錯返回-1。

一定要區分開connfd和listenfd的作用,listenfd僅用于監聽,監聽到了以后并不用它來進行信息傳輸。

2.5 connect( )函數

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> // 包含了這個頭文件,上面兩個就可以省略
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

功能: 客戶端連接服務器
參數:

  • sockfd : 用于通信的文件描述符
  • addr : 客戶端要連接的服務器的地址信息
  • addrlen : 第二個參數的內存大小

返回值:成功 0, 失敗 -1

客戶端需要調用connect()連接服務器,connect和bind的參數形式一致,區別在于bind的參數是自己的地址,而connect的參數是對方的地址。

3. 實現一個簡單的服務端-客戶端通信

接下來使用以上函數實現一個簡單的服務端客戶端通信,實現將客戶端的小寫字母轉換為大寫字母。

3.1 通信流程

在這里插入圖片描述
上圖給了一個C/S模型網絡編程的socket模板。

服務端:

  1. 調用 socket() 建立套接字
  2. 創建 struct sockaddr_in ,并初始化服務端的ip和端口號
  3. 調用 bind() 將第一步建立的套接字與第二步的sockaddr_in(ip和端口號)綁定
  4. 調用 listen() 設置最大同時發起連接數量
  5. 調用 accept() 阻塞等待客戶端發起連接
  6. 調用 read() 讀取客戶端發出的數據
  7. 處理請求
  8. 調用 write() 發送數據給客戶端
  9. 調用 close() 關閉socket偽文件

客戶端:

  1. 調用 socket() 建立套接字
  2. 調用 connect() 向客戶端發起連接
  3. 調用 write() 發送數據給服務端
  4. 調用 read() 讀取服務端返回的數據
  5. 調用 close() 關閉socket偽文件

其中還有很多細節,需要寫代碼的時候才能體會。

3.2 服務端

/*實現一個簡單的服務器-客戶端通信*/#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>// 設定一個服務器端口號
#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666int main()
{int lfd; // 用于監聽的socket的文件描述符,真正用于通信的套接字是接下來accept函數返回的cfd套接字struct sockaddr_in serv_addr;lfd = socket(AF_INET, SOCK_STREAM, 0);serv_addr.sin_family = AF_INET;// 注意這里,要把小端存儲的端口號改為大端存儲serv_addr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); // 調用ip轉換函數,把字符串ip轉化為網絡字節序bind(lfd, (struct sockaddr * )&serv_addr, sizeof(serv_addr));listen(lfd, 128); // 最大連接與待連接數設為128int cfd; // 已連接的客戶端的socket的文件描述符, 以便一會兒read用struct sockaddr_in clie_addr;                // 作為accept的第二個參數,為傳出參數,傳出的是客戶端的sockadd_insocklen_t clie_addr_len = sizeof(clie_addr); // 作為accept的第三個參數,為傳入傳出參數,之所以要單獨定義出來是因為要傳出cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len);while(1) {// 此處輸出連接的客戶端的ip和端口char clie_IP[BUFSIZ];printf("Client IP: %s, client port: %d\n", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)),ntohs(clie_addr.sin_port));char buf[BUFSIZ]; // 給read使用,存儲讀出的數據,BUFSIZ宏是系統用來專門給buf賦長度的宏,為8k(Default buffer size)int len; // read的返回值,是讀入字符的長度len = read(cfd, buf, sizeof(buf));// 把小寫字母轉化為大寫字母int i;for(i = 0; i < len; ++i) {if(buf[i] <= 'z' && buf[i] >= 'a') {buf[i] -= 32;}}write(cfd, buf, len);}close(lfd);close(cfd);return 0;
}

以上代碼為突出主體,沒有寫錯誤判斷與錯誤提示。帶錯誤提示代碼如下:

/*實現一個簡單的服務器-客戶端通信*/#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>// 設定一個服務器端口號
#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666int main()
{int lfd; // 用于監聽的socket的文件描述符struct sockaddr_in serv_addr;int ret; // 用于錯誤檢測lfd = socket(AF_INET, SOCK_STREAM, 0);if(lfd == -1) {perror("socket error");exit(1);}serv_addr.sin_family = AF_INET;// 注意這里,要把小端存儲的端口號改為大端存儲serv_addr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); // 調用ip轉換函數,把字符串ip轉化為網絡字節序ret = bind(lfd, (struct sockaddr * )&serv_addr, sizeof(serv_addr));if(ret == -1) {perror("bind error");exit(1);}ret = listen(lfd, 128); // 最大連接與待連接數設為128if(ret == -1) {perror("listen error");exit(1);}int cfd; // 已連接的客戶端的socket的文件描述符, 以便一會兒read用struct sockaddr_in clie_addr;                // 作為accept的第二個參數,為傳出參數,傳出的是客戶端的sockadd_insocklen_t clie_addr_len = sizeof(clie_addr); // 作為accept的第三個參數,為傳入傳出參數,之所以要單獨定義出來是因為要傳出cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len);if(cfd == -1) {perror("accept error");exit(1);}// 此處輸出連接的客戶端的ip和端口char clie_IP[BUFSIZ];printf("Client IP: %s, client port: %d\n", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)),ntohs(clie_addr.sin_port));while(1) {char buf[BUFSIZ]; // 給read使用,存儲讀出的數據,BUFSIZ宏是系統用來專門給buf賦長度的宏,為8k(Default buffer size)int len; // read的返回值,是讀入字符的長度len = read(cfd, buf, sizeof(buf));if(len == -1) {perror("read error");exit(1);}// 把小寫字母轉化為大寫字母int i;for(i = 0; i < len; ++i) {if(buf[i] <= 'z' && buf[i] >= 'a') {buf[i] -= 32;}}ret = write(cfd, buf, len);if(ret == -1) {perror("write error");exit(1);}}close(cfd);close(lfd);return 0;
}

3.3 客戶端

#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>// 服務器的ip和端口
#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666int main()
{int cfd; // 用于寫入數據傳輸給服務端的socket的文件描述符cfd = socket(AF_INET, SOCK_STREAM, 0);// bind()  可以不調用bind(), linux會隱式地綁定struct sockaddr_in serv_addr; // 因為要連接服務端,這里的sockadd_in是用于指定服務端的ip和端口bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT);inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); // 調用ip轉換函數,把字符串ip轉化為網絡字節序connect(cfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));while(1) {// 從終端讀取內容char buf[BUFSIZ];fgets(buf, sizeof(buf), stdin);  // 讀一行// 寫入到cfd中,傳輸給服務端write(cfd, buf, strlen(buf)); // 注意不要寫成sizeof(buf),sizeof是在內存中所占的大小,strlen是到第一個'\0'位止。// read在讀socket時默認時阻塞的,阻塞等待服務端傳輸數據int len;len = read(cfd, buf, sizeof(buf));printf("%s", buf);}close(cfd);return 0;
}

以上代碼為突出主體,沒有寫錯誤判斷與錯誤提示。帶錯誤提示代碼如下:

#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>// 服務器的ip和端口
#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666int main()
{int ret; // 用于錯誤檢測int cfd; // 用于寫入數據傳輸給服務端的socket的文件描述符cfd = socket(AF_INET, SOCK_STREAM, 0);if(cfd == -1) {perror("socket error");exit(1);}// bind()  可以不調用bind(), linux會隱式地綁定struct sockaddr_in serv_addr; // 因為要連接服務端,這里的sockadd_in是用于指定服務端的ip和端口bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT);inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); // 調用ip轉換函數,把字符串ip轉化為網絡字節序ret = connect(cfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));if(ret == -1) {perror("connect error");exit(1);}// 從終端讀取內容while(1) {char buf[BUFSIZ];fgets(buf, sizeof(buf), stdin);  // 讀一行// 寫入到cfd中,傳輸給服務端ret = write(cfd, buf, strlen(buf)); // 注意不要寫成sizeof(buf),sizeof是在內存中所占的大小,strlen是到第一個'\0'位止。if(ret == -1) {perror("write error");exit(1);}// read在讀socket時默認時阻塞的,阻塞等待服務端傳輸數據int len;len = read(cfd, buf, sizeof(buf));if(len == -1) {perror("read error");exit(1);}printf("%s", buf);}close(cfd);return 0;
}

3.4 程序運行結果及注意事項

先啟動server后啟動client,在client的終端輸入小寫字符后,可見翻譯成了大寫:
在這里插入圖片描述
同時在server端輸出了client的ip和端口號
在這里插入圖片描述
程序有兩個注意事項:

  1. 先啟動服務端,再啟動客戶端。因為先啟動客戶端的話,服務器來不及監聽,客戶端就connect了,會導致connect失敗。
    在這里插入圖片描述
  2. 關閉時先關閉客戶端,再關閉服務端。如果先關閉服務端,服務器程序處于TIME_WAIT狀態,程序中寫死的6666端口號仍然被占用,可以使用netstat -apn | grep 6666查看程序對6666端口占用情況:
    在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/384557.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384557.shtml
英文地址,請注明出處:http://en.pswp.cn/news/384557.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Linux網絡編程學習】使用socket實現簡單服務器——多進程多線程版本

此為牛客Linux C課程和黑馬Linux系統編程筆記。 1. 多進程版 1.1 思路 大體思路與上一篇的單進程版服務器–客戶端類似&#xff0c;都是遵循下圖&#xff1a; 多進程版本有以下幾點需要注意&#xff1a; 由于TCP是點對點連接&#xff0c;服務器主進程連接了一個客戶端以后…

【Linux網絡編程學習】I/O多路復用——select和poll

此為牛客Linux C課程和黑馬Linux系統編程筆記。 0. I/O多路復用 所謂I/O就是對socket提供的內存緩沖區的寫入和讀出。 多路復用就是指程序能同時監聽多個文件描述符。 之前的學習中寫了多進程和多線程版的簡單服務器模型&#xff0c;但是有個問題&#xff1a;每次新來一個客…

【Linux網絡編程學習】I/O多路復用——epoll

此為牛客Linux C課程和黑馬Linux系統編程筆記。 1. 關于epoll epoll是Linux下多路復用IO接口select/poll的增強版本&#xff0c;它能顯著提高程序在大量并發連接中只有少量活躍的情況下的系統CPU利用率&#xff0c;因為它會復用文件描述符集合來傳遞結果而不用迫使開發者每次…

【Linux網絡編程學習】阻塞、非阻塞、同步、異步以及五種I/O模型

文章目錄1. 基本概念1.1 阻塞與非阻塞1.2 同步與異步1.3 為什么沒有“異步阻塞”2. 五種IO模型2.1 阻塞 blocking2.2 非阻塞 non-blocking2.3. IO復用&#xff08;IO multiplexing&#xff09;2.4 信號驅動&#xff08;signal-driven&#xff09;2.5 異步&#xff08;asynchron…

LRU緩存 數據結構設計(C++)

做LeetCode第146題LRU緩存&#xff0c;覺得收獲不小&#xff0c;特此記錄。 請你設計并實現一個滿足 LRU (最近最少使用) 緩存 約束的數據結構。 實現 LRUCache 類&#xff1a; LRUCache(int capacity) 以 正整數 作為容量 capacity 初始化 LRU 緩存。int get(int key) 如果關鍵…

STM32時鐘樹解析

本人之前其實也用STM32做過一些小東西&#xff0c;但因為時鐘的初始化一般是直接在SystemInit時鐘系統初始化函數里直接配置為72MHz&#xff0c;所以對于STM32的時鐘框圖并沒有怎么理會&#xff0c;今天剛好有空就重新看了一下并寫一篇博客記錄一下吧&#xff0c;以免以后又忘了…

S3C2440時鐘體系

S3C2440在默認情況下&#xff0c;整個系統全靠一個12MHz的外部晶振提供頻率來工作運行的&#xff0c;也就是說CPU、內存、UART、ADC等所有需要用到時鐘頻率的硬件都工作在12MHz下&#xff0c;但是通過查閱芯片手冊我們知道CPU時鐘最高可為400MHZ&#xff0c;那么怎么設置時鐘讓…

關于MCU、CPU擴展SDRAM的一個小知識

像上圖這種硬件電路圖上的16個數據位和我們在初始化SDRAM的時候設置的16位數據位寬是指我們讀寫SDRAM的時候可以同時讀寫16個數據位&#xff0c;數據線越多肯定越快&#xff0c;但是數據線也不可能無限增加&#xff0c;我們在程序里是可以讀寫8位&#xff0c;16位&#xff0c;3…

S3C2440擴展SDRAM

本文主要目的是記錄一下S3C2440擴展SDRAM的一些知識&#xff0c;方便以后查閱。 通過查閱手冊我們知道&#xff0c;2440有8個可以用來擴展內存的BANK&#xff0c;其中第6和第7還可用來擴展SDRAM 下面我們來看一下2440擴展SDRAM需要設置哪些寄存器。 一、BWSCON寄存器 該寄存器…

匯編語言的相對跳轉和絕對跳轉以及反匯編代碼解析

上圖第一行的b1 main為相對跳轉&#xff0c;即跳轉到pcoffset,其中pc為當前pc值&#xff0c;offset可以理解為偏移地址&#xff0c;也就是根據當前所在地址加上偏移地址實現跳轉&#xff0c;為相對跳轉。 我們來看看它的反匯編代碼 上圖清除完bss區后使用b1指令跳轉到30000668…

韋東山嵌入式第一期14課第004節_und異常模示程序示例_P筆記

本節課的第一個程序韋老師是想讓大家見識一下未定義異常&#xff0c;而第二個程序是對第一個程序進行改進&#xff0c;防止在某些條件下執行不了&#xff0c;下面就來講一下第2個程序改進了哪些地方并且有什么用。 程序在此路徑中&#xff1a;源碼文檔圖片\源碼\源碼_20180321…

關于NOR FLASH地址左右移的問題

問題引入&#xff1a;不知道你會不會有這樣的疑問&#xff1a;為什么在發送解鎖命令時&#xff0c;我們不用右移一位&#xff0c;而發送扇區地址時卻要右移一位&#xff08;nor_cmd函數內部已經左移一位&#xff09;&#xff0c;這里先補充說明一下說明是cpu角度和nor角度&…

在linux下利用ls命令進行模糊查找

如上圖&#xff0c;我們當前路徑下有三個文件&#xff0c;分別為helloworld.c以及helloworld和1.c&#xff0c;直接輸入命令ls則顯示所有文件&#xff0c;我們可以利用ls 加*的方向進行模糊查找。 輸入ls 目錄名 形式的命令行&#xff0c;則是對該目錄名下的文件全部進行顯示&a…

Makefile常見符號意思

Makefile里有許許多多的符號&#xff0c;對于新手而言如果沒有經常使用&#xff0c;就很容易忘記&#xff0c;所以我把常見符號的意義寫下&#xff0c;方便日后忘記查詢。本文章會持續更新... 1.$&#xff1a;代表目標&#xff1b;$^代表所有依賴&#xff0c;$^代表第一個依賴。…

Linux下串口通信詳解

https://blog.csdn.net/u010783226/article/details/73369097

fstat、stat和lstat 區別

nt fstat(int filedes, struct stat *buf); int stat(const char *path, struct stat *buf); int lstat(const char *path, struct stat *buf); 一眼就能看出來fstat的第一個參數是和另外兩個不一樣的&#xff0c;fstat區別于另外兩個系統調用的地方在于&#xff0c;fstat系…

Linux的幀緩沖設備

Linux的幀緩沖設備 幀緩沖&#xff08;framebuffer&#xff09;是 Linux 為顯示設備提供的一個接口&#xff0c;把顯存抽象后的一種設備&#xff0c;他允許上層應用程序在圖形模式下直接對顯示緩沖區進行讀寫操作。這種操作是抽象的&#xff0c;統一的。用戶不必關心物理顯存的…

Linux下沒有包含頭文件(不知是哪個)導致編譯無法通過的解決心得

最近寫程序的時候編譯出錯了&#xff0c;提示信息為&#xff1a;invalid use of undefined type fb_var_screeninfo。顯示根據英文知道是沒有定義 fb_var_screeninfo這個類型&#xff0c;明顯是缺少了某個頭文件&#xff0c;但是缺少哪個頭文件以及有什么又快又好的解決方法呢&…

gcc編譯缺少數學庫

Linux下編譯出現以下提示可以在編譯的后面加上-lm&#xff0c;例如&#xff0c;arm-none-linux-gnueabi-gcc -o example1 example1.c -lm&#xff0c;意思就是添加數學庫的意思&#xff0c;編譯就能通過了 example1.c:(.text0x3e8): undefined reference to cos example1.c:(.…