這系列是閱讀 The Linux socket TCP/IP protocols network programming tutorials所記錄的筆記,這份tutorial寫的相當好,如果有不清楚的地方,建議大家看看原文,寫的非常詳細,這裡只節錄重點。

 

Socket

1. socket是IPC所使用的一種API。

2. 也稱為Berkeley Socket或BSD Socket

3. Connection-oriented socket (TCP)

 

connection-oriented

4. Connectionless socket (UDP)

 

connectionless

Data structure for socket

1. protocol family: 建socket 的一個參數

2. Service type: 建socket 的一個參數 (Stream, Datagram)

3. Local IP address: 用bind()設定

4. Local port: 用bind()設定

5. Remote IP address: 用connect()設定

6. Remote port: 用connect()設定

Socket endpoint

TCP/IP 的communication是由2個endpoint組成。一個endpoint是由一個ip加一個port,所以一台電腦可以有很多的endpoint。PF_INET是internet protocol family; AF_INET是internet address family (Normally PF_INET = AF_INET = 2)。

 

Host / Hostname

host指的是internet上的一台電腦 (computer, file server, etc),而hostname則是用來識別此電腦的名稱,有時會跟domain name混用,雖然有些技術上的差異,如www.google.com,主要是用來uniquely identify。

 

 

Host address data type

hostaddress是由4byte的數字組成,如140.113.23.3,前3個稱為network number,最後1個叫local address。hostaddress可用一個unsigned long int表示。scoket中的struct in_addr將這個整數包裝起來,定義在in.h。

/* Internet address. */
struct in_addr {
    __be32    s_addr;
};

這個s_addr用unsigned long int 所表示的host address number。

特殊的host address number:

1. INADDR_LOOPBACK: 指本機的address,也就是127.0.0.1 (localhost),而不需去查其真實的address。只在本機的IPC,使用INADDR_LOOPBACK可避免不必要的traffic。

2. INADDR_ANY: 指任何連上來的address。如果要接受所有來自 internet的connection可使用。

3. INADDR_BROADCAST: 傳送broadcast訊息可使用。

4. INADDR_NONE: 某些function錯誤時的回傳值。

 

host address的處理函數

定義在arpa/inet.h。(network byte order v.s. host byte order??)

1.int inet_aton(const char *name, struct in_addr *addr): 將像140.113.23.3這樣的adress轉成binary,並且存入struct in_addr。成功時傳回非零,失敗傳0。

2. unsigned long int inet_addr(const char *name):也是將address轉成binary,但錯誤時傳回 INADDR_NONE。這是inet_aton的舊版,因為INADDR_NONE也是合法的address (255.255.255.255),而inet_aton有比較好的error return處理。

3. unsigned long int inet_network(const char *name): 將像140.113.23.3的address轉成host byte order的number,成功傳回轉換結果,失敗傳回-1。

4. char * inet_ntoa(struct in_addr addr): 將internet host address轉回像140.113.23.3的address,傳回轉換結果的pointer。


接下來的函數是處理同一塊buffer,連續呼叫會覆寫之前的結果。因此要將處理結果先存下來。


5. struct in_addr inet_makeaddr(int net, int local)

6. int inet_lnaof(struct in_addr addr)

7. Function int inet_netof(struct in_addr addr)


socket的API

 

structure

 

struct sockaddr {
    sa_family_t    sa_family;    /* address family, AF_xxx    */
    char        sa_data[14];    /* 14 bytes of protocol address    */
};

 

struct sockaddr_in {
  sa_family_t        sin_family;    /* Address family        */
  __be16        sin_port;    /* Port number            */
  struct in_addr    sin_addr;    /* Internet address        */

  /* Pad to size of `struct sockaddr'. */
  unsigned char        __pad[__SOCK_SIZE__ - sizeof(short int) -
            sizeof(unsigned short int) - sizeof(struct in_addr)];
};

1. To deal with struct sockaddr, programmers created a parallel structure: struct sockaddr_in ("in" for "Internet".)

