《Unix網絡編程》卷一(簡介TCP/IP、基礎套接字編程)

一般說函數返回某個錯誤值,其實是函數返回值爲-1,而全局變量errno被置爲指定的常值(即稱函數返回這個錯誤值)。linux

exit終止進程,Unix在一個進程終止時老是關閉該進程全部打開的描述符數組

TCP三次握手完畢,accept返回其返回值是一個稱爲已鏈接描述符(connected descriptor)的新描述符。調用close關閉與客戶的鏈接。服務器

套接字APIcookie

TCP狀態轉移圖,11種狀態。網絡

MSS,maximum segment size,最大分節大小。一般客戶端主動打開時發送SYN的同時會發送MSS,服務端應答(SYN、ACK、MSS)時一樣也發送一個MSS,不一樣方向上MSS能夠不一樣app

 

SCTP多宿特性,單個SCTP端點能支持多個IP地址。dom

SCTP的四路握手。驗證標記、狀態cookie。socket

 

IPv4數據報最大長度65535,包括IP首部,由於總長度字段是16位;IPv6數據報最大長度65575,包括40字節IPv6首部由於淨長度字段是16位。tcp

IPv4首部20字節(固定長度),另外還有最多40字節可選部分ide

 

套接字地址結構在內核和進程間傳遞,從進程傳到內核,從內核傳到進程,不是很是理解,應該就是參數傳遞進去和被寫回吧?另外有一個值-結果參數之前不多碰到。

 每一個文件套接字都有一個引用計數,引用計數在文件表項中維護。

存放在硬盤上的可執行程序文件能被Unix執行的惟一方法是:由一個現有進程調用六個exec函數中的一個。(理解)

六個exec函數區別在於:待執行程序由文件名仍是路徑名指定參數是一一列出仍是指針數組把調用進程環境傳遞給新程序仍是指定新環境

子進程調用exec時,子進程內存映像被替換成新程序文件,只有文件描述符(在socket中是已鏈接套接字描述符)跨exec繼續保持開放

 

一個簡單echo客戶/服務器程序涉及到的一些細節(很是重要):

僵死子進程,經過捕獲SIGCHLD信號加以處理;

信號處理函數必須調用waitpid而不是wait,由於unix信號是不排隊的,當有多個子進程時wait函數只能處理第一個,後面的將變成僵死進程;

另外一個問題是服務器進程終止時,客戶進程沒被告知,由於客戶進程阻塞於等待用戶輸入而未接收到通知,這須要select或poll函數來處理,它們等待多個描述符中的任何一個就緒而不是阻塞於某個描述符。

select函數中間的三個參數(讀、寫、異常描述符集)都是值—結果參數,返回時,未就緒描述符對應的被置0,其餘是1,從新調用時,將所關心位重置1

close函數有兩個限制,shutdown函數能夠克服,它關閉一半tcp鏈接(可參數選讀或者寫)。

pselect間參數結構不一樣另外一個不一樣點是多了一個指向信號掩碼的指針,從而運行程序先禁止提交某些信號。

 

拒絕服務(denial of service,DoS)型攻擊。

 

UDP也可使用connect函數,成爲已鏈接UDP套接字,而後可使用write和read函數讀寫,而不是sento和recvfrom。

 

DNS中的條目稱爲資源記錄(resource record,RR)。常見RR類型有:A、AAAA、PTR、MX、CNAME。

 

gethostbyname和gehostbyaddr的返回值均爲一個hostent(host entry縮寫)結構:

struct hostent { const char    *h_name;    // 主機的正式名稱。 
    char    **h_aliases;    // 空字節-地址的預備名稱的指針。
    short    h_addrtype;    // 地址類型; 一般是AF_INET。 
    short    h_length;    // 地址的比特長度
    char    **h_addr_list;    // 主機網絡地址指針(只有IPV4
   #define    h_addr    h_addr_list[0]    // h_addr 爲 h_addr_list中的第一地址。  };

gethostbyname只能返回IPV4地址,而getaddrinfo可以同時處理IPV4和IPV6地址。

gethostbyname和gehostbyaddr用於主機名和地址的映射,而getservbyname和getservbyport則用於服務與端口之間的映射。

getaddrinfo能處理名字到地址、服務到端口的轉換(由參數中列表中第一個爲主機名,第二個爲服務名可看出):

int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );//返回結果保存到這裏

