--- ###### tags: `linux2022` --- # CS:APP 第 11 章 contributed by < `eric88525` > [鳥哥網路介紹,建議先看](https://linux.vbird.org/linux_server/centos6/0110network_basic.php) [video: network programming: Part 1 ](https://scs.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=54178cf8-d57e-4984-b46c-b66db645431a) # 1. Overview + 大部分的網路架構都是 client-server 架構 ![](https://i.imgur.com/Pq5T1zx.png) + network 是透過有限或是無線相連的系統 + SAN (system area network) + LAN (local area network) + WAN (wide area network ) + **i**nternet vs **I**nternet + 小寫的 i 代表互相連接的網路 + Golbal IP Internet 是最有名的 internet # 2. Network + [Hub(集線器)、Bridge(橋接器)、Switch(交換器)與Router(路由器)](https://giboss.pixnet.net/blog/post/26798642) + [鳥哥 linux 私藏 / 共不共享很重要,集線器還是交換器? ](https://linux.vbird.org/linux_server/centos6/0110network_basic.php) + 最大的差異在於集線器 hub 封包都是可見的,交換器是會單獨傳送 + [router switch hub](https://tung168.pixnet.net/blog/post/330937279-%E7%B6%B2%E8%B7%AF%E8%A8%AD%E5%82%99hub%E3%80%81switch%E5%92%8Crouter%E7%9A%84%E5%B7%AE%E7%95%B0) ## lowest level: Ethernet Segment + 用於房間或是一層樓 + each ethernet adapter has 48-bit address(MAC address) + host 間傳遞 bit 資料的單位為 **frames** + hub 很懶惰,every host sees every bit ```graphviz graph{ node[shape=box] host1--hub host2--hub [label=" port"] } ``` ## Bridged Ethernet Segment + 一堆的 ethernet 可以連成 LAN + bridge 知道要把資料傳給哪個 host + 用於學校或公司 ![](https://i.imgur.com/VbVfmsi.png) # 3. internets (lower case) 透過 router 來連接多個 LAN (這些 LAN 未必相同架構) ![](https://i.imgur.com/hP6J8Tk.png) ## internet protocol + 提供了 naming scheme (命名規則),如 host address + 每個 host 都有他專屬的 address 來識別 + 定義了傳遞機制,基本單位為 packet + packet 含有 **header**(packet 資訊) 和 **payload**(資料) 在此架構下的傳遞情況 + PH: internet packet header + FH: LAN frame header ![](https://i.imgur.com/kLpemg3.png) # 4. Global IP Internet + 最有名的 internet + base on TCP/IP + IP (Internet Protocol): provieds **basic naming scheme** and unrealiable delivery capability of packets from **host-to-host** + UDP (Unreliable datagram protocol): **process-to-process** 但不穩定 + TCP (transmission control protocol): 透過 IP 來進行可靠的 **process-to-process** 傳輸 ## hardware and software organization of an internet application ![](https://i.imgur.com/GcglsFx.png) + 站在程式的角度 + Hosts 映射到 32-bit **IP address** + The set of **IP addresses** is mapped to set of identifiers called internet **domain names** + 不同 host 的 process 可透過 **connection** 來溝通 ## IPv4 and IPv6 + 32 bit-ipv4 不夠用了,因此有 128 bit ipv6 替代 + IP address 被存在 IP address struct,格式為 **network byte order** ```c /* Internet address structure */ struct in_addr { uint32_t s_addr; /*network byte order (big-endian)*/ } ``` Network byte-order conversion functions l: 32bits s: 16bit h: host n: network ```c htonl // convert uint32_t from [host] to [network] byte order htons // convert uint16_t from [host] to [network] byte order ntohl // convert uint32_t from [network] to [host] byte order ntohs // convert uint16_t from [network] to [host] byte order ``` Function of converting between **binary IP address** and **dotted decimal strings** "n": network, "p" presentation ```c inet_pton: dotteed deciaml string -> IP address in network byte order inet_ntop: IP address in network byte order -> dotteed deciaml string ``` ## internet domain name > Q: 要如何將 domain name 對應到 ip 呢? > A: Internet 會維護 IP address <-> domain name 的對應,這些儲存在 **huge worldwide distributed database** called **DNS** ![](https://i.imgur.com/SlfdfbZ.png) 查詢 DNS 可透過 `nslookup` 來查找 ```c $ nslookup www.google.com Server: 127.0.0.53 Address: 127.0.0.53#53 Non-authoritative answer: Name: www.google.com Address: 74.125.200.105 Name: www.google.com Address: 74.125.200.106 Name: www.google.com Address: 74.125.200.104 Name: www.google.com Address: 74.125.200.99 Name: www.google.com Address: 74.125.200.147 Name: www.google.com Address: 74.125.200.103 Name: www.google.com Address: 2404:6800:4012:2::2004 ``` ## Internet connections client 和 server 可透過 `connections` 互相傳輸 bytes stream , `connection` 的特性 + point-to-point + Full-duplex: 雙向的 + Reliable: 資料順序不會變動 A **socket** is an endpoint of a connection + Socket address = **IPaddress:port** A **port** is a 16-bit interger that identifies a process * Ephemeral(臨時) port: 當 client 連線時自動決定 * Well-know port: 提供特殊服務,對應表放在 `/etc/services` * 7/ echo * 22/ ssh * 25/ smtp ![](https://i.imgur.com/rRpb0Gh.png) ![](https://i.imgur.com/EMkv7E0.png) # 5. socket + 對於 kernel, sokcet 是連線的終點 + 對於 application 來說他就是一個 file descriptor 可以 read/write,在 linux 中萬物皆檔案 ![](https://i.imgur.com/CAYMH4o.png) 如果使用 **Internet-specific** socket,最後要轉形成 (struct sockaddr \*)。 當初會這樣設計是希望能有個 interface 能兼容所有種類的 socket address structure。(from book p902) :+1: The _in suffix is short for internet, not input. ```c /* Generic socket address structure (for connect, bind, and accept) */ struct sockaddr{ unsigned short sa_family; /* Protocol family */ char sa_data[14]; /* Address data */ }; /* Internet-style socket address structure */ struct sockaddr_in { unsigned short sin_family; /* Address family (always AF_INET)*/ unsigned short sin_port; /* Port number in network byte order */ struct in_addr sin_addr; /* IP address in network byre order */ unsigned char sin_zero[8]; /* Pad to sizeof (struct sockaddr)*/ }; ``` ![](https://i.imgur.com/RJ1Fnxm.png) # 6. 程式介紹 ## socket 用於 client 和 server 建立 socket。 ```c /* socket - create an endpoint for communication * @domain: protocol * @type: communication semantic * @protocol: The protocol specifies a particular protocol to be used with the socket. * */ int socket(int domain, int type, int protocol); /* example */ int clientfd = socket(AD_INET, SOCK_STREAM, 0) ``` ## connect 用於 client ,建立和 server 的連線,成功的話 sockfd 就能進行 read / write, ```c #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); ``` ## bind + 用於 server 端 + The bind function tells the kernel to associate the server’s socket address in my_addr with the socket descriptor sockfd. ```c #include <sys/socket.h> int bind(int sockfd, struct sockaddr *my_addr, int addrlen); ``` ## listen + 相較於 client 是主動連線, server 是等待連線。 + 此函式將 sockfd 從 **active socket** 轉成具有接受連線能力的 **listening socket** + backlog 為提示 kernel 預計會有多少個連線,要先準備好 ```c #include <sys/socket.h> /* Returns: 0 if OK, −1 on error */ int listen(int sockfd, int backlog); ``` ## open_listenfd 結合了 `socket`, `bind`, `listen` 的 function ```c #include "csapp.h" /* Returns: descriptor if OK, −1 on Unix error */ int open_listenfd(int port); ``` ## open_clientfd 把 socket 和 connect 包在一起的實做 ```c #include "csapp.h" /* Returns: descriptor if OK, −1 on Unix error, −2 on DNS error */ int open_clientfd(char *hostname, int port); ``` ```c /* * open_clientfd - open connection to server at <hostname, port> * and return a socket descriptor ready for reading and writing. * Returns -1 and sets errno on Unix error. * Returns -2 and sets h_errno on DNS (gethostbyname) error. */ typedef struct sockaddr SA; int open_clientfd(char *hostname, int port) { int clientfd; struct hostent *hp; struct sockaddr_in serveraddr; if ((clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1; /* check errno for cause of error */ /* Fill in the server's IP address and port */ if ((hp = gethostbyname(hostname)) == NULL) return -2; /* check h_errno for cause of error */ bzero((char *) &serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; bcopy((char *) hp->h_addr_list[0], (char *) &serveraddr.sin_addr.s_addr, hp->h_length); serveraddr.sin_port = htons(port); /* Establish a connection with the server */ if (connect(clientfd, (SA *) &serveraddr, sizeof(serveraddr)) < 0) return -1; return clientfd; } ``` ## accept Servers wait for connection requests from clients by calling the accept function ```c #include <sys/socket.h> /* accept - accept a connection on a socket */ int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict addrlen); ``` ![](https://i.imgur.com/aLEHRtO.png) ## Example code client 端 ```c /* * echoclient.c - An echo client */ /* $begin echoclientmain */ #include "csapp.h" int main(int argc, char **argv) { int clientfd; char *host, *port, buf[MAXLINE]; rio_t rio; if (argc != 3) { fprintf(stderr, "usage: %s <host> <port>\n", argv[0]); exit(0); } host = argv[1]; port = argv[2]; clientfd = Open_clientfd(host, port); Rio_readinitb(&rio, clientfd); while (Fgets(buf, MAXLINE, stdin) != NULL) { Rio_writen(clientfd, buf, strlen(buf)); Rio_readlineb(&rio, buf, MAXLINE); Fputs(buf, stdout); } Close(clientfd); //line:netp:echoclient:close exit(0); } /* $end echoclientmain */ ``` Server 端 ```c /* * echoserveri.c - An iterative echo server */ #include "csapp.h" /* * echo - read and echo text lines until client closes connection */ void echo(int connfd) { size_t n; char buf[MAXLINE]; rio_t rio; Rio_readinitb(&rio, connfd); while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) { //line:netp:echo:eof printf("server received %d bytes\n", (int)n); Rio_writen(connfd, buf, n); } } int main(int argc, char **argv) { int listenfd, connfd; socklen_t clientlen; struct sockaddr_storage clientaddr; /* Enough space for any address */ //line:netp:echoserveri:sockaddrstorage char client_hostname[MAXLINE], client_port[MAXLINE]; if (argc != 2) { fprintf(stderr, "usage: %s <port>\n", argv[0]); exit(0); } listenfd = Open_listenfd(argv[1]); while (1) { clientlen = sizeof(struct sockaddr_storage); connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen); Getnameinfo((SA *) &clientaddr, clientlen, client_hostname, MAXLINE, client_port, MAXLINE, 0); printf("Connected to (%s, %s)\n", client_hostname, client_port); echo(connfd); Close(connfd); } exit(0); } ``` ## getaddrinfo > Given node and service, which identify an Internet host and a service, getaddrinfo() returns one or more addrinfo structures, each of which contains an Internet address that can be specified in a call to bind(2) or connect(2). ```c #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> /* Given node and service, which identify an Internet host and a service, * getaddrinfo() returns one or more addrinfo structures, each of which * contains an Internet address that can be specified in a call to * bind(2) or connect(2). */ int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); void freeaddrinfo(struct addrinfo *res); const char *gai_strerror(int errcode); ``` Struct of addrinfo returned by getaddrinfo ```c struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; struct sockaddr *ai_addr; char *ai_canonname; struct addrinfo *ai_next; }; ``` 前面提到 `nslookup` 可以找 domain 對應許多ip, getaddrinfo 回傳的 linked list 長這樣 ![](https://i.imgur.com/mS0oopg.png) `getnameinfo` 則是相反,將 socket address 轉成對應的 host 和 service ```c #include <sys/socket.h> #include <netdb.h> int getnameinfo(const struct sockaddr *restrict addr, socklen_t addrlen, char *restrict host, socklen_t hostlen, char *restrict serv, socklen_t servlen, int flags); ``` # Web Servers + web client 和 server 使用 text-based application-level protocol **HTTP(Hypertext transfer protocol)** 溝通。 ```graphviz digraph{ node [shape=box] label="HTTP protocol" client->server [label="request"] server->client [label = "content"] rankdir="LR" } ``` ## web content + content is a sequence of bytes with an associated MIME (Multipurpose Internet Mail Extensions) type + content 分為 static 和 dynamic content,差別在於有沒有需要處理資料