(4.1)C/S, TCP/IP協議

一:客戶端與服務器

	//客戶端:client,一般字母c表示
	//服務器:server,一般字母s表示 ,所以c/s一般就是 :客戶端/服務器
	//客戶端:就是一個程序,
	//服務器:也是 一個程序;
	//(1.1)解析一個瀏覽器訪問網頁的過程

	//(1.2)客戶端服務器角色規律總結
	//a)數據通訊總在兩端進行,其中一端叫客戶端,另一端叫服務器端;
	//b)總有一方先泛起第一個數據包,這發起第一個數據包的這一端,就叫客戶端【瀏覽器】;被動收到第一個數據包這端,叫服務器端【淘寶服務器】;
	//c)連接建立起來,數據雙向流動,這叫 雙工【你可以發數據包給我,我也可以發數據包給你】
	//d)既然服務器端是被動接收連接,那麼客戶端必須得能夠找到服務器在哪裏;
	//我瀏覽器要訪問淘寶網,我需要知道淘寶服務器的地址【ip地址:192.168.1.100 三個點分隔四個數】,
	      //以及淘寶服務器的姓名【端口號,這是 一個無符號數字,範圍 0-65535之間的一個數字】
	// 淘寶網服務器【nginx服務器】會調用listen()函數來監聽80端口;
	//在編寫網絡通訊程序時,你只需要指定淘寶服務器的ip地址和淘寶服務器的端口號,就能夠跟淘寶服務器進行通訊;
	//e)epoll

二:網絡模型

(2.1)OSI七層網絡模型:

    //物【物理層】 鏈【數據鏈路層】 網【網絡層】 傳【傳輸層】 會【會話層】 表【表示層】 應【應用層】

    //OSI(Open System Interconnect):開放式系統互聯;是ISO(國際標準化組織)在1985年研究的網絡互聯模型;

    //把一個要發送出去的數據包從裏到外裹了7層,就跟 一個人一樣,穿了7件衣服,一件套一件 ;最終把包裹了7層的數據包發送都網絡上去了;

(2.2)TCP / IP協議四層模型

    //Transfer Control Protocol[傳輸控制協議]/Internet Protocol[網際協議];

    //tcp/ip實際是 一組 協議的代名詞,而不僅僅是一個協議;

    //tcp/ip協議,其實每一層都對應着一些協議;

(2.3)TCP / IP協議的解釋和比喻

    //我們把人看成 要發送出去的數據包;人出門上街 ,我們把外邊的街道,就看成網絡,我們人出門上街,就等於把數據包發送到互聯網是上去;

    // 人 <=======> 數據包

    //街道 <=======> 互聯網

    //人上街 <=======> 數據包發送到互聯網上

 

    //人不能光腚上街,人要先穿內衣內褲【TCP】;套一個襯衣襯褲【IP】,套個外衣外褲【以太網幀】,可以出門了;

    // TCP 比喻成了 內衣內褲

    // IP 比喻成了 襯衣襯褲

    // 以太網幀 比喻成了 外衣外褲

 

    //你要發送 abc 這三個字母出去到網絡上;

     //加個tcp頭【abc套了個內衣內褲】

     //加個IP頭【abc套了個襯衣襯褲】

     //加個以太網幀頭/尾【abc套了個外衣外褲】

    //加了這三個頭一個尾之後,就認爲這個數據包符合了TCP/IP協議,這個數據包能夠被髮送到網絡上去了【人穿好了衣服可以出門了】;

 

 三:最簡單的客戶端和服務器程序實現代碼

    //a)客戶端程序,服務器端程序;只具備演示價值,不具備商業價值。

    //b)最終epoll技術實現商用的服務器程序;

    //c)《Unix網絡編程》第一卷;

 nginx_server.c


#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>

#define SERV_PORT 9000  //本服務器要監聽的端口號,一般1024以下的端口很多都是屬於周知端口,所以我們一般採用1024之後的數字做端口號

int main(int argc, char *const *argv)
{    
    //這些演示代碼的寫法都是固定套路,一般都這麼寫

    //服務器的socket套接字【文件描述符】
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);    //創建服務器的socket,大家可以暫時不用管這裏的參數是什麼,知道這個函數大概做什麼就行

    struct sockaddr_in serv_addr;                  //服務器的地址結構體
    memset(&serv_addr,0,sizeof(serv_addr));
    
    //設置本服務器要監聽的地址和端口,這樣客戶端才能連接到該地址和端口併發送數據
    serv_addr.sin_family = AF_INET;                //選擇協議族爲IPV4
    serv_addr.sin_port = htons(SERV_PORT);         //綁定我們自定義的端口號,客戶端程序和我們服務器程序通訊時,就要往這個端口連接和傳送數據
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //監聽本地所有的IP地址;INADDR_ANY表示的是一個服務器上所有的網卡(服務器可能不止一個網卡)多個本地ip地址都進行綁定端口號,進行偵聽。

    bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));//綁定服務器地址結構體
    listen(listenfd, 32);     //參數2表示服務器可以積壓的未處理完的連入請求總個數,客戶端來一個未連入的請求,請求數+1,連入請求完成,c/s之間進入正常通訊後,請求數-1

    int connfd;
    const char *pcontent = "I sent sth to client!"; //指向常量字符串區的指針
    for(;;)
    {
        //卡在這裏,等客戶單連接,客戶端連入後,該函數走下去【注意這裏返回的是一個新的socket——connfd,後續本服務器就用connfd和客戶端之間收發數據,而原有的lisenfd依舊用於繼續監聽其他連接】        
        connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);

        //發送數據包給客戶端
        write(connfd,pcontent,strlen(pcontent)); //注意第一個參數是accept返回的connfd套接字
        
        //只給客戶端發送一個信息,然後直接關閉套接字連接;
        close(connfd); 
    } //end for
    close(listenfd);     //實際本簡單範例走不到這裏,這句暫時看起來沒啥用
    return 0;
}

 nginx_client.c