其中,保存返回結果的struct addrinfo結構體詳細信息以下:

表頭文件: #include<netdb.h>

struct addrinfo
{ 
int ai_flags; 
int ai_family; //AF_INET,AF_INET6,UNIX etc
int ai_socktype; //STREAM,DATAGRAM,RAW
int ai_protocol; //IPPROTO_IP, IPPROTO_IPV4, IPPROTO_IPV6 etc
size_t ai_addrlen;//length of ai_addr
char* ai_canonname; //full hostname 
struct sockaddr* ai_addr; //addr of host
struct addrinfo* ai_next;
}

value of ai_falgs:
AI_PASSIVE: Socket address is intended for `bind'.
AI_CANONNAME:Request for canonical name.
AI_NUMERICHOST: Don't use name resolution.
AI_V4MAPPED: IPv4 mapped addresses are acceptable. 
AI_ALL: Return IPv4 mapped and IPv6 addresses. 
AI_ADDRCONFIG:Use configuration of this host to choose

定義函數: 
int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints,
struct addrinfo **result );

函數說明:
      getaddrinfo函數可以處理名字到地址以及服務到端口這兩種轉換,返回的是一個sockaddr 結構的鏈而 不是一個地址清單。它具備協議無關性。
      hostname:一個主機名或者地址串(IPv4的點分十進制串或者IPv6的16進制串)
      service:一個服務名或者10進制端口號數串。
       hints:能夠是一個空指針,也能夠是一個指向某個addrinfo結構的指針,調用者在這個結構中填入關於指望返回的信息類型的暗示。舉例來講:若是指定的服務既支持TCP也支持UDP,那麼調用者能夠把hints結構中的ai_socktype成員設置成SOCK_DGRAM使得返回的僅僅是適用於數據報套接口的信息。返回0: 成功,返回非0: 出錯。

定義函數:const char *gai_strerror( int error );
函數說明:
      該函數以getaddrinfo返回的非0錯誤值的名字和含義爲他的惟一參數,返回一個指向對應的出錯信息串的指針。

定義函數: void freeaddrinfo( struct addrinfo *ai );
函數說明:
       由getaddrinfo返回的全部存儲空間都是動態獲取的,這些存儲空間必須經過調用freeaddrinfo返回給系統。
struct addrinfo結構體

與getaddrinfo函數對應的是getnameinfo函數。

 

在linux環境下,結構體struct sockaddr在/usr/include/linux/socket.h中定義,具體以下:
typedef unsigned short sa_family_t;
struct sockaddr {
        sa_family_t     sa_family;    /* address family, AF_xxx       */
        char            sa_data[14];    /* 14 bytes of protocol address */

在linux環境下,結構體struct sockaddr_in在/usr/include/netinet/in.h中定義,具體以下:
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
    __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)];     
                           /* 字符數組sin_zero[8]的存在是爲了保證結構體struct sockaddr_in的大小和結構體struct sockaddr的大小相等 */
};
struct sockaddr是通用的套接字地址,而struct sockaddr_in則是internet環境下套接字的地址形式,兩者長度同樣,都是16個字節。兩者是並列結構,指向sockaddr_in結構的指針也能夠指向sockaddr。通常狀況下,須要把sockaddr_in結構強制轉換成sockaddr結構再傳入系統調用函數中。

下面是struct sockaddr_in中用到兩個數據類型,具體定義以下:
/* Type to represent a port. */
typedef uint16_t in_port_t; 

struct in_addr其實就是32位IP地址
struct in_addr {
        unsigned long s_addr;
};

BSD網絡軟件中包含了兩個函數,用來在二進制地址格式和點分十進制字符串格式之間相互轉換,可是這兩個函數僅僅支持IPv4。
       in_addr_t inet_addr(const char *cp);
       char *inet_ntoa(struct in_addr in);
功能類似的兩個函數同時支持IPv4和IPv6
       const char *inet_ntop(int domain, const void *addr, char *str, socklen_t size);
       int inet_pton(int domain, const char *str, void *addr);

一般的用法是:
int sockfd;
struct sockaddr_in my_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0); 

my_addr.sin_family = AF_INET; /* 主機字節序 */
my_addr.sin_port = htons(MYPORT); /* short, 網絡字節序 */

my_addr.sin_addr.s_addr = inet_addr("192.168.0.1");

bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */
//memset(&my_addr.sin_zero, 0, 8);

bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));

#define UNIX_PATH_MAX 108

  struct sockaddr_un {

  sa_family_t sun_family; /*PF_UNIX或AF_UNIX */

  char sun_path[UNIX_PATH_MAX]; /* 路徑名 */

  };

struct sockaddr結構類型是用來保存socket信息的:
   struct sockaddr {
   unsigned short sa_family; /* 地址族, AF_xxx */——地址的格式
  char sa_data[14]; /* 14 字節的協議地址 */——地址值(IP和端口號)
  };

Sockfd是調用socket函數返回的socket描述符,my_addr是一個指向包含有本機IP地址及端口號等信息的sockaddr類型的指針;addrlen常被設置爲sizeof(struct sockaddr)。 
  struct sockaddr結構類型是用來保存socket信息的: 
  struct sockaddr { 
   unsigned short sa_family; /* 地址族, AF_xxx */ 
char sa_data[14]; /* 14 字節的協議地址 */ 
}; 
  sa_family通常爲AF_INET,表明Internet(TCP/IP)地址族;sa_data則包含該socket的IP地址和端口號。 
  另外還有一種結構類型: 
  struct sockaddr_in { 
   short int sin_family; /* 地址族 */ 
   unsigned short int sin_port; /* 端口號 */ 
   struct in_addr sin_addr; /* IP地址 */ 
   unsigned char sin_zero[8]; /* 填充0 以保持與struct sockaddr一樣大小 */ 
  }; 
   這個結構更方便使用。sin_zero用來將sockaddr_in結構填充到與struct sockaddr一樣的長度,能夠用bzero()或memset()函數將其置爲零。指向sockaddr_in 的指針和指向sockaddr的指針能夠相互轉換,這意味着若是一個函數所需參數類型是sockaddr時,你能夠在函數調用的時候將一個指向 sockaddr_in的指針轉換爲指向sockaddr的指針;或者相反。


你只要記住,填值的時候使用sockaddr_in結構,而做爲函數的 
參數傳入的時候轉換成sockaddr結構就好了,畢竟都是16個字符 
長。


struct in_addr { 
union { 
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; 
struct { u_short s_w1,s_w2; } S_un_w; 
u_long S_addr; 
} S_un };
struct sockaddr與struct sockaddr_in ,struct sockaddr_un的區別和聯繫

 

inet_pton函數將點分十進制格式IP地址轉換爲二進制整數,IPV4和IPV6都支持:
 
inet_pton:將「點分十進制」 -> 「二進制整數」 int inet_pton(int af, const char *src, void *dst); 這個函數轉換字符串到網絡地址,第一個參數af是地址簇,第二個參數*src是來源地址,第三個參數* dst接收轉換後的數據。 inet_pton 是inet_addr的擴展,支持的多地址族有下列: af = AF_INET src爲指向字符型的地址,即ASCII的地址的首地址(ddd.ddd.ddd.ddd格式的),函數將該地址轉換爲in_addr的結構體,並複製在*dst中。 af = AF_INET6 src爲指向IPV6的地址,函數將該地址轉換爲in6_addr的結構體,並複製在*dst中。 若是函數出錯將返回一個負值,並將errno設置爲EAFNOSUPPORT,若是參數af指定的地址族和src格式不對,函數將返回0。
相關文章
相關標籤/搜索