《TCP/IP網絡編程》讀書筆記

1.Windows 下的 socket 程序和 Linux 思路相同,但細節有所差異
(1) Windows 下的 socket 程序依賴 Winsock.dll 或 ws2_32.dll,必須提早加載。DLL 有兩種加載方式,請查看:動態連接庫DLL的加載
(2) Linux 使用「文件描述符」的概念,而 Windows 使用「文件句柄」的概念;Linux 不區分 socket 文件和普通文件,而 Windows 區分;
(3) Linux 下 socket() 函數的返回值爲 int 類型,而 Windows 下爲 SOCKET 類型,也就是句柄。
(4) Linux 下使用 read() / write() 函數讀寫,而 Windows 下使用 recv() / send() 函數發送和接收。
(5) 關閉 socket 時,Linux 使用 close() 函數,而 Windows 使用 closesocket() 函數。

2.分配給標準輸入標準輸出及標準錯誤輸出的文件描述符
文件描述符       對象
 0                  標準輸出
 1                  標準輸出
 2                  標準錯誤
 
3.WinSock(Windows Socket)編程依賴於系統提供的動態連接庫(DLL),有兩個版本:  
較早的DLL是 wsock32.dll,大小爲 28KB,對應的頭文件爲 winsock1.h;
最新的DLL是 ws2_32.dll,大小爲 69KB,對應的頭文件爲 winsock2.h。
使用#pragma命令,在編譯時加載:
 #pragma comment (lib, "ws2_32.lib")
WinSock 編程的第一步就是加載 ws2_32.dll,而後調用 WSAStartup() 函數進行初始化,並指明要使用的版本號
WSADATA wsaData;
WSAStartup( MAKEWORD(2, 2), &wsaData);

 

4. socket編程算法

(1) 使用socket()函數建立套接字
(2) 使用bind()函數將套接字與特定的IP地址和端口綁定起來,只有這樣,流經該IP地址和端口的數據才能交給套接字處理
(3) 使用connect()函數來創建鏈接
(4) sockaddr結構體
能夠認爲,sockaddr 是一種通用的結構體,能夠用來保存多種類型的IP地址和端口號,而 sockaddr_in 是專門用來保存 IPv4 地址的結構體
另外還有 sockaddr_in6,用來保存 IPv6 地址
(5) 使用listen()函數讓套接字進入被動監聽狀態
(6) 使用accept()函數能夠隨時響應客戶端的請求
(7) accept() 返回一個新的套接字來和客戶端通訊,addr 保存了客戶端的IP地址和端口號
(8) listen() 只是讓套接字進入監聽狀態,並無真正接收客戶端請求,listen() 後面的代碼會繼續執行,直到遇到accept()
(9) accept() 會阻塞程序執行(後面代碼不能被執行),直到有新的請求到來。
(10) TCP服務器端函數調用順序:socket()->bind()->listen()->accept()->read()/write()->close()
(11) TCP客戶端函數調用順序:socket()->connect()->read()/write()->close()

5.Ack號 = Seq號 + 傳遞的字節數 + 1
 
6.
 (1) 調用 close()/closesocket() 函數意味着徹底斷開鏈接,即不能發送數據也不能接收數據
 (2) 使用 shutdown() 函數能夠只斷開一條數據傳輸通道,而保留另外一條
int shutdown(int sock, int howto);  //Linux
int shutdown(SOCKET s, int howto);  //Windows

sock 爲須要斷開的套接字,howto 爲斷開方式編程

howto 在 Linux 下有如下取值:
  • SHUT_RD:斷開輸入流。套接字沒法接收數據(即便輸入緩衝區收到數據也被抹去),沒法調用輸入相關函數。
  • SHUT_WR:斷開輸出流。套接字沒法發送數據,但若是輸出緩衝區中還有未傳輸的數據,則將傳遞到目標主機。
  • SHUT_RDWR:同時斷開 I/O 流。至關於分兩次調用 shutdown(),其中一次以 SHUT_RD 爲參數,另外一次以 SHUT_WR 爲參數。
