(54)LINUX應用編程和網絡編程之九Linux網絡通訊實踐

3.9.1.linux網絡編程框架
3.9.1.一、網絡是分層的
(1)OSI 7層模型(理論指導)
(2)網絡爲何要分層
(3)網絡分層的具體表現
3.9.1.二、TCP/IP協議引入(網絡分層實現的具體實現)
(1)TCP/IP協議是用的最多的網絡協議實現
(2)TCP/IP分爲4層,對應OSI的7層
(3)咱們編程時最關注【應用層】,瞭解傳輸層(TCP/UDP/TFTP),網際互聯層和網絡接入層不用管
3.9.1.三、BS和CS
(1)CS架構介紹(client server,   客戶端服務器架構)
(2)BS架構介紹(broswer server,瀏覽器服務器架構)
-----------------------------------------------------------------------------------------------------------------------------------------------------------
網絡編程補充:注意在不一樣函數中sockfd是不一樣的。
 
                                                                                                   服務器端                                                                                
(1)建立套接字函數:int socket(int domain,int type,int protocol);第一個參數是建立套接字所使用的協議棧,一般爲AF_INET;di第二個參數是用來指定套接字的類型,即指定數據流套接字(SOCK_STREAM)仍是數據報套接字(SOCK_DGRAM);參數protocol一般設置爲0.返回一個socket描述符。
(2)綁定套接字:將套接字與計算機上的某個端口/IP綁定,進而在該端口監聽服務請求,使用 int bind(int sockfd,struct sockaddr *my_addr,int addrlen),咱們計算機存儲數據的方式是低位字節優先,而數據在網絡上傳輸的時候是高位字節優先,因此通常須要先進行字節轉換。其中htonl()和htons()函數是把主機字節順序轉化爲網絡字節順序;ntohl()函數和ntohs()是將網絡字節順序轉化爲主機字節順序。
(3)監聽網絡端口請求:int listen(int sockfd,int backlog),backlog用來設置請求隊列中容許的最大請求數。注意:TCP傳輸中爲被動套接字設置了兩個隊列:徹底創建鏈接的隊列和未徹底創建鏈接的隊列。
(4)接收鏈接請求:從徹底創建鏈接的隊列中接收一個鏈接,使用int accept(int sockfd,void *addr,int *addrlen);參數addr爲一個指向sockaddr_in結構體的指針,這個結構體是客戶端的IP地址和端口。服務器接收客戶端的鏈接請求後,accept函數會返回一個新的socket描述符,服務器進程可使用這個新的描述符同客戶進程傳輸數據。
(5)面向鏈接的數據傳輸:面向鏈接就是說在鏈接成功以後纔會進行數據傳輸。 int send(int sockfd,const void *msg,int len,unsigned int flags);其中第一個參數爲第四個步驟中產生的新的socket描述符;參數msg爲一個指針,指向要發送的數據;參數len爲要發送的數據長度;參數flag爲控制選項,通常置0. int recv(int sockfd,void *buf,int len,unsigned int flags);其中第一個參數爲第四個步驟中的socket描述符;參數buf爲存放接收數據的緩衝區;參數len爲緩衝的字節數;參數flags爲控制選項,通常狀況下設定爲0,。
(6)關閉套接字:int close(int sockfd);其中參數爲第四個步驟中產生的新的socket描述符
 
                                                                                                   客戶端
