Socket編程實踐(1) 基本概念

1. 什麼是socket

socket能夠當作是用戶進程與內核網絡協議棧的編程接口。TCP/IP協議的底層部分已經被內核實現了,而應用層是用戶須要實現的,這部分程序工做在用戶空間。用戶空間的程序須要經過套接字來訪問內核網絡協議棧。html

套接口是全雙工的通訊,它不只能夠用於本機的進程間通訊,還能夠用於網絡上不一樣主機的進程間通訊。編程

套接字還能夠異構系統間進行通訊,異構系統指的是在硬件或軟件上有所差異的系統,例如安卓系統的手機與windows系統的PC機上均可以實現QQ通訊,套接字能夠實如今這兩個設備上的通訊。windows

2. IPV4套接口地址結構

套接口既然可以鏈接兩個端系統,那它就須要一個地址來標記該端系統,例如兩個電話須要電話號碼來標記才能夠進行撥號。這抽象成套接口的地址結構。IPV4套接口地址結構一般也稱爲「網際套接字地址結構」,它以sockaddr_in命名,定義在頭文件< netinet/in/h >中。網絡

struct sockaddr_in{

    uint8_t sin_len;

    sa_family_t sin_family;

    in_port_t sin_port;

    struct in_addr sin_addr;

    char sin_zero[8];

};

說明:socket

  1. sin_len:整個sockaddr_in結構體的長度,在4.3BSD-Reno版本以前的第一個成員是sin_family。
  2. sin_family:指定帶地址家族,在這裏必須設置爲AF_INET。socket在設計時不只能夠用於TCP/IP協議,它還能夠用於其餘協議,例如unix域協議,地址家族用於指定該套接字用於哪一種協議。AF_INET表示用於IPV4協議。
  3. sin_port:端口號,16位的無符號整數,可以表示到65535。2個字節。
  4. sin_addr: IPV4的地址。4個字節的整數。
  5. sin_zero:暫不使用,通常將其設置爲0。

其中,struct in_addr僅僅是一個32位的無符號整數,能夠在終端下輸入man 7 ip進行查看:函數

接下來看一下通用的地址結構。上面說過,socket能夠用於不一樣的協議上,通用的地址結構能夠用於任何協議的socket編程。測試

struct sockaddr{

    uint8_t sin_len;

    sa_family sin_family;

    char sa_data[14];

};

說明:ui

  1. sin_len:整個sockaddr結構大小
  2. sin_family:指定該地址家族
  3. sa_data:由sin_family決定它的形式

能夠看到,在通用地址結構中sa_data是14個字節,而在IPV4的地址結構中,sin_port、sin_addr、sin_zero三個變量加起來也等於14個字節。也便是說,這兩種結構是兼容的。設計

3. 網絡字節序

字節序能夠分爲大端字節序與小端字節序:unix

  • 大端字節序(Big Endian) :最高有效位存儲於最低內存地址處,最低有效位存儲於最高地址內存處。
  • 小端字節序(Little Endian):恰好與大端字節序倒過來,最高有效位存於最高內存地址處,最低有效位存儲於最低內存地址處。

這樣提及來挺抽象,經過一幅圖來講明:

上面說過,socket能夠用於異構系統之間的通訊。而不一樣的系統採用的字節序多是不一樣的,有的系統採用大端字節序,例如Motorola 6800;有的採用小端字節序,如X86。所以,在進行字節傳輸時,應該同一一個字節序,稱爲網絡字節序。網絡字節序採用大端字節序。若是主機A爲小端字節序的系統,那麼在傳輸時須要先將小端字節序轉換成網絡字節序。這須要一些字節序的轉換函數。

咱們能夠編寫程序來測試本身的主機是什麼字節序:

#include<stdio.h>

int main(void)

{

        unsigned int x = 0x12345678;

        unsigned char *p = (unsigned char*)&x;

        printf("%0x,%0x,%0x,%0x\n",p[0],p[1],p[2],p[3]);

        return 0;

}

在個人電腦上輸出結果爲:78,56,34,12. 所以個人主機爲小端字節序。

4. 字節序轉換函數

若是主機的字節序與網絡字節序不一樣,那麼須要進行字節序的轉換。下面是一些字節序轉換函數:

# include < arpa/inet.h >

   uint32_t htonl(uint32_t hostlong);

   uint16_t htons(uint16_t hostshort);

   uint32_t ntohl(uint32_t netlong);

   uint16_t ntohs(uint16_t netshort);

說明:h表明host;n表明network;s表明short;l表明long

描述:

  • htonl()函數將無符號整數hostlong從主機字節序轉換成網絡字節序。
  • htons()函數將無符號短整型hostshort從主機字節序轉換成網絡字節序。
  • ntohl()函數功能與 htonl()函數相反
  • ntohs()函數功能與htons()函數相反

咱們能夠進行驗證,剛纔已經經過程序測試出個人主機是小端字節序,接下來使用函數 htonl()將整數0x12345678轉換成網絡字節序。

#include<stdio.h>

#include <arpa/inet.h>

int main(void)

{

        unsigned int x = 0x12345678;

        unsigned char *p = (unsigned char*)&x;

        printf("轉換前:%0x,%0x,%0x,%0x\n",p[0],p[1],p[2],p[3]);

        unsigned int y = htonl(x);

        p = (unsigned char *) &y;

        printf("轉換後:%0x,%0x,%0x,%0x\n",p[0],p[1],p[2],p[3]);

        return 0;

}

結果輸出:

轉換前:78,56,34,12

轉換後:12,34,56,78

5. 地址轉換函數

對於IP地址,咱們一般採用點分十進制的形式進行直觀的認識,而程序更多的時候是處理32位的地址,所以須要有函數在點分十進制與32位地址這兩種形式間進行轉換。

# include < sys/socket.h>

   # include < netinet/in.h>

   # include < arpa/inet.h>



   int inet_aton(const char *cp, struct in_addr *inp);

   in_addr_t inet_addr(const char *cp);

   char *inet_ntoa(struct in_addr in);

描述:

  • inet_addr()函數:表示將點分十進制的IP地址轉換成32位的ip地址(整數)。
  • inet_ntoa()函數:將32位ip地址(網絡字節序)轉換成點分十進制的ip之地。

例程:

#include<stdio.h>

#include<arpa/inet.h>

int main()

{

        unsigned long addr = inet_addr("192.168.0.100");//將點分十進制轉換爲32bit地址

        printf("addr = %u\n",htonl(addr)); 



        struct in_addr ipaddr;

        ipaddr.s_addr = addr;

        printf("ipaddr = %s\n",inet_ntoa(ipaddr)); //網絡字節序地址轉換爲點分十>進制

        return 0;

}

輸出:

addr = 3232235620

ipaddr = 192.168.0.100

6. 套接字類型

套接字類型主要有三種:

  1. 流方套接字(SOCK_STREAM):它對應TCP協議,它提供面向鏈接的、可靠的數據傳輸服務,數據無差錯、無重複的發送,且按發送順序接收。
  2. 數據報套接字(SOCK_DGREAM):提供無鏈接服務。不提供無錯保證,數據可能丟失或重複,而且接收順序混亂。
  3. 原始套接字(SOCK_RAW):它提供一種能力,讓咱們直接跨越傳輸層,直接對IP層進行數據封裝,經過該套接字,咱們能夠直接將數據封裝成IP層可以認識的協議格式。

文章鏈接:http://www.cnblogs.com/QG-whz/p/5426634.html

相關文章
相關標籤/搜索