Linux網絡編程 | socket介紹、網絡字節序與主機字節序概念與兩者的轉換、TCP/UDP 連接中常用的 socket 接口

文章目錄

  • 套接字
  • socket 地址
    • 通用 socket 地址
    • 專用 socket 地址
  • 網絡字節序與主機字節序
    • 地址轉換
  • TCP/UDP 連接中常用的 socket 接口


套接字

什么是套接字?

所謂 套接字 (Socket) ,就是對網絡中 不同主機 上的應用進程之間進行雙向通信的端點的抽象。

UNIX/Linux下一切皆文件。 socket 就是可讀、可寫、可控、可關的文件描述符。socket 最開始的含義是一個 (IP地址,端口)(IP, port) 。唯一地表示了使用 TCP 通信的一端。

為什么要用到套接字?

我們知道,數據鏈路層、網絡層、傳輸層協議是在內核中實現的。而 socket 就是 操作系統 提供給 應用程序 通過 系統調用 訪問這些 協議服務 的一組 APIsocket 不但可以訪問內核中 TCP/IP 協議棧,而且訪問其他網絡協議棧。

socket 定義的 API 提供哪些功能?

  1. 應用程序數據用戶緩沖區 中復制到 TCP/UDP 內核發送緩沖區 ,將發送數據交付內核。
  2. TCP/UDP 內核接收緩沖區 中復制數據到 用戶緩沖區 ,以讀取數據。
  3. 幫助 應用程序 修改 內核中各層協議的某些頭部信息 或 其他數據結構,從而精確地控制底層通信行為。(如:通過 setsockopt函數 來設置 IP 數據報 在網絡上的存活時間)

socket 的主要 API 都定義在 sys/socket.h 頭文件中。Linux 提供了一套定義在 netdb.h 頭文件中的網絡信息 API ,以實現 主機名IP地址 之間的轉換,以及服務名稱和端口號之間的轉換。


socket 地址

存儲 socket 的地址信息的數據結構有三種:sockaddrsockaddr_insockaddr_un 。我們將 sockaddr 稱為 通用 socket 地址 ,將 后兩者 稱為 專用 socket 地址 。這樣劃分的意義在于:在使用時,我們可以選擇自己所需要的結構,通信時再將我們所使用的結構強轉為 sockaddr ,這樣就能保證數據格式的一致。
在這里插入圖片描述

通用 socket 地址

sockaddr 的定義如下:

#include<bits/socket.h>
struct sockaddr
{sa_family_t 	sa_family; // 地址族類型(sa_family_t)的變量。char 			sa_data[14]; // 存放 socket 地址值。
}

地址族類型通常與協議族類型對應:

協議族地址族描述地址值含義和長度
PF_UNIXAF_UNIXUNIX本地協議族文件的路徑名,長度可達108字節
PF_INETAF_INETTCP/IPv4協議族16 bit 端口號和 32 bit IPv4 地址,共6字節
PF_INET6AF_INET6TCP/IPv6協議族16 bit 端口號,32 bit 流標識,128 bit IPv6 地址,32 bit 范圍ID,共26字節

PF_*AF_* 都定義在 bits/socket.h 頭文件中,且有完全相同的值,因此二者經常混用。

然而,通用的 sockaddr 對于各個協議族而言適用性并不好—— sa_data目標IP地址端口信息 混在一起了。因此,Linux 為各個協議族提供了專門的 socket 地址結構體。


拓展: 由上表易知,14 字節的 sa_data 無法容納多數協議族的地址值。因此,Linux 定義了一個新的通用 socket 地址結構體:

#include<bits/socket.h>
struct sockaddr_storage
{sa_family_t 		safamily;unsigned long int   __ss_align;char 				__ss_padding[128-sizeof(__ss_ag=lign)];
}

這個結構體不僅提供了足夠大的空間用于存放地址值,而且是內存對齊的(這是 __ss_align 成員的作用)。


專用 socket 地址

sockaddr_un 的定義如下:

UNIX 本地域協議族 使用 sockaddr_un 地址結構體:

#include<sys/un.h>
struct sockaddr_un
{sa_family_t sin_family; // 地址族:AF_UNIXchar sun_path[108]; // 文件路徑名
}

sockaddr_in 的定義如下:

TCP/IP協議族IPv4 使用 sockaddr_in 地址結構體:

struct sockaddr_in
{sa_family_t     sin_family; /* 地址族:AF_INET */u_int16_t       sin_port;   /* 端口號,要用網絡字節序表示 */struct in_addr  sin_addr;   /* IPv4地址結構體 */char            sin_zero;   /* 不使用 */
}struct in_addr
{u_int32_t 		s_addr; /* 32位 IPv4 地址,要用網絡字節序表示 */
}

可以清楚看到,該結構體解決了 sockaddr 的缺陷,把 portaddr 分開儲存在兩個變量中。sockaddr_insockaddr 長度一樣,都是 16 個字節,即 占用的內存大小是一致的 ,因此可以互相轉化。二者是并列結構,指向 sockaddr_in 結構的指針也可以指向 sockaddr

sockaddrsockaddr_in 是 Linux網絡編程中最常用的 socket 結構體,sockaddr_in 用于 socket 定義和賦值;sockaddr 用于函數參數。 一般先把 sockaddr_in 變量賦值后,強制類型轉換后傳入 參數為 sockaddr 的函數。

sockaddr_in6 的定義如下:

TCP/IP協議族IPv6 使用 sockaddr_in6 地址結構體:

struct sockaddr_in6
{sa_family_t      sin6_family;  	 /* 地址族:AF_INET6 */u_int16_t        sin6_port;   	 /* 端口號,要用網絡字節序表示 */u_int32_t 		 sin6_flowinfo;  /* 流信息,應設置為0 */struct in6_addr  sin6_addr;   	 /* IPv6地址結構體 */u_int32_t        sin6_scope_id;	 /* scope ID,尚處于實驗階段 */
}struct in6_addr
{unsigned char	 sa_addr[16];    /* 128位 IPv6 地址,要用網絡字節序表示 */
}

網絡字節序與主機字節序

在使用網絡協議的編程中,在兩臺使用不同存儲模式(大端/小端)的主機之間傳遞數據時,往往會產生歧義。解決問題的方法是:

  • 發送端總是把要發送的數據轉化成大端字節序數據后再發送
  • 接收端根據自己的字節序決定是否將傳送過來的數據進行轉換(自身模式為小端則轉換,為大端則不轉換)

因此將 大端字節序 稱為 網絡字節序小端字節序 稱為 主機字節序

Linux 提供了如下 4 個函數來完成 主機字節序網絡字節序 之間的轉換:

#include<netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostlong);
unsigned long int ntotl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);/* h代表主機字節序,n代表網絡字節序,l代表長整型,s代表短整型 */

上述函數中,長整型函數 通常用來轉換 IP地址短整型函數 用來轉換 端口號


地址轉換

記錄日志時,我們習慣用 可讀性好的字符串 來表示 IP地址;編程時,我們往往更需要以 整數(二進制數) 形式表示 IP地址。而這種頻繁切換的需求需要通過函數來滿足:

#include <arpa/inet.h>
in_addr_t inet_addr(const char *strptr); 
// 將點分十進制的 字符串IP地址 轉換為網絡字節序的 整數IP地址 ,失敗時返回INADDR_NONE
char *inet_ntoa(struct in_addr in);
// 將網絡字節序的 整數IP地址 轉換為點分十進制的 字符串IP地址,該函數內部用一個靜態變量存儲轉化結果
// 函數的返回值指向該靜態內存,因此 inet_ntoa 是不可重入的。
int inet_aton(const char *cp, struct in_addr *inp);
// 將點分十進制的 字符串IP地址 轉換為網絡字節序的 整數IP地址(與addr的區別它會認為如255.255.255.255這類特殊地址有效),成功返回1,失敗返回0。
in_addr_t inet_network(const char *cp); 
// 將點分十進制的 字符串IP地址 轉換為主機字節序的 整數IP地址
struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);/* 同時適用于 IPv4 和 IPv6: */
int inet_pton(int af, const char* src, void* dst);
// 將 字符串 表示的IP地址src(用點分十進制表示的IPv4地址或用十六進制字符表示的IPv6地址)轉換成 網絡字節序整數 表示的IP地址
// 轉換結果存在dst指向的內存中。af指定地址族(AF_INET 或 AF_INET6),成功返回1、失敗返回0并設置errno。
const char* inet_ntop(int af, const void* src, char* dst, socklen_t cnt);
// 將 網絡字節序的整數IP地址 轉換為 點分十進制的字符串IP地址,成功返回目標存儲單元的地址、失敗返回NULL并設置errno。
// cnt指定目標存儲單元的大小,通過下面兩個宏來指定大小:
#include<netinet/in.h>
#define INET_ADDRSTRLEN 16 // 用于 IPv4
#define INET6_ADDRSTRLEN 46 // 用于 IPv6 