howto 在 Windows 下有如下取值:
  • SD_RECEIVE:關閉接收操做,也就是斷開輸入流。
  • SD_SEND:關閉發送操做,也就是斷開輸出流。
  • SD_BOTH:同時關閉接收和發送操做。
shutdown() 用來關閉鏈接,而不是套接字,無論調用多少次 shutdown(),套接字依然存在,直到調用 close() / closesocket() 將調用 close()/closesocket() 關閉套接字時,或調用 shutdown() 關閉輸出流時,都會向對方發送 FIN 包。FIN 包表示數據傳輸完畢,計算機收到 FIN 包就知道不會再有數據傳送過來了
 (3) 默認狀況下,close()/closesocket() 會當即向網絡中發送FIN包,無論輸出緩衝區中是否還有數據,而shutdown() 會等輸出緩衝區中的數據傳輸完畢再發送FIN包, 也就意味着,調用 close()/closesocket() 將丟失輸出緩衝區中的數據,而調用 shutdown() 不會。
 
7. recv() 返回 0 的惟一時機就是收到FIN包時
 
8. 網絡字節序
不一樣 CPU 保存和解析數據的方式不一樣(主流的 Intel 系列 CPU 爲小端序),小端序系統和大端序系統通訊時會發生數據解析錯誤。所以在發送數據前,要將數據轉換爲統一的格式——網絡字節序(Network Byte Order)。網絡字節序統一爲大端序。
 
9.
ping 域名能夠查看域名對應的IP地址
nslookup命令能夠查看計算機中註冊的默認DNS服務器地址
 
10.
 (1) 下列函數能夠經過傳遞字符串格式的域名獲取IP地址
#include <netdb.h>
struct hostent *gethostbyname(const char *hostname);

 成功時返回hostnet結構體指針,失敗時返回NULL指針服務器

 
struct hostnet
{
  char *h_name;
  char **h_aliases;
  int h_addrtype;
  int h_lenght;
  char **h_addr_list;
}

 (2) gethostbyaddr()函數利用IP地址獲取域相關信息網絡

#include <netdb.h>
struct hostnet *gethostbyaddr(const char *addr, socklen_t len, int fanily);

 成功時返回hostnet結構體變量地址值,失敗時返回NULL指針socket

 addr:含有IP地址信息的in_addr結構體指針
 len:  向第一個參數傳遞的地址信息的字節數,IPv4時爲4,IPv6時爲6
 family: 傳遞地址族信息,IPv4時爲AF_INET, IPv6時爲AF_INET6
 (3)字節序轉換函數
 unsigned short htons(unsigned short);
 unsigned short ntohs(unsigned short);
 unsigned long htonl(unsigned long);
 unsigned long ntohl(unsigned long);

 (4) 將字符串信息轉換爲網絡字節序的整數型函數

 #include <arpa/inet.h>
 in_addr_t inet_addr(const char *string);

  成功時返回32位大端序整數型值,失敗時返回INADDR_NONEspa

 (5) inet_aton()也將字符串形式IP地址轉換位32位網絡字節序整數並返回,只不過該函數利用in_addr結構體,且其使用頻率跟高
 
#include <arpa/inet.h>
int inet_aton(const char *string, struct in_addr *addr);

  成功時返回1(true),失敗時返回0(false)指針

 (6) inet_ntoa()函數能夠把網絡字節序整數型IP地址轉換成字符串形式
 #include <arpa/inet.h>
 char *inet_ntoa(struct in_addr adr);

  成功時返回轉換的字符串地址值,失敗時返回-1;code

        調用完該函數後應當即將字符串信息複製到其餘內存空間,由於,若再次調用inet_ntoa函數,則有可能覆蓋以前保存的字符串信息
 
 (7) 網絡地址信息初始化方法:
 
struct sockaddr_in addr;
char *serv_ip = "211.217.168.13";
char *serv_port "9190";
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(serv_ip);
addr.sin_port = htons(atoi(serv_port));

 利用常數INADDR_ANY分配服務器的IP地址,能夠自動獲取運行服務器端的計算機IP地址對象

 addr.sin_addr.s_addr = htonl(INADDR_ANY);