(1)建立套接字:int socket(int domain,int type,int protocol);第一個參數是建立套接字所使用的協議棧,一般爲AF_INET;di第二個參數是用來指定套接字的類型,即指定數據流套接字(SOCK_STREAM)仍是數據報套接字(SOCK_DGRAM);參數protocol一般設置爲0.返回一個socket描述符。
(2)與服務器創建鏈接:int connect(int sockfd,struct sockaddr  *serv_addr,int *addrlen );其中第一個參數爲第一個步驟中的socket描述符,第二個參數是指向sockaddr結構的指針,該結構體中包含了要鏈接的服務器端的IP地址和端口信息。
(3)面向鏈接的數據傳輸:面向鏈接就是說在鏈接成功以後纔會進行數據傳輸。 int send(int sockfd,const void *msg,int len,unsigned int flags);其中第一個參數爲第一個步驟中的socket描述符;參數msg爲一個指針,指向要發送的數據;參數len爲要發送的數據長度;參數flag爲控制選項,通常置0.  int recv(int sockfd,void *buf,int len,unsigned int flags);其中第一個參數爲第一個步驟中的socket描述符;參數buf爲存放接收數據的緩衝區;參數len爲緩衝的字節數;參數flags爲控制選項,通常狀況下設定爲0。
(4)關閉套接字:int close(int sockfd);其中參數爲第一個步驟中的socket描述符
 
 
 
                                                                                     服務器模型
(1)循環服務器模型和併發服務器模型:由於對於循環服務器來講,TCP循環服務器一次只可以處理一個客戶端的請求,處理完成後纔可以接受下一個請求,因此不多使用。咱們常用的是併發服務器模型,所謂併發,也就是說這種服務器模型在同一個時刻能夠響應多個客戶端的請求。它的核心思想就是每個客戶端的請求並非由服務器進程直接處理,而是由服務器建立一個子進程來處理,這個優點能夠彌補TCP循環服務器容易出現某個客戶端獨佔服務端的缺陷。
(2)TCP併發服務器的模型以下:
(3)併發服務器模型代碼示例:
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#define   SERV_PORT    5550                          //服務器監聽端口號
#define   LENGTH          10                              //請求隊列的長度
#define   SIZE                 128                           //緩衝區的長度
int  main()
{
    int res;
    int pth;                                                //定義建立子進程標識符
    int sockfd;                                            //定義監聽sockfd描述符
    int clientfd;                                         //定義數據傳輸sockfd描述符
    struct sockaddr_in hostaddr;                //服務器(主機)IP地址和端口號信息
    struct sockaddr_in clientaddr;                //客戶端IP地址和端口號信息
    unsigned int  addrlen;
    char buf[SIZE];                                    //定義緩衝區
    int cnt;
    /*建立套接字*/
    sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
    {
        printf("套接字建立失敗!\n");
        exit(1);
    }
    /*將套接字與IP地址和端口號進行綁定*/
    hostaddr.sin_family = AF_INET;                                     //TCP/IP協議
    hostaddr.sin_port = htons(SERV_PORT);                       //讓系統隨機選擇一個未被佔用的端口號
    hostaddr.sin_addr.s_addr = INADDR_ANY;                    //服務器(主機)IP地址
    bzero(&(hostaddr.sin_zero),8);                                         //清零
    res = bind(sockfd,(struct sockaddr *)&hostaddr,sizeof(struct sockaddr) );
    if(res == -1)
    {
        perror("套接字綁定失敗!\n");
        exit(1);
    }
    /*將套接字設置爲監聽模式,以等待客戶端的鏈接請求*/
    res = listen(sockfd,LENGTH);
    if(res == -1)
    {
        perror("設置監聽模式失敗!\n");
        exit(1);
    }
    printf("等待客戶端請求鏈接.......\n");
    /*循環等待客戶端的鏈接請求*/
    while(1)
    {
        addrlen = sizeof(struct sockaddr_in);
        clientfd =accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen);
        /*接受一個客戶端鏈接*/
        if(clientfd == -1)
        {
            perror("與客戶端鏈接錯誤\n");
            continue;
        }
 
        pth = fork();          //建立子進程
        if(pth<0)
        {
            perror("子進程建立失敗\n");
            exit(1);
        }
        if(pth == 0)               //子進程,處理客戶端的請求
        {
          //  close(sockfd);         //關閉父進程的套接字
            printf("客戶端IP:%s\n", inet_ntoa(clientaddr.sin_addr));     //輸出客戶端IP地址
            cnt = recv (clientfd ,buf ,SIZE ,0);                                            //接收客戶端的數據
            if(cnt==-1)
            {
                perror("接收客戶端數據失敗\n");
                  exit(1);
             }
             printf("收到的數據是:  %s\n",buf);
             close(clientfd);                                                                       //關閉當前客戶端的鏈接
             exit(0);                                                                                  //子進程退出
        }
        close(clientfd);                                                                            //父進程中,關閉子進程的套接字,準備接收下一個客戶端的鏈接
  }
    return 0;
}
 