不可重入的 inet_ntoa 函數

struct in_addr addr1,addr2;
ulong l1,l2;
l1 = inet_addr("1.1.1.1");
l2 = inet_addr("127.0.0.1");
memcpy(&addr1, &l1, 4);
memcpy(&addr2, &l2, 4);
char *cp1 = inet_ntoa(addr1);
char *cp2 = inet_ntoa(addr2);
cout << "address 1: " << cp1 << endl;
cout << "address 2: " << cp2 << endl;

輸出結果:
在這里插入圖片描述
我們會發現,由于 inet_ntoa 的返回值指向一個函數內部的靜態內存,因此最后一次傳入的參數會掩蓋之前的參數。


TCP/UDP 連接中常用的 socket 接口

1. 創建 socket

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol);
domain:底層協議族
type:服務類型
/* 服務類型主要有 */
// SOCK_STREAM服務(流服務),對于 TCP/IP 協議族而言,表示傳輸層使用 TCP 協議。
// SOCK_UGRAM服務(數據報),對于 TCP/IP 協議族而言,表示傳輸層使用 UDP 協議。
/* 拓展 */
// Linux內核版本2.6.17起,type可以是上述兩種類型與下面兩個標志的 與值
// SOCK_NONBLOCK 將新創建的 socket 設置為非阻塞的。
// SOCK_CLOEXEC 用 fork 調用創建子進程時在子進程中關閉該 socket
// 2.6.17版本前,文件描述符的這兩個屬性都需要使用額外的系統調用(如 fcntl)來設置。
protocol:通常是唯一的(前兩個參數已經完全確定了它的值),大部分情況下被設為0,表使用默認協議。返回值:系統調用成功返回一個 socket 文件描述符,失敗返回 -1 并設置 errno。

2. 命名/綁定 socket

創建 socket 時,我們給他指定了地址族,但是 并未指定使用該地址族中哪個具體地址 。 將一個 socket 與具體的地址綁定稱為給 socket 命名。

  • 我們通常要給服務器命名 socket ,因為只有命名后客戶端才知道如何連接服務器。
  • 客戶端不需要命名 socket ,它通常使用操作系統自動分配的 socket 地址。
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr* my_addr, sklen_t addrlen);
// 將 my_addr 所指的 socket 地址分配給未命名的 sockfd 文件描述符,addrlen 指出該地址的長度。
// 成功返回0、失敗返回-1并設置errno。
// 其中兩種常見errno:
// EACCES:被綁定的地址是受保護的地址,僅超級用戶能夠訪問。如:普通用戶將 socket 綁定到知名服務端口(端口號0~1023)。
// EADDRINUSE:被綁定的地址正在使用中。如:將 socket 綁定到一個處于 TIME_WAIT 狀態的 socket 地址。

3. 監聽 socket

socket 被命名之后,還不能立馬接受客戶連接,需要使用系統調用來創建一個 監聽隊列 以存放待處理的客戶連接(同時有 全連接半連接 ):

#include<sys/socket.h>
int listen(int sockfd, int backlog);
// sockfd 指定被監聽的 socket;backlog 提示內核監聽隊列的最大長度(常設為5),超過 backlog+1 則不受理新的客戶連接,客戶端也收到 ECONNREFUSED 錯誤信息。
// Linux內核2.2版本之前,backlog 指的是所有處于 半連接狀態(SYN_RCVD)和 完全鏈接狀態(ESTABISHED)的 socket 總上限。
// 2.2版本之后,它中表示處于 完全連接狀態 的 socket 的上限。半連接狀態 的 socket 上限值由 /proc/sys/ipv4/tcp_max_syn_backlog 內核參數定義。
//  成功返回0;失敗返回-1并設置errno

