1.TCP/IP理論基礎
2.Linux網絡程式設計
協定棧:Linux的優點之一就是在于它豐富而穩定的網絡協定棧,其範圍是從協定無關層(如通用的socket層接口和裝置層)到各種網絡協定的實作;
協定介紹:對于網絡理論介紹一般采用OSI模型,但是Linux中網絡棧的介紹一般分為四層的Internet模型。
LInux TCP/IP四層概念模型:應用層,傳輸層,網際層,網絡接口。
TCP/IP協定族:TCP/IP 實際上一個協同工作的通信家族,為網絡資料通信提供通路。為讨論友善可TCP/IP 協定組大體上分為三部分:
1、Internet 協定(IP)
2、傳輸控制協定(TCP)和使用者資料報協定(UDP)
3、處于 TCP 和 UDP 之上的一組應用協定。它們包括:TELNET,檔案傳送協定(FTP),域名服務(DNS)和簡單的郵件傳送程式(SMTP)等
網絡層:第一部分稱為網絡層。主要包括Internet 協定(IP)、網際控制封包協定(ICMP)和位址解析協定(ARP)
Internet 協定(IP)
該協定被設計成互聯分組交換通信網,以形成一個網際通信環境。它負責在源主機和目的地主機之間傳輸來自其較高層軟體的稱為資料封包的資料塊,它在源和目的地之間提供非連接配接型傳遞服務
網際控制封包協定(ICMP)
它實際上不是IP層部分,但直接同IP層一起工作,報告網絡上的某些出錯情況。允許網際路由器傳輸差錯資訊或測試封包。
位址解析協定(ARP)
ARP 實際上不是網絡層部分,它處于IP和資料鍊路層之間,它是在32位IP位址和48位實體位址之間執行翻譯的協定。
傳輸層協定:第二部分是傳輸層協定,包括傳輸控制協定和使用者資料封包協定
傳輸控制協定(TCP):
該協定對建立網絡上使用者程序之間的對話負責,它確定程序之間的可靠通信,所提供的功能如下:
1.監聽輸入對話建立請求
2.請求另一網絡站點對話
3.可靠的發送和接收資料
4.适度的關閉對話
使用者資料封包協定(UDP):
UDP 提供不可靠的非連接配接型傳輸層服務,它允許在源和目的地之間傳送資料,而不必在傳送資料之前建立對話。它主要用于那些非連接配接型的應用程式,如:視訊點播
應用協定層“這部分主要包括Telnet,檔案傳送協定(FTP 和TFTP),簡單檔案傳送協定(SMTP)和域名服務(DNS)等協定”。
IP協定:IP主要有以下四個主要功能:
資料傳送
尋址
路由選擇
資料封包的分段
IP的主要目的是為資料輸入/輸出網絡提供基本算法,為高層協定提供無連接配接的傳送服務.這意味着在IP将資料遞交給接收站點以前不在傳輸站點和接收站點之間建立對話。它隻是封裝和傳遞資料,但不向發送者或接收者報告包的狀态,不處理所遇到的故障
IP包由IP協定頭與協定資料兩部分構成
TCP協定:TCP是重要的傳輸層協定,目的是允許資料同網絡上的其他節點進行可靠的交換。它能提供端口編号的譯碼,以識别主機的應用程式,而且完成資料的可靠傳輸TCP 協定具有嚴格的内裝差錯檢驗算法確定資料的完整性TCP 是面向位元組的順序協定,這意味着包内的每個位元組被配置設定一個順序編号,并配置設定給每包一個順序編号。
UDP也是傳輸層協定,它是無連接配接的,不可靠的傳輸服務.當接收資料時它不向發送方提供确認資訊,它不提供輸入包的順序,如果出現丢失包或重份包的情況,也不會向發送方發出差錯封包.由于它執行功能時具有較低的開銷,因而執行速度比TCP快。
socket:Linux中的網絡程式設計通過Socket(套接字)接口實作,Socket是一種檔案描述符
類型:套接字socket有三種類型:
流式套接字(SOCK_STREAM)
流式的套接字可以提供可靠的、面向連 接的通訊流。它使用了TCP協定。TCP保證了資料傳輸的正确性和順序性
資料報套接字(SOCK_DGRAM)
資料報套接字定義了一種無連接配接的服務,資料通過互相獨立的封包進行傳輸,是無序的,并且不
保證可靠,無差錯,它使用資料報協定UDP。
原始套接字
原始套接字允許對低層協定如IP或ICMP直接訪
問,主要用于新的網絡協定的測試等
位元組序轉換:不同類型的 CPU 對變量的位元組存儲順序可能不同:有的系統是高位在前,低位在後,而有的系統是低位在前,高位在後,而網絡傳輸的資料順序是一定要統一的。是以當内部位元組存儲順序和網絡位元組順序不同時,就一定要進行轉換。
網絡位元組順序是TCP/IP中規定好的一種資料表示格式,它與具體的CPU類型、作業系統等無關,進而可以保證資料在不同主機之間傳輸時能夠被正确解釋。
網絡位元組順序采用big endian排序方式。
位址轉換:IP位址通常由數字加點(192.168.0.1)的形式表示,而在struct in_addr中使用的是IP位址是由32位的整數表示的,為了轉換我們可以使用下面兩個函數:
int inet_aton(const char *cp,struct in_addr *inp)
char *inet_ntoa(struct in_addr in)
函數裡面 a 代表 ascii n 代表network.第一個函數表示将a.b.c.d形式的IP轉換為32位的IP,存儲在 inp指針裡面。第二個是将32位IP轉換為a.b.c.d的格式
函數:進行Socket程式設計的常用函數有:
socket
建立一個socket
bind
用于綁定IP位址和端口号到socket
connect
該函數用于綁定之後的client端與伺服器建立連接配接
listen
設定能處理的最大連接配接要求,Listen()并未開始接收連線,隻是設定socket為listen模式。
accept
用來接受socket連接配接。
send
發送資料
recv
接收資料
socket建立:int socket(int family, int type, int protocol);
socket()打開一個網絡通訊端口,如果成功的話,就像open()一樣傳回一個檔案描述符,應用程式可以像讀寫檔案一樣用read/write在網絡上收發資料,如果socket()調用出錯則傳回-1
對于IPv4,family參數指定為AF_INET
對于TCP協定,type參數指定SOCK_STREAM,表示面向流的傳輸協定如果是UDP協定,則type參數指定為SOCK_DGRAM,表示面向資料報的傳輸協定
protocol參數的介紹從略,指定為0即可。
bind綁定:int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
伺服器程式所監聽的網絡位址和端口号通常是固定不變的,用戶端程式得知伺服器程式的位址和端口号後就可以向伺服器發起連接配接,是以伺服器需要調用bind綁定一個固定的網絡位址和端口号.bind()成功傳回0,失敗傳回-1。
bind()的作用是将參數sockfd和myaddr綁定在一起,使 sockfd這個用于網絡通訊的檔案描述符監聽myaddr所描述的位址和端口号
struct sockaddr *是一個通用指針類型,myaddr參數實際上可以接受多種協定的sockaddr結構體,而它們的長度各不相同,是以需要第三個參數addrlen指定結構體的長度。
sockaddr-in初始化:bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT);
首先将整個結構體清零,然後設定位址類型為AF_INET,網絡位址為INADDR_ANY,這個宏表示本地的任意IP位址,因為伺服器可能有多個網卡,每個網卡也可能綁定多個IP位址,這樣設定可以在所有的IP位址上監聽,直到不某個用戶端建立了連接配接時才确定下來到底用哪個IP位址,端口号為SERV_PORT,我們定義為8000。
ACCEPT連結:int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
三方插手完成後,伺服器調用accept()接受連接配接,如果伺服器調用accept()時還沒有用戶端的連接配接請求,就阻塞等待直到有用戶端連接配接上來,cliaddr是一個傳出參數,accept()傳回時傳出用戶端的位址和端口号.addrlen參數是一個傳入傳出參數(value-result argument),傳入的是調用者提供的緩沖區cliaddr的長度以避免緩沖區溢出問題,傳出的是用戶端位址結構體的實際長度(有可能沒有占滿調用者提供的緩沖區)。
如果給cliaddr參數傳NULL,表示不關心用戶端的位址。
Coonect連接配接:int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
用戶端需要調用connect()連接配接伺服器,connect和bind的參數形式一緻,差別在于bind的參數是自己的位址,而connect的參數是對方的位址.
connect()成功傳回0,出錯傳回-1。
基于TCP伺服器:
1. 建立一個socket,用函數socket()
2. 綁定IP位址、端口等資訊到socket上,用函數bind()
3.設定允許的最大連接配接數,用函數listen()
4.接收用戶端上來的連接配接,用函數accept()
5.收發資料,用函數send()和recv(),或者read()和write()
6.關閉網絡連接配接
基于TCP用戶端:
1.建立一個socket,用函數socket()
2.設定要連接配接的對方的IP位址和端口等屬性
3.連接配接伺服器,用函數connect()
4.收發資料,用函數send()和recv(),或者
read()和write()
5.關閉網絡連接配接
基于UDP伺服器;
1.建立一個socket,用函數socket()
2.綁定IP位址、端口等資訊到socket上,用函數bind()
3.循環接收資料,用函數recvfrom()
4.關閉網絡連接配接
基于UDP用戶端:
1.建立一個socket,用函數socket()
2.綁定IP位址、端口等資訊到socket上,用函數bind()
3.設定對方的IP位址和端口等屬性
4.發送資料,用函數sendto()
5.關閉網絡連接配接
伺服器模型:在網絡程式裡面,一般來說都是許多客戶對應一個伺服器,為了處理客戶的請求, 對服務端的程式就提出了特殊的要求。目前最常用的伺服器模型有:
循環伺服器:伺服器在同一個時刻隻可以響應一個用戶端的請求
并發伺服器:伺服器在同一個時刻可以響應多個用戶端的請求
TCP循環伺服器:TCP循環伺服器一次隻能處理一個用戶端的請求。隻有在這個客戶的所有請求都滿足後, 伺服器才可以繼續後面的請求。這樣如果有一個用戶端占住伺服器不放時,其它的客戶機都不能工作了,是以,TCP伺服器一般很少用循環伺服器模型的。
TCP并發伺服器:并發伺服器的思想是每一個客戶機的請求并不由伺服器直接處理,而是由伺服器建立一個 子程序來處理。
TCP并發伺服器可以解決TCP循環伺服器客戶機獨占伺服器的情況。但同時也帶來了問題:為了響應客戶的請求,伺服器要建立子程序來處理,而建立子程序是一種非常消耗資源的操作