(4)多路複用 I/O 併發服務器:爲了解決子進程帶來的系統資源消耗問題
 
 
-----------------------------------------------------------------------------------------------------------------------------------------------------------
3.9.2.TCP協議的學習1
3.9.2.一、關於TCP理解的重點
(1)TCP協議工做在傳輸層,對上服務socket接口(操做系統跟網絡編程有關的API),對下調用IP層(網絡層)
(2)TCP協議面向鏈接,通訊前必須先3次握手(通過3個步驟的握手通訊)創建鏈接關係後才能開始通訊(打電話例子)。
qq聊天就是面向非鏈接的。
(3)TCP協議提供可靠傳輸,不怕【丟包】、【亂序】等。
3.9.2.二、TCP如何保證可靠傳輸
(1)TCP在傳輸有效信息前要求通訊雙方必須先握手,創建鏈接才能通訊
(2)TCP的接收方收到數據包後會ack迴應(每次通訊都有迴應)給發送方,若發送方未收到ack會丟包重傳
(3)TCP的有效數據內容會附帶校驗碼,以防止內容在傳遞過程當中損壞
(4)TCP會根據網絡帶寬(網絡帶寬)來自動調節適配速率(滑動窗口技術)也就是說通訊雙方的通訊速率不是固定的,通訊速率和兩個方面有關:(1)一個包的大小(2)發送包的頻率
(5)發送方會給各分割報文編號,接收方會校驗編號,一旦順序錯誤即會重傳。
 
 
3.9.3.TCP協議的學習2(參考http://blog.csdn.net/whuslei/article/details/6667471
3.9.3.一、TCP創建鏈接時的三次握手
(1)創建鏈接須要三次握手
(2)創建鏈接的條件:服務器listen(監聽:隨時等待客戶端的鏈接)時客戶端主動發起connect
3.9.3.二、TCP關閉鏈接時的四次握手
(3)關閉鏈接須要四次握手
(4)服務器或者客戶端均可以主動發起關閉
注:這些握手協議已經封裝在TCP協議內部,socket編程接口平時不用管
3.9.3.三、基於TCP通訊的服務模式
(1)具備公網IP地址(在外網能夠訪問)的服務器(或者使用動態IP地址映射技術:花生殼)
(2)服務器端socket、bind、listen後處於監聽狀態
(3)客戶端socket後,直接connect去發起鏈接。
(4)服務器收到並贊成客戶端接入後會創建TCP鏈接,而後雙方開始收發數據,收發時是雙向的,並且雙方都可發起
(5)雙方都可發起關閉鏈接
3.9.3.四、常見的使用了TCP傳輸協議的網絡應用
(1)http、ftp(單純的傳文件)
(2)QQ服務器
(3)mail服務器
 
 
3.9.4.socket編程接口介紹
3.9.4.一、創建鏈接
(1)socket。socket函數相似於open,用來打開一個網絡鏈接,若是成功則返回一個網絡文件描述符(int類型),以後咱們操做這個網絡鏈接都經過這個網絡文件描述符。
/*
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain表示是IPV4仍是IPV6.內部是一個宏定義,參數domain用來指定要建立的套接字所使用的協議棧,一般爲AF_INET.表示互聯網協議族(TCP/IP協議族)
type:用來指定套接字的類型
(1)  SOCK_STREAM           TCP協議   數據流套接字
(2)SOCK_DGRAM               UDP協議  數據報套接字
(3)SOCK_SEQPACKET
protocol:一般設爲0,讓系統根據咱們以前設定的domain和type自動設置協議。
返回值:一個socket描述符,是指向內部數據結構的指針;在調用過程當中出現錯誤的時候會返回-1.
*/
(2)bind  綁定套接字函數
/* #include <sys/socket.h>
 int bind(int sockfd, const struct sockaddr *address,socklen_t address_len);
*/
sockfd爲要綁定的socket描述符,address爲一個指向本機IP地址和端口號等信息的sockaddr結構體的指針,address_len一般設置爲sockaddr結構的長度。
返回值:函數調用成功後返回值爲0,不然返回值爲-1.
 
sockaddr結構用來保存socket信息:
struct sockaddr {
unsigned short sa_family;        //表示套接字的協議棧地址,對於TCP/IP能夠設置爲 AF_INET
char sa_data[14];       //sa_data爲套接字的IP地址和端口號
 };
(3)listen
// int listen(int socket, int backlog);
backlog表示請求隊列中容許的最大請求數。
(4)connect客戶端
/* int connect(int socket, const struct sockaddr *address,socklen_t address_len);*/
返回成功返回值爲0,不然爲-1.
3.9.4.三、創建鏈接後進行數據的發送和接收
(1)send和write
/*ssize_t send(int socket, const void *buffer, size_t length, int flags);*/
flags:通常設置爲0便可
(1)MSG_EOR:Terminates a record (if supported by the protocol).
(2)MSG_OOB:Sends  out-of-band  data on sockets that support out-of-band communications.  The significance and semantics of out-of-band data  are  protocol-specific.
 
(2)recv和read
/* ssize_t recv(int socket, void *buffer, size_t length, int flags);*/
 
3.9.4.四、輔助性函數(主要是進行IP地址轉換的)
(1)inet_aton、inet_addr、inet_ntoa
(2)inet_ntop(從32位二進制轉換爲點分十進制字符串)、inet_pton(從點分十進制字符串轉換爲32位二進制)(推薦使用)
注意:點分十進制字符串即:"192.168.1.11"類型
3.9.4.五、表示IP地址相關數據結構
(1)都定義在 netinet/in.h
(2)struct sockaddr,這個結構體是網絡編程接口中用來表示一個IP地址的,注意這個IP地址是不區分IPv4和IPv6的(或者說是兼容IPv4和IPv6的)
(3)typedef uint32_t in_addr_t;        網絡內部用來表示IP地址的類型(vi  /usr/include/netinet/in.h)
(4)struct in_addr
  {
    in_addr_t s_addr;
  };
(5)struct sockaddr_in  針對IPV4
  {
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;                 /* Port number.  */
    struct in_addr sin_addr;            /* Internet address.  */
 
    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
                           __SOCKADDR_COMMON_SIZE -
                           sizeof (in_port_t) -
                           sizeof (struct in_addr)];
  };
