套接字概念

Socket本身有「插座」的意思,在Linux環境下,用於表示進程間網路通信的特殊文件類型。本質為內核藉助緩衝區形成的偽文件。既然是文件,那麼理所當然的,我們可以使用文件描述符引用套接字。與管道類似的,Linux系統將其封裝成文件的目的是為了統一介面,使得讀寫套接字和讀寫文件的操作一致。區別是管道主要應用於本地進程間通信,而套接字多應用於網路進程間數據的傳遞。套接字的內核實現較為複雜,不宜在學習初期深入學習。在TCP/IP協議中,「IP地址+TCP或UDP埠號」唯一標識網路通訊中的一個進程。「IP地址+埠號」就對應一個socket。欲建立連接的兩個進程各自有一個socket來標識,那麼這兩個socket組成的socket pair就唯一標識一個連接。因此可以用Socket來描述網路連接的一對一關係。

套接字通信原理如下圖所示:

套接字通訊原理示意在網路通信中,套接字一定是成對出現的。一端的發送緩衝區對應對端的接收緩衝區。我們使用同一個文件描述符索發送緩衝區和接收緩衝區。TCP/IP協議最早在BSD UNIX上實現,為TCP/IP協議設計的應用層編程介面稱為socket API。現在的主要內容是socket API,主要介紹TCP協議的函數介面,最後介紹UDP協議和UNIX Domain Socket的函數介面。

網路編程介面

1、什麼是計算機網路?

多個計算機進行通信--->計算機網路。2、計算機通信的複雜度 (1)、傳輸信息的複雜度(種類、內容); (2)、信息的數量 (3)、傳輸距離(幹擾...) (4)、信息的安全問題 (5)、計算機體系的完整性和封閉性。 既要保證計算機的封閉性,又要達成計算機的通信。3、ip地址

(1)、IP地址是有限的,需要一種方式將IP地址復用。

(2)、IP地址的復用導致了數據傳遞的複雜性(ATM,存儲轉發機制;路由機制)。 (3)、IP地址過於抽象不方便使用,於是給出了IP地址的人文化轉義:域名。 (4)、域名只能代表一個IP網路地址,於是就只能代表一個網路上的節點實體。 (5)、實際上訪問節點的時候,本質上使用的是IP地址,所以就需要將域名轉化為IP地址(DNS...)4、IP地址的分類 (1)、IPv4地址是4位元組的,中間以 . 劃分;IPv6地址是16位元組的; 規定:在IP地址劃分上,一般不取全0/全1; IP一般分為5類; A、B、C、D、E,一般常用的IP地址為A類,B類,C類;

A類IP:第一個位元組是以0開頭 0000 0000--->0111 1111 0~127B類IP:第一個位元組是以10開頭 1000 0000--->1011 1111 128~191 C類IP:第一個位元組是以110開頭 1100 0000--->1101 1111 192~223 (2)、子網掩碼:就是將網路號設置為1,主機號設置為0(對每一個位元組的位進行設置); 例:C類IP地址,3個位元組網路號和一個位元組的主機號; 1111 1111 1111 1111 1111 1111 0000 0000 255 . 255 . 255 . 0 (3)、子網劃分:此時就存在C類地址的子網掩碼不一定總是255.255.255.0;

這還的看C類IP下面有沒有子網劃分,

有子網劃分的話,最後一個位元組,也就是主機號可能為2段(01/10)、4段(00/01/10/11) 例:192.168.3.11xx xxxx 1111 1111 1111 1111 1111 1111 1100 0000 此時對應的子網掩碼為:255.255.255.192 (4)、IOS和TCP/IP模型分析

(5)、埠號 port:唯一標識應用程序的編號;

我們之間通過QQ、微信、郵箱進行收發數據時,沒有導致數據的錯亂接收,是怎麼做到的呢?又是怎麼一一對應找到的呢?

:通過埠號,識別了電腦上的某一應用程序,也就是找對應的編號。 我們在進行數據的發送時:首先通過IP尋找物理計算機,在根據port,尋找對應的應用程序。 (6)、TCP和UDP UDP屬於TCP/IP體系中的一部分。 TCP協議和UDP協議都屬於傳輸層協議。 TCP協議: i>、面向連接的傳輸協議、可靠的、同步的; ii>、面向連接的網路傳輸特點:a、需要有一方主動的建立連接,另一方接收連接請求;b、只有建立了連接之後纔能夠進行數據的傳輸;c、當數據傳輸完畢之後,就需要釋放連接,由連接的兩端來共同決定連接是否保持。 UDP協議:

a、面向無連接的:即就是在進行數據傳輸的時候,不需要預先創建一個連接;

b、不可靠的:無法知道發送的數據是否能夠到達目的,也無法知道什麼時候能夠到達目的。 c、非同步的: (7)、TCP的三次握手、四次揮手 TCP------->至少3次握手(最後一次防止誤按,2次的話,有可能死鎖); : 打電話模型 模型分析

TCP-------->4次揮手。 模型:男女朋友分手模型

通過IP,只能保證物理上的連通,至於收發數據的形式是什麼,都不歸它管。 127.0.0.1:本機回送地址,可作為測試本機使用,不安裝網卡也是可以ping通的。5、TCP的編程實現 基礎的socket編程對TCP的就是下面的步驟:(1)、模型分析

(2)、代碼實現utili.h

#include<stdio.h>