#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>


#define SERV_PORT 9000    //要連接到的服務器端口,服務器必須在這個端口上listen着

int main(int argc, char *const *argv)
{    
    //這些演示代碼的寫法都是固定套路,一般都這麼寫
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); //創建客戶端的socket,大家可以暫時不用管這裏的參數是什麼,知道這個函數大概做什麼就行

    struct sockaddr_in serv_addr; 
    memset(&serv_addr,0,sizeof(serv_addr));

    //設置要連接到的服務器的信息
    serv_addr.sin_family = AF_INET;                //選擇協議族爲IPV4
    serv_addr.sin_port = htons(SERV_PORT);         //連接到的服務器端口,服務器監聽這個地址
    //這裏爲了方便演示,要連接的服務器地址固定寫
    if(inet_pton(AF_INET,"192.168.1.126",&serv_addr.sin_addr) <= 0)  //IP地址轉換函數,把第二個參數對應的ip地址轉換第三個參數裏邊去,固定寫法
    {
        printf("調用inet_pton()失敗,退出!\n");
        exit(1);
    }

    //連接到服務器
    if(connect(sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0)
    {
        printf("調用connect()失敗,退出!\n");
        exit(1);
    }

    int n;
    char recvline[1000 + 1]; 
    while(( n = read(sockfd,recvline,1000)) > 0) //僅供演示,非商用,所以不檢查收到的寬度,實際商業代碼,不可以這麼寫
    {
        recvline[n] = 0; //實際商業代碼要判斷是否收取完畢等等,所以這個代碼只有學習價值,並無商業價值
        printf("收到的內容爲:%s\n",recvline);
    }
    close(sockfd); //關閉套接字
    printf("程序執行完畢,退出!\n");
    return 0;
}

    //(3.1)套接字socket概念

    //套接字(socket):就是個數字,通過調用socket()函數來生成;這個數字具有唯一性;一直給你用,直到你調用close()函數把這個數字關閉;

    //文件描述符;一切皆文件,咱們就把socket也看成是文件描述符,我們可以用socket來收發數據;send(),recv();

 

    //(3.2)一個簡單的服務器端通訊程序範例【看調用了哪些函數:面試官可能 會考】

 

    //(3.3)IP地址簡單談

    //192.168.1.100[IVP4]:理解成現實社會中的居住地址

    //192.168.1.100[IVP4]:第四個版本的IP地址格式;

    //發展處了新的IP地址版本【第六版】,IPV6

    //我們寫通訊程序代碼時是否需要根據ipv4,ipv6來調整呢?

    //a)寫服務器程序,不用考慮ipv4,ipv6的問題,遵照ipv4規則寫就行;

    //b)寫客戶端程序,只演示ipv4版本的客戶端範例。

    //後續寫項目老師會帶着大家寫同時兼容ipv4,ipv6【協議無關】客戶端程序;

 

    //(3.4)一個簡單的客戶端通訊程序範例

    //c/s建立連接時雙方彼此都要有 ip地址 /端口號;

    //連接一旦建立起來,那麼雙方的通訊【雙工收發】,就只需要用雙方彼此對應的套接字即可;

 

    //(3.5)客戶端服務器程序綜合演示和調用流程圖

    //服務器端程序要先運行;

四:TCP和UDP的區別

    //TCP(Transfer Control Protocol):傳輸控制協議

    //UDP(User Datagram Protocol):用戶數據報協議

    //socket()

 

    //TCP 比喻成 內衣內褲

    //UDP 比喻成 內衣內褲

    //a)TCP是大品牌內衣內褲,售後質量好;如果被偷取,廠家負責派人幫你找

    //b)UDP小品牌內衣內褲,沒有什麼售後服務;

 

    //TCP協議:可靠的面向連接的協議;數據包丟失的話操作系統底層會感知並且幫助你重新發送數據包;

    //UDP協議:不可靠的,無連接的協議;

    

    //優缺點:

    //a)tcp:可靠協議,必然要耗費更多的系統資源確保數據傳輸的可靠;

     //得到好處就是隻要不斷線,傳輸給對方的數據,一定正確的,不丟失,不重複,按順序到達對端;

    //b)udp:不可靠協議;發送速度特別快;但無法確保數據可靠性

 

    //各自的用途:

    //a)tcp:文件傳輸,收發郵件需要準確率高,但效率可以相對差;一般TCP比UDP用的範圍和場合更廣;

    //b)udp:qq聊天信息;DNS。。。。。。估計隨着網絡的發展,網絡性能更好,丟包率更低,那麼udp應用範圍更廣;

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章