4. 接受連接

accept 可以從 全連接隊列 中接受一個客戶連接,但 accept 只是從監聽隊列中取出連接,而不關心連接處于何種狀態(ESTABLISHEDCLOSE_WAIT),更不關心任何網絡狀態的變化(取出的客戶端可能掉線了)。

#include <sys/types,h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
// 將 addr 所指的 socket 地址分配給執行過 listen 系統調用的 socket 文件描述符,地址長度為 addrlen。
// 成功時返回一個新的 連接socket,唯一地標識被接受的這個連接,服務器可通過讀寫該 socket 與被接受連接對應的客戶端通信;失敗返回-1并設置errno。

5. 發起連接

服務器 通過 listen 系統調用來 被動 接受連接,客戶端 通過 connect 系統調用來 主動 與服務器建立連接。

#include<sys/types.h>
#include<sys/socket.h>
int connetct(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
// sockfd:socket 系統調用返回的文件描述符。
// serv_addr:處于服務器監聽隊列的客戶端 socket 地址
// 成功返回0,sockfd 唯一標識這個鏈接,客戶端可以通過讀寫 sockfd 與服務器通信;失敗返回-1并設置errno。 
其中兩種常見的errno是:
- ECONNREFUSED:目標端口不存在,連接被拒絕。服務器發送給客戶端一個復位報文段(seq=0),客戶端不必回復復位報文段,應關閉連接或重新連接。
- ETIMEDOUT:連接超時。進行若干次重連,每次重連超時時間都增加一倍。

6.斷開連接

關閉連接就是關閉連接對應的 socker 。 可以通過 close 系統調用來關閉文件描述符。

#include<unistd.h>
int close(int fd);
// fd:待關閉的socket

值得一提的是, close 并非立即關閉一個鏈接,而是將 fd 的引用計數減 1 。當 fd 的引用計數為 0 時,才真正關閉連接。多進程程序中,一次 fork 系統調用默認將使父進程中打開的 socket 的引用計數加 1 ,因此必須在 父子進程中都 對該 socket 執行 close 調用才能關閉連接。

如果無論如何都要立刻終止連接,可以使用 shutdown 系統調用:

#include<sys/socket.h> // 從隸屬頭文件可以看出,它是專門為網絡編程設計的
int shutdown(int sockfd, int howto);
// sockfd:待關閉的 socket。
// howto:shutdown 的行為。
// 成功返回0,失敗返回-1并設置errno。

howto 可選值有:

可選值含義
SHUT_RD關閉 sockfd 上讀的這一半。應用程序不能再執行讀操作,并且該 socket 接收緩沖區中的數據都被丟棄。
SHUT_WR關閉 sockfd 上寫的這一半。sockfd 的發送緩沖區中的數據會在真正關閉連接之前全部發送出去,應用程序不可再執行寫操作。這種情況下,連接處于半關閉狀態。
SHUT_RDWR同時關閉 sockfd 上的讀和寫

closeshutdown 最大的不同是,close 關閉連接時只能將 socket 上的讀和寫同時關閉,而 shutdown 可以分別(或同時)關閉。


7. 數據讀寫

對文件的讀寫 writeread 同樣適用于 socke ,但 socket 還有專門用于 socket數據讀寫 的系統調用:

/* 用于TCP流數據讀寫的系統調用 */
#include<sys/types>
#include<sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
// recv 讀 sockfd 上的數據,send 往 sockfd 上寫數據。
// buf 和 len 分別指定緩沖區的位置和大小。
// flags 為數據收發提供額外控制,通常為0。
// 成功時返回讀/寫的數據長度,失敗返回-1并設置errno。
// recv 成功時返回的長度可能小于 len,因此可能要多次調用 recv 以讀取完整數據;recv 返回 0 意味著 通信對方已經關閉連接。/* UDP數據讀寫 */
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
// buf 和 len 參數分別指定讀/寫緩沖區的位置和大小
// 由于 UDP 通信沒有連接的概念,所以我們每次讀取數據都需要獲取發送端的 socket 地址,即 src_addr 所指向的內容。
// dest_addr 指定接收端的 socket 地址。
/* 上述兩個系統調用也可以用于面向連接(STREAM)的 socket 的數據讀寫,只需將最后兩個參數都設置為 NULL *//* 兼容 TCP流數據 和 UDP數據報 的數據讀寫 */
#include<sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);struct msghdr
{void* msg_name;				// socket 地址,對于TCP協議,需設置為NULL。socklen_t msg_namelen;		// socket 地址的長度struct iovec* msg_iov;		// 分散內存塊,詳情見下文。int msg_iovlen;				// 分散內存塊的數量void* msg_control;			// 指向輔助數據的起始位置socklen_t msg_controllen;	// 輔助數據的大小int msg_flags;				// 復制函數中的 flags 參數,recvmsg 還會在調用過程中將某些更新后的標志設置到 msg_flags 中。
}struct iovec
{void *iov_base; // 內存起始地址size_t iov_len; // 這塊內存長度
}
對于 recvmsg 而言,數據將被讀取并存放在 msg_iovlen 塊分散的內存中,這種操作被稱為 分散讀(scatter read)。
對于 sendmsg 而言,分散內存中的數據將被一并發送,這稱為 集中寫(gather write)。

flags 可選值:

選項名含義sendrecv
MSG_CONFIRM指示數據鏈路層持續監聽對方的回應,直到得到答復。僅能用于 SOCK_DGRAM 和 SOCK_RAW 類型的 socket。YN
MSG_DONTROUTE不查看路由表,直接將數據發送給本地局域網絡內的主機。用于發送者確定目標主機就在本網絡。YN
MSG_DONTWAIT對 socket 的此次操作將是非阻塞的YY
MSG_MORE告訴內核應用程序還有更多數據要發送,內核將 超時等待 新數據寫入 TCP 發送緩沖區后一并發送。防止 TCP 發送過多小報文段,提高傳輸效率。YN
MSG_WAITALL讀操作僅在讀取到指定數量的字節后才返回NY
MSG_PEEK窺探讀緩存中的數據,本次讀操作不會清除這些數據。NY
MSG_OOB發送或接收緊急數據(帶外數據)YY
MSG_NOSIGNAL往讀端關閉的管道或 socket 連接中讀寫數據時不引發 SIGPIPE 信號YN

flags 參數只對 sendrecv 的當前調用生效,而后面講到的 setsockopt 系統調用會永久地修改 socket 某些屬性。


7.1 帶外數據

實際應用中,通常無法預知 帶外數據 何時到來,幸運的是 Linux 內核檢測到 TCP 緊急標志(URG)時,將通知應用程序有帶外數據需要接受。通知方法有兩種:

  • I/O復用 產生的異常事件
  • SIGURG 信號

但應用程序接到通知后也只知道有帶外數據要來,解決的時間的不確定性,但仍不知道帶外數據在數據流中的具體位置。想要知道具體位置可以通過如下系統調用:

#include<sys/socket.h>
int sockatmark(int fockfd);

sockatmark 判斷 sockfd 是否處于帶外標記,即下一個被讀取到的數據是否是帶外數據。若是返回 1 ,此時就可以用帶 MSG_OBB 標志的 recv 調用來接收帶外數據;若不是返回 0


8. 地址信息函數

我們可以知道一個連接 socket 的 本端socket地址 ,以及 遠端socket地址

#include<sys/socket.h>
int getsockname(int sockfd, struct sockaddr* address, socklen_t* address_len); // 獲取 sockfd 對應的socket地址
int getpeername(int sockfd, struct sockaddr* address, socklen_t* address_len); // 遠端socket地址
// 將獲得的地址存于 address 指定的內存中,地址長度存于 address_len 中。
// 如果實際 socket 地址長度大于 address 所指內存區的大小,那么該 socket地址 將被截斷。
// 成功返回0,失敗返回-1并設置errno。

9. 總結

用一張圖總結 TCP三次握手 過程中對 socket接口 的使用。
在這里插入圖片描述

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

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

相關文章

網絡協議分析 | 傳輸層 :史上最全UDP、TCP協議詳解,一篇通~

文章目錄UDP概念格式UDP如何實現可靠傳輸基于UDP的應用層知名協議TCP概念格式保證TCP可靠性的八種機制確認應答、延時應答與捎帶應答超時重傳滑動窗口滑動窗口協議后退n協議選擇重傳協議流量控制擁塞控制發送窗口、接收窗口、擁塞窗口快速重傳和快速恢復連接管理機制三次握手連…

JDom,jdom解析xml文件

1.要解析的文件模板如下&#xff1a; <?xml version"1.0" encoding"GBK"?> <crsc> <data><舉報信息反饋><R index"1"><舉報編號>1</舉報編號><狀態>1</狀態><答復意見>填寫…

網絡協議分析 | 應用層:HTTP協議詳解、HTTP代理服務器

文章目錄概念URLHTTP協議的特點HTTP協議版本格式請求報文首行頭部空行正文響應報文首行頭部空行正文Cookie與SessionHTTP代理服務器正向代理服務器反向代理服務器透明代理服務器概念 先了解一下 因特網&#xff08;Internet&#xff09; 與 萬維網&#xff08;World Wide Web&…

MySQL命令(一)| 數據類型、常用命令一覽、庫的操作、表的操作

文章目錄數據類型數值類型字符串類型日期/時間類型常用命令一覽庫的操作顯示當前數據庫創建數據庫使用數據庫刪除數據庫表的操作創建表顯示當前庫中所有表查看表結構刪除表數據類型 mysql 的數據類型主要分為 數值類型、日期/時間類型、字符串類型 三種。 數值類型 數值類型可…

C++ 繼承 | 對象切割、菱形繼承、虛繼承、對象組合

文章目錄繼承繼承的概念繼承方式及權限using改變成員的訪問權限基類與派生類的賦值轉換回避虛函數機制派生類的默認成員函數友元與靜態成員多繼承菱形繼承虛繼承組合繼承 繼承的概念 繼承可以使得子類具有父類的屬性和方法或者重新定義、追加屬性和方法等。 當創建一個類時&…

博弈論 | 博弈論簡談、常見的博弈定律、巴什博弈

文章目錄博弈論什么是博弈論&#xff1f;博弈的前提博弈的要素博弈的分類非合作博弈——有限兩人博弈囚徒困境合作博弈——無限多人博弈囚徒困境常見的博弈定律零和博弈重復博弈智豬博弈斗雞博弈獵鹿博弈蜈蚣博弈酒吧博弈槍手博弈警匪博弈海盜分金巴什博弈博弈論 什么是博弈論…

MySQL命令(二)| 表的增刪查改、聚合函數(復合函數)、聯合查詢

文章目錄新增 (Create)全列插入指定列插入查詢 (Retrieve)全列查詢指定列查詢條件查詢關系元素運算符模糊查詢分頁查詢去重&#xff1a;DISTINCT別名&#xff1a;AS升序 or 降序更新 (Update)刪除 (Delete)分組&#xff08;GROUP BY&#xff09;聯合查詢內連接&#xff08;inne…

Spring3.1+Quertz1.8實現多個計劃任務

1.主要是配置文件&#xff1a;如下&#xff1a;(這里說明一下主要是看紅色部分的配置&#xff0c;其他的可以根據自己的實際情況修改&#xff0c;這里只是個思路。) <?xml version"1.0"?> <beans xmlns"http://www.springframework.org/schema/beans…

MySQL | 數據庫的六種約束、表的關系、三大范式

文章目錄數據庫約束NOT NULL&#xff08;非空約束&#xff09;UNIQUE&#xff08;唯一約束&#xff09;DEFAULT&#xff08;缺省約束&#xff09;PRIMARY KEY&#xff08;主鍵約束&#xff09;AUTO_INCREMENT 自增FOREIGN KEY&#xff08;外鍵約束&#xff09;CHECK&#xff08…

哈希 :哈希沖突、負載因子、哈希函數、哈希表、哈希桶

文章目錄哈希哈希&#xff08;散列&#xff09;函數常見的哈希函數字符串哈希函數哈希沖突閉散列&#xff08;開放地址法&#xff09;開散列&#xff08;鏈地址法/拉鏈法&#xff09;負載因子以及增容對于閉散列對于開散列結構具體實現哈希表&#xff08;閉散列&#xff09;創建…

C++ 泛型編程(一):模板基礎:函數模板、類模板、模板推演成函數的機制、模板實例化、模板匹配規則

文章目錄泛型編程函數模板函數模板實例化隱式實例化顯式實例化函數模板的匹配規則類模板類模板的實例化泛型編程 泛型編程旨在削減重復工作&#xff0c;如&#xff1a; 將一個函數多次重載不如將他寫成泛型。 void Swap(int& left, int& right) {int temp left;lef…

你真的了解靜態變量、常量的存儲位置嗎?

文章目錄引言C對內存的劃分如何落實在Linux上自由存儲區和堆之間的問題棧常量區靜態存儲區靜態局部變量靜態局部變量、靜態全局變量、全局變量的異同macOS系統的測試結果總結引言 在動態內存的博客中&#xff0c;我提到&#xff1a; 在Linux 內存管理的博客中&#xff0c;我提…

C++ 泛型編程(二):非類型模板參數,模板特化,模板的分離編譯

文章目錄非類型模板參數函數模板的特化類模板的特化全特化偏特化部分參數特化參數修飾特化模板分離編譯解決方法非類型模板參數 模板的參數分為兩種&#xff1a; 類型參數&#xff1a; 則是我們通常使用的方式&#xff0c;就是在模板的參數列表中在 class 后面加上參數的類型…

Java操作——獲取文件擴展名,去掉文件擴展名

昨天收郵件&#xff0c;得知要參加一個產品部的會議&#xff0c;猜想&#xff0c;也許是因為我做的這個產品demo問題。于是昨天忙活到凌晨3點半&#xff0c;結果早上一來才知道又被調戲了。發郵件的MM把郵件誤發給我了。悲催啊有木有&#xff0c;困啊有木有&#xff01;自己還是…

數據結構 | B樹、B+樹、B*樹

文章目錄搜索結構B樹B樹的插入B樹的遍歷B樹的性能B樹B樹的插入B樹的遍歷B*樹B*樹的插入總結搜索結構 如果我們有大量的數據需要永久存儲&#xff0c;就需要存儲到硬盤之中。但是硬盤的訪問速度遠遠小于內存&#xff0c;并且由于數據量過大&#xff0c;無法一次性加載到內存中。…

MySQL 索引 :哈希索引、B+樹索引、全文索引

文章目錄索引引言常見的索引哈希索引自適應哈希索引B樹索引聚集索引非聚集索引使用方法聯合索引最左前綴匹配規則覆蓋索引全文索引使用方法索引 引言 為什么需要索引&#xff1f; 倘若不使用索引&#xff0c;查找數據時&#xff0c;MySQL必須遍歷整個表。而表越大&#xff0c;…

服裝店怎么引流和吸引顧客 服裝店鋪收銀系統來配合

實體店的同城引流和經營是實體經濟的一個重要的一環&#xff0c;今天我們來分享服裝行業的實體店鋪怎么引流和吸引、留住顧客&#xff0c;并實現復購。大家點個收藏&#xff0c;不然劃走就再也找不到了&#xff0c;另外可以點個關注&#xff0c;下次有新的更好的招&#xff0c;…

約瑟夫環(丟手絹問題)

文章目錄問題描述思路代碼實現問題描述 有 1~N 個數字&#xff0c;從 1~m 依次報數&#xff0c;數到 m 的數字要被刪掉&#xff0c;求最后剩下的數字是&#xff1f; 思路 第一次報數第二次報數1n-m12n-m2……m-2n-2m-1n-1m被刪掉了m11m22……n-1n-1-mnn-m 通過上面的表格&…

MySQL 鎖的相關知識 | lock與latch、鎖的類型、簡談MVCC、鎖算法、死鎖、鎖升級

文章目錄lock與latch鎖的類型MVCC一致性非鎖定讀&#xff08;快照讀&#xff09;一致性鎖定讀&#xff08;當前讀&#xff09;鎖算法死鎖鎖升級lock與latch 在了解數據庫鎖之前&#xff0c;首先就要區分開 lock 和 latch。在數據庫中&#xff0c;lock 和 latch 雖然都是鎖&…

Hibernate使用原生SQL適應復雜數據查詢

HQL盡管容易使用&#xff0c;但是在一些復雜的數據操作上功能有限。特別是在實現復雜的報表統計與計算&#xff0c;以及多表連接查詢上往往無能為力&#xff0c;這時可以使用SQL&#xff08;Native SQL&#xff09;實現HQL無法完成的任務。 1、使用SQL查詢 使用SQL查詢可以通過…