#include<unistd.h>#include<string.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#define SERVER_PORT 9090#define SERVER_IP "127.0.0.1"#define LISTEN_QUEUE 5#define BUF_SIZE 255伺服器端代碼:#include"utili.h"//TCPint main(void){ int sockSer = socket(AF_INET, SOCK_STREAM, 0); if(sockSer == -1){ perror("socket"); return -1; } struct sockaddr_in addrSer, addrCli; addrSer.sin_family = AF_INET; addrSer.sin_port = htons(SERVER_PORT); addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); int yes = 1; setsockopt(sockSer, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); //地址、埠的重用 socklen_t len = sizeof(struct sockaddr); int res = bind(sockSer, (struct sockaddr *)&addrSer, len); if(res == -1) { perror("bind"); close(sockSer); return -1; } res = listen(sockSer, LISTEN_QUEUE); if(res == -1) { perror("listen"); close(sockSer); return -1; } int sockConn; char sendbuf[BUF_SIZE]; char recvbuf[BUF_SIZE]; while(1) { sockConn = accept(sockSer, (struct sockaddr *)&addrCli, &len); if(sockConn == -1){ continue; } else { printf("Server Accept Client Connect OK
"); } printf("Ser :>"); scanf("%s", sendbuf); if(strncmp(sendbuf, "quit", 4) == 0) break; send(sockConn, sendbuf, strlen(sendbuf)+1, 0); recv(sockConn, recvbuf, BUF_SIZE, 0); printf("Cli :>%s
", recvbuf); } close(sockSer); return 0;}客戶端代碼:#include"utili.h"//TCPint main(void){ int sockCli = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addrSer; addrSer.sin_family = AF_INET; addrSer.sin_port = htons(SERVER_PORT); addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); struct sockaddr_in addrCli; addrCli.sin_family = AF_INET; addrCli.sin_port = htons(7070); addrCli.sin_addr.s_addr = inet_addr("192.168.1.155"); int yes = 1; setsockopt(sockCli, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); //地址、埠的重用 socklen_t len = sizeof(struct sockaddr); int res = bind(sockCli, (struct sockaddr *)&addrCli, len); if(res == -1) { perror("bind"); close(sockCli); return -1; } res = connect(sockCli, (struct sockaddr*)&addrSer, len); if(res == -1) { perror("connect"); close(sockCli); return -1; } else { printf("Client Connect Server ok
"); } char sendbuf[BUF_SIZE]; char recvbuf[BUF_SIZE]; while(1) { connect(sockCli, (struct sockaddr*)&addrSer, len); recv(sockCli, recvbuf, BUF_SIZE, 0); printf("Ser :>%s
", recvbuf); printf("Cli :>"); scanf("%s", sendbuf); if(strncmp(sendbuf, "quit", 4) == 0) break; send(sockCli, sendbuf, strlen(sendbuf)+1, 0); } close(sockCli); return 0;}(3)、運行結果

6、UDP的編程實現 基礎的socket編程對UDP的就是下面的步驟:(1)、模型分析

(2)、代碼實現utili.h#include<unistd.h>#include<stdio.h>#include<string.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#define SERVER_PORT 9090#define SERVER_IP "127.0.0.1"#define LISTEN_QUEUE 5#define BUFFER_SIZE 255伺服器端代碼:#include"utili.h"int main(){ int sockSer = socket(AF_INET, SOCK_DGRAM, 0); if(sockSer == -1) { perror("socket"); return -1; } struct sockaddr_in addrSer, addrCli; addrSer.sin_family = AF_INET; addrSer.sin_port = htons(SERVER_PORT); addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); socklen_t len = sizeof(struct sockaddr); int res = bind(sockSer, (struct sockaddr*)&addrSer, len); if(res == -1) { perror("bind"); close(sockSer); return -1; } char sendbuf[BUFFER_SIZE]; char recvbuf[BUFFER_SIZE]; while(1) { recvfrom(sockSer, recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&addrCli, &len); printf("Cli:>%s
",recvbuf); printf("Ser:>"); scanf("%s",sendbuf); if(strncmp(sendbuf, "quit", 4) == 0){ break; } sendto(sockSer, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr*)&addrCli, len); } close(sockSer); return 0;}客戶端代碼:#include"utili.h"int main(){ int sockCli = socket(AF_INET, SOCK_DGRAM, 0); if(sockCli == -1){ perror("socket"); return -1; } struct sockaddr_in addrSer; addrSer.sin_family = AF_INET; addrSer.sin_port = htons(SERVER_PORT); addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); char sendbuf[BUFFER_SIZE]; char recvbuf[BUFFER_SIZE]; socklen_t len = sizeof(struct sockaddr); while(1){ printf("Cli:>"); scanf("%s",sendbuf); if(strncmp(sendbuf, "quit", 4) == 0){ break; } sendto(sockCli, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr*)&addrSer, len); recvfrom(sockCli, recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&addrSer, &len); printf("Ser:>%s
",recvbuf); } close(sockCli); return 0; }(3)、運行結果伺服器端截圖

客戶端截圖

服務端的套接字總領全局,不與任何客戶端進行通信,為每一個新的客戶端所分配一個新的套接字,進行通信。 LISTEN_QUEUE:等待隊列的大小(最多等待多少隊列); UDP:必須先知道伺服器在哪。喜歡本文的朋友們,歡迎長按下圖關注訂閱號編程小兔崽,收看更多精彩內容

weixin.qq.com/r/NByrsyH (二維碼自動識別)

每天進步一點點,如果有用給小編點個贊


推薦閱讀:
相關文章