(6)struct sockaddr            這個結構體是linux的網絡編程接口中用來表示IP地址的標準結構體,bind、connect等函數中都須要這個結構體,這個結構體是兼容IPV4和IPV6的。在實際編程中這個結構體會被一個struct sockaddr_in或者一個struct sockaddr_in6所填充。
 
 
3.9.5.IP地址格式轉換函數實踐
補充:
(一)大端模式,是指數據的高字節保存在內存的低地址中,而數據的低字節保存在內存的高地址中,這樣的存儲模式有點兒相似於把數據看成字符串順序處理地址由小向大增長,而數據從高位往低位放;
小端模式,是指數據的高字節保存在內存的高地址中,而數據的低字節保存在內存的低地址中,這種存儲模式將地址的高低和數據位權有效地結合起來,高地址部分權值高,低地址部分權值低,和咱們的邏輯方法一致。
(二)
網絡字節序:網絡字節是隻容許大段模式
電腦是小端模式:要轉換爲大端模式
3.9.5.一、inet_addr、inet_ntoa、inet_aton
3.9.5.二、inet_pton、inet_ntop
inet_addr解析:會自動檢測咱們電腦是大端仍是小端
/*
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_addr(const char *cp);    //參數是一個點分十進制的IP地址字符串,轉爲in_addr_t,即uint32_t網絡字節大端格式的二進制
*/
inet_pton解析:參數是一個點分十進制的IP地址字符串,轉爲32位二進制   兼容IPV6
/*   
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
*/
inet_ntop 解析:
/*
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);
*/
 