11.套接字的可選項
getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen)
  • SO_SNDBUF:輸入緩衝大小相關可選項
  •  SO_RCVBUF:輸出緩衝大小相關可選項
  •  SO_REUSEADDR:該可選項設置爲TRUE可將Time_wait狀態下的套接字端口號從新分配給新的套接字
 TCP_NODELAY設置爲1可禁用Nagle算法
 
12.多進程
 (1) 經過調用fork函數建立進程
 #include <unistd.h>
 pid_t fork(void);

 父進程:fork函數返回子進程的ID

 子進程:fork函數返回0
(2) 應當向建立子進程的父進程傳遞子進程的exit參數值或return語句的返回值,防止殭屍進程的產生
 銷燬殭屍進程1:利用wait函數
#include <unistd.h>
pid_t wait(int *statloc);

 ->成功時返回終止的子進程ID,失敗時返回-1

 子進程終止時傳遞的返回值將保存到statloc所指內存空間,須要用下列宏進行分離
  •  WIFEXITED 子進程正常終止時返回「真」true
  •  WEXITSTATUS 返回子進程的返回值
 銷燬殭屍進程2:利用waitpid函數
 #include <sys/wait.h>
 pid_t waitpid(pid_t pid, int *statloc, int options);

 ->成功時返回終止的子進程ID,失敗時返回-1

 pid 等待終止的目標子進程ID,若傳遞-1,則能夠等待任意子進程終止

 (3) statloc與wait函數的statloc參數具備相同含義
 (4) options 傳遞sys/wait.h中聲明的常量WNOHANG,即便沒有終止的子進程也不會進入組賽狀態,而是返回0並退出函數
 (5)信號與signal函數
 #include <signal.h>
 void (*signal(int signal, void (*func)(void)))(int);

 ->爲了在產生信號時調用,返回以前註冊的函數指針

 (6) 利用sigaction函數進行信號處理
 #include <signal.h>
 int sugacyion(int signo, const struct sigaction *act, struct sigaction *oldact);

 ->成功時返回0,失敗時返回-1

 經過fork函數複製套接字文件描述符後,同一端口將對應多個套接字,只有這些套接字描述符都終止,才能銷燬套接字
 
13.進程間通訊
 建立管道的函數:
 #include <unistd.h>
 int pipe(int filedes[2]);

 ->成功時返回0,失敗時返回-1

 filedes[0]:經過管道接收數據時使用的文件描述符,即管道出口
 filedse[1]:經過管道傳輸數據時使用的文件描述符,即管道入口
 
14.I/O複用
 (1) 針對fd_set變量的操着的宏:
 FD_ZERO(fd_set *fdset)
 FD_SET(int fd, fd_set *fdset)
 FD_CLR(int fd, fd_set *fdset)
 FD_ISSET(fint fd, d_set *fdset)

(2) select函數:

 #include <sys/select.h>
 #include <sys/time.h>
 int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);

 

15.多種I/O函數

 (1) 收到MSG_OOB緊急消息時,操着系統將產生SIGURG消息,並調用註冊的信號處理函數
 (2) 處理SIGURG信號時必須指定處理信號的進程,而geipid函數返回調用此函數的進程ID
 (3) fcntl函數用於控制文件描述符
 
fcntl(recv_sock, F_SETOWN, getpid());

 上述調用的含義是「將文件描述符recv_sock指向的套接字擁有者(F_SETOWN)改成把getpid函數返回值用做ID的進程

 (4) 緊急指針指向緊急消息的下一個位置(偏移量+1),緊急消息的意義在於督促消息處理,而非緊急傳輸形式受限的消息  (5) 調用recv函數的同時傳遞MSG_PEEK可選項,是爲了保證即便不存在待讀取的數據也不會進入阻塞狀態,設置MSG_PEEK選項並調用recv函數時,即便讀取了輸入緩衝的數據也不會刪除,該選項一般與MSG_DONTWAIT合做,用於調用以非阻塞方式驗證待讀取數據存在與否的函數
相關文章
相關標籤/搜索