2. The sin_zero field is reserved, and you must set it to hexadecimal zeroes.

3. sockaddr_in is a "specialized" sockaddr.

4. The sin_port and sin_addr must be in Network Byte Order.

 

函數

socket()

NAME
       socket() - create an endpoint for communication
SYNOPSIS
       #include <sys/types.h>
       #include <sys/socket.h>
       int socket(int domain, int type, int protocol);

1. 由server和client使用

2. domain: 設AF_INET

3. type: SOCK_STREAM或SOCK_DGRAM

4. protocol: 0 (讓socket()根據type自動設定)。更好的方式是使用getprotobyname()。

5. 成功傳回socket descriptor,失敗傳回-1 (並使用errno的macro)

6. 正確使用為在struct sockaddr_in使用AF_INET,在socket()使用PF_INET。但AF_INET可用在任何地方。

 

bind()

NAME
       bind() - bind a name to a socket
SYNOPSIS
       #include <sys/types.h>
       #include <sys/socket.h>
       int bind(int sockfd, struct sockaddr *my_addr, int addrlen);

1. 由server使用

2. sockfd為socket()的回傳值。

3. myaddr 須指定port和ip後傳入,注意是用struct sockaddr_in宣告,傳入時轉為struct sockaddr。

4. addrlen直接用 sizeof(struct sockaddr)

5. bind就是將local的endpoint attach到一個socket

connect()

NAME
       connect() - initiate a connection on a socket.
SYNOPSIS
       #include <sys/types.h>
       #include <sys/socket.h>
       int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

1. connect()由client呼叫

2. sockfd由socket()產生,serv_addr指定server的ip和port後傳入

3. addrlen直接用 sizeof(struct sockaddr)

 

 

listen()

NAME
       listen() - listen for connections on a socket
SYNOPSIS
       #include <sys/socket.h>
       int listen(int sockfd, int backlog);

1. 由server使用

2. sockfd由socket()產生,backlog設定可queue住的connection數量 (等待accept())。

3. 失敗傳回-1,並使用errno

 

 

accept()

NAME
       accept() - accept a connection on a socket
SYNOPSIS
       #include <sys/types.h>
       #include <sys/socket.h>
       int accept(int sockfd, struct sockaddr *addr, int *addrlen);

 

1. sockfd: listen()所使用的那個sockfd

2. addr 宣告完就拿來用,將存放client的資訊

3. addrlen直接用 sizeof(struct sockaddr)

4. 失敗傳回-1,並使用errno

5. server listen()完後開始等待accept()。accept()傳回一個file descriptor以供此connection的I/O之用。accpet完之後,server繼續使用listen()的fd等待下一個connection。

send()

int send(int sockfd, const void *msg, int len, int flags);

1. sockfd可以是listen()的fd或是accpet的fd。

2. msg就是data,len就是data 的length (sizeof(msg)),flag就設0。

3. 回傳值為送出去的大小

4. 失敗傳回-1,並使用errno

recv()

  • The recv() call is similar in many respects:

int recv(int sockfd, void *buf, int len, unsigned int flags);

1. sockfd: 要從哪個fd接收。

2. 回傳值為收到的大小。若回傳0,表示對方把connection切了。

 

write()

NAME
       write() - write to a file descriptor
SYNOPSIS
       #include <unistd.h>
       ssize_t write(int fd, const void *buf, size_t count);

1. 可寫到file,device或socket

 

read()

 

NAME
       read() - read from a file descriptor
SYNOPSIS
       #include <unistd.h>
       ssize_t read(int fd, void *buf, size_t count);

1. 從file,device或socket讀取

2. 沒有data則read會block

3. count指定要讀的長度,如果沒有那麼多,則return,不會block

 

 

int shutdown(int sockfd, int how);

1. 0: 不允許將來的receive; 1: 不允許將來的send; 2: 不允許將來的send和receice (跟close()一樣,但沒有真的的close fd,將fd free掉)

 

 

Byte order