代碼示例:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
 
#define IPADDR    "192.168.1.102"
 
// 0x66        01    a8        c0
// 102        1    168        192
// 網絡字節序,其實就是大端模式
 
 
int main(void)
{
    struct in_addr addr = {0};
    char buf[50] = {0};
 
    addr.s_addr = 0x6703a8c0;
 
    inet_ntop(AF_INET, &addr, buf, sizeof(buf));
 
    printf("ip addr = %s.\n", buf);
 
 
/*    模塊二
    // 使用inet_pton來轉換
    int ret = 0;
    struct in_addr addr = {0};
 
    ret = inet_pton(AF_INET, IPADDR, &addr);
    if (ret != 1)
    {
        printf("inet_pton error\n");
        return -1;
    }
 
    printf("addr = 0x%x.\n", addr.s_addr);
*/   
 
    /*模塊一
    in_addr_t addr = 0;
 
    addr = inet_addr(IPADDR);
 
    printf("addr = 0x%x.\n", addr);        // 0x6601a8c0
*/   
    return 0;
}
 
 
3.9.6_7.soekct實踐編程1_2
3.9.6.一、服務器端程序編寫
服務器跟你的這臺電腦的IP地址相綁定。當客戶端路由到你這臺電腦的IP後,怎麼找到你這臺電腦的具體哪一個服務器呢?答案就是經過端口號來標識。
IP地址用來精確到某一臺電腦,端口號用來標識這臺電腦的某一個具體進程。
(1)socket
(2)bind
(3)listen
(4)accept,返回值是一個fd,accept正確返回就表示咱們已經和前來鏈接個人客戶端之間創建了一個TCP鏈接了,之後咱們就要經過這個鏈接來和客戶端進行讀寫操做,讀寫操做就須要一個fd,這個fd就由accept來返回了。
注意:socket返回的fd叫作監聽fd,是用來監聽客戶端的,也就是判斷是哪個客戶端鏈接來的,不能用來和任何客戶端進行讀寫;accept返回的fd叫作鏈接fd,用來和鏈接那端的客戶端程序進行讀寫。
3.9.6.二、客戶端程序編寫
(1)socket
(2)connect
 
 
概念:端口號,實質就是一個數字編號,用來在咱們一臺主機中(主機的操做系統中)惟一的標識一個能上網的進程。端口號和IP地址一塊兒會被打包到當前進程發出或者接收到的每個數據包中。每個數據包未來在網絡上傳遞的時候,內部都包含了發送方和接收方的信息(就是IP地址和端口號),因此IP地址和端口號這兩個每每是打包在一塊兒不分家的。
 
 
3.9.8.socket實踐編程3
3.9.8.一、客戶端發送&服務器接收
3.9.8.二、服務器發送&客戶端接收
3.9.8.三、探討:如何讓服務器和客戶端好好溝通
(1)客戶端和服務器原則上均可以任意的發和收,可是實際上雙方必須配合:client發的時候server就收,而server發的時候client就收
(2)必須瞭解到的一點:client和server之間的通訊是異步的,這就是問題的根源
(3)解決方案:依靠應用層協議來解決。說白了就是咱們server和client事先作好一系列的通訊約定。
 
 
3.9.9.socket編程實踐4
3.9.9.一、自定義應用層協議第一步:規定發送和接收方法
(1)規定鏈接創建後由客戶端主動向服務器發出1個請求數據包,而後服務器收到數據包後回覆客戶端一個迴應數據包,這就是一個通訊回合,完成了一次數據交換
(2)整個鏈接的通訊就是由N多個回合組成的。
 
3.9.9.二、自定義應用層協議第二步:定義數據包格式
3.9.9.三、經常使用應用層協議:http、ftp······
3.9.9.四、UDP簡介
相關文章
相關標籤/搜索