不同的架構在communication時,其word會使用不同的byte order==>Big-endian(MSB)和Little-endian。 Internet protocols 有其標準的byte order,即network byte order。建立socket時,必須確定ip和port (sin_addr和sin_port)是用network byte order來表示。

getservbyname(),gethostbyname和inet_addr都是network byte order,因此可直接使用。

轉換的函數如下,並定義在netinet/in.h:

htons() / ntohs(): 轉換sin_port

htonl()/ ntohl: 轉換sin_addr

ex: ina.sin_addr.s_addr = inet_addr("10.12.110.57"); //obsolete of inet_aton (ascii to network),但不是每個平台都有implement inet_aton()

Typical use:

 

struct sockaddr_in my_addr;

/* host byte order */

my_addr.sin_family = AF_INET;

/* short, network byte order */

my_addr.sin_port = htons(MYPORT);

inet_aton("10.12.110.57", &(my_addr.sin_addr));

/* zero the rest of the struct */

memset(&(my_addr.sin_zero), 0, 8);

reverse function:

printf("%s", inet_ntoa(ina.sin_addr));

要注意的是,inet_ntoa使用的是靜態的buffer,所以會有如下的情形:

char *a1, *a2;

...

...

a1 = inet_ntoa(ina1.sin_addr); /* this is 192.168.4.1 */

a2 = inet_ntoa(ina2.sin_addr); /* this is 10.11.110.55 */

printf("address 1: %s\n", a1);

printf("address 2: %s\n", a2);

  • Will print:

address 1: 10.11.110.55

address 2: 10.11.110.55

因此可先用strcpy將a1存起來。

Endian -- 0x01020304

Big-endian: 01, 02, 03, 04 ((MSB)-first, network byte order)

Little-endian: 04, 03, 02, 01 (intel)

 

 

Name resolution

1. symbolic name比numbers-and-dots notation好記。

2. db的來源是/etc/hosts或是dns server。

3. 相關定義在netdb.h。

4. structure:

hostent: 用來表示db中的entry


Data Type

Description

char *h_name

host 的正式名稱 (如www.google.com.tw)

char **h_aliases

該host的別名

int h_addrtype

host 的address type。通常是AF_INET

int h_length

address 的長度,以byte為單位

char **h_addr_list

如果host連到不同的網路,就會有不同的address,這是所有address的pointer

char *h_addr

h_addr_list[0];;就是第一個host address

 

5. 在hostent裡的host address是network byte order。

6. 使用static的buffer,連續呼叫需先將結果存起來。

7. 失敗傳回nul,使用h_errno (用extern  int   h_errno宣告),error code如下:

h_errno

Description

HOST_NOT_FOUND

No such host is known in the data base.

TRY_AGAIN

This condition happens when the name server could not be contacted. If you try again later, you may succeed then.

NO_RECOVERY

A non-recoverable error occurred.

NO_ADDRESS

The host database contains an entry for the name, but it doesn't have an associated Internet address.

 

函數

 

Looking Up a Computer Name


NAME
       gethostbyname() - get network host entry
SYNOPSIS
       #include <netdb.h>
       extern int h_errno;
       struct hostent
       *gethostbyname(const char *name);

 

1. name可以是name / dot decimal address

 

Looking Up a Port Number by Name

NAME
   getservbyname() - get service entry
SYNOPSIS
   #include <netdb.h>
   struct servent *getservbyname(const char *name, const char *proto);
struct servent {
   char  *s_name;
   char  **s_aliases;
   int   s_port;
   char  *s_proto;
}

1. s_port: 是用network byte order表示

 

Looking Up a Protocol by Name

NAME
       getprotobyname() - get protocol entry
SYNOPSIS
       #include <netdb.h>
       struct protoent
       *getprotobyname(const char *name);
struct protoent {
   char  *p_name;
   char  **p_aliases;
   int   p_proto;
}

p_proto: protocol number (可用在socket())

 

 

Reference

byte order convention

 

 

 

 

 

 

 

kezeodsnx 發表在 痞客邦 PIXNET 留言(0) 人氣()