网络编程01-TCP协议(详)
生活随笔
收集整理的這篇文章主要介紹了
网络编程01-TCP协议(详)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
目錄
一、網絡編程學習大綱
二、回顧系統編程中進程的通信方式
1、 管道
2、信號
3、IPC對象
三、網絡編程(套接字編程)
1、特點
2、協議
3、歷史
四、網絡應用程序設計模式
1、C/S模式
2、B/S模式
3、優缺點
五、計算機網絡體系結構模型
1、概念
2、作用
3、分類
4、OSI參考模型(七層模型)
5、TCP/IP協議模型(四層)
六、TCP/IP網絡模型通信過程
1、兩臺計算機通過TCP/IP協議通訊的過程
2、數據包封裝
3、以太網幀格式
4、ARP數據報格式
5、IP段格式
6、TCP數據報格式
七、傳輸層協議
TCP協議(打電話)
UDP協議(信)
八、網絡編程中幾個重要概念
1、socket --> 插座,套接字 插座的種類繁多,就像很多個協議一樣,必須提前設置好協議。
?2、IP地址
3、端口號(16位)
4、字節序
九、TCP通信
1、通信過程
2、客戶端API
1、建立套接字
2、發起連接 -撥打電話
3、發送數據-------write send
4、關閉文件--close
服務器端API:
1、綁定 bind
2、設置鈴聲
3、等待客戶端的連接 --accept
4、接收數據 recv
練習3: 修改代碼,實現TCP客戶端與TCP服務器互相可以收跟發。
練習4: 寫一個回射服務器,這個服務器功能就是客戶端發送什么數據給服務器,服務器都會將數據回發到客戶端。
一、網絡編程學習大綱
1、計算機網絡體系結構模型、網絡編程專業術語(socket/IP/端口號)、通信時序圖 2、傳輸層協議: TCP協議/UDP協議。 3、多進程并發服務器、多線程并發服務器 4、網絡編程IO模型:阻塞IO/非阻塞IO/多路復用/信號驅動 5、超時接收數據方法二、回顧系統編程中進程的通信方式
1、 管道
無名管道(只能作用親緣關系) -- pipe() 有名管道(任意兩個進程) --- mkfifo()2、信號
發送信號 -- kill() 捕捉信號 -- signal()3、IPC對象
消息隊列---接收特征類型的數據 -- ftok() msgget() msgsnd() msgrcv() msgctl() 共享內存 --> 雙方進程可以同時對一片內存進行讀寫 shmget() shmat() shmdt() shmctl() 信號量 --> 不屬于通信方式,只是一種互斥的量 semget() semop() semctl() 特點: 只能在同一臺主機上內部通信,不能跨平臺。三、網絡編程(套接字編程)
1、特點
既可以在同一臺主機上內部通信,也可以在不同主機之間通信。 自己的ubuntu ----自己的ubuntu 自己的ubuntu -----同一個局域網內除了自己之外任意一臺主機 總結一下: 網絡通信前提: 只要你在某個局域網內,就可以與局域網任意一臺主機通信。2、協議
1)概念應用的角度出發,在不同的主機之間通信,雙方都必須遵循的同一種規則。協議可理解為“規則”, 是數據傳輸和數據的解釋的規則。 假設,A、B雙方欲傳輸文件。 規定: 第一次,傳輸文件名,接收方接收到文件名,應答OK給傳輸方; 第二次,發送文件的尺寸,接收方接收到該數據再次應答一個OK; 第三次,傳輸文件內容。同樣,接收方接收數據完成后應答OK表示文件內容接收成功。 由此,無論A、B之間傳遞何種文件,都是通過三次數據傳輸來完成。A、B之間形成了一個最簡單的數 據傳輸規則。雙方都按此規則發送、接收數據。A、B之間達成的這個相互遵守的規則即為協議。 這種僅在A、B之間被遵守的協議稱之為原始協議。當此協議被更多的人采用,不斷的增加、改進、維 護、完善。最終形成一個穩定的、完整的文件傳輸協議,被廣泛應用于各種文件傳輸過程中。該協議 就成為一個標準協議。最早的ftp協議就是由此衍生而來。 TCP協議注重數據的傳輸。http協議著重于數據的解釋。 2)常見的協議 傳輸層 常見協議有TCP/UDP協議。 應用層 常見的協議有HTTP協議,FTP協議。 網絡層 常見協議有IP協議、ICMP協議、IGMP協議。 網絡接口層 常見協議有ARP協議、RARP協議。 TCP傳輸控制協議(Transmission Control Protocol)是一種面向連接的、可靠的、基于字節流的傳輸層通信協議。 UDP用戶數據報協議(User Datagram Protocol)是OSI參考模型中一種無連接的傳輸層協議,提供面向事務的簡單不可靠信息傳送服務。 HTTP超文本傳輸協議(Hyper Text Transfer Protocol)是互聯網上應用最為廣泛的一種網絡協議。 FTP文件傳輸協議(File Transfer Protocol) IP協議是因特網互聯協議(Internet Protocol) ICMP協議是Internet控制報文協議(Internet Control Message Protocol)它是TCP/IP協議族的一 個子協議, 用于在IP主機、路由器之間傳遞控制消息。 IGMP協議是 Internet 組管理協議(Internet Group Management Protocol),是因特網協議家族中的一個組 播協議。該協議運行在主機和組播路由器之間。 ARP協議是正向地址解析協議(Address Resolution Protocol),通過已知的IP,尋找對應主機的MAC地址。 RARP是反向地址轉換協議,通過MAC地址確定IP地址。3、歷史
ARPAnet(阿帕網): 1)歷史: 1958年美國總統艾森豪威爾向美國國會提出建立DARPA (Defense Advanced Research Project Agency),即國防部高級研究計劃署,簡稱ARPA。1968年6月DARPA提出“資源共享計算機網絡” (Resource Sharing Computer Networks),目的在于讓DARPA的所有電腦互連起來,這個網絡就叫做ARPAnet。 2)使用的協議:網絡控制協議(Network Control Protoco,NCP)3) 缺點:不能互聯不同類型的計算機 和 不同類型的操作系統,同時也沒有糾錯功能 Internet(因特網): 1)由于ARPAnet網絡的局限性,引入了TCP/IP協議。TCP/IP是Internet上所有網絡和主機之間進行交 流的標準連接協議。通常所說的TCP/IP協議實際上包含了大量的協議和應用,且由多個獨立定義的協議組合在一起,因此,更確切地說,應該稱之為TCP/IP協議簇。 TCP/IP協議: 傳輸控制協議/因特網互聯協議 TCP協議: 用于檢測網絡中傳輸差錯 IP協議: 負責不同的網絡之間的通信 通俗講: TCP負責發送傳輸問題,一旦有問題發出信號,要求重新傳輸,直到數據安全到達對方為止。 IP給每一臺聯網設備規定一個地址四、網絡應用程序設計模式
1、C/S模式
傳統的網絡應用設計模式,客戶機(client)/服務器(server)模式。需要在通訊兩端各自部署客戶機和服務器來完成數據通信。2、B/S模式
瀏覽器(browser)/服務器(server)模式。只需在一端部署服務器,而另外一端使用每臺PC都默認配置的瀏覽器即可完成數據的傳輸。3、優缺點
對于C/S模式來說,其優點明顯。客戶端位于目標主機上可以保證性能,將數據緩存至客戶端本地,從而提高數據傳輸效率。且,一般來說客戶端和服務器程序由一個開發團隊創作,所以他們之間所采用 的協議相對靈活。可以在標準協議的基礎上根據需求裁剪及定制。例如,騰訊公司所采用的通信協 議,即為ftp協議的修改剪裁版。 因此,傳統的網絡應用程序及較大型的網絡應用程序都首選C/S模式進行開發。如,知名的網絡游戲魔 獸世界。3D畫面,數據量龐大,使用C/S模式可以提前在本地進行大量數據的緩存處理,從而提高觀感。 C/S模式的缺點也較突出。由于客戶端和服務器都需要有一個開發團隊來完成開發。工作量將成倍提 升,開發周期較長。另外,從用戶角度出發,需要將客戶端安插至用戶主機上,對用戶主機的安全性構成威脅。這也是很多用戶不愿使用C/S模式應用程序的重要原因。 B/S模式相比C/S模式而言,由于它沒有獨立的客戶端,使用標準瀏覽器作為客戶端,其工作開發量較小。只需開發服務器端即可。另外由于其采用瀏覽器顯示數據,因此移植性非常好,不受平臺限制。如早期的偷菜游戲,在各個平臺上都可以完美運行。 B/S模式的缺點也較明顯。由于使用第三方瀏覽器,因此網絡應用支持受限。另外,沒有客戶端放到對方主機上,緩存數據不盡如人意,從而傳輸數據量受到限制。應用的觀感大打折扣。第三,必須與瀏覽器一樣,采用標準http協議進行通信,協議選擇不靈活。因此在開發過程中,模式的選擇由上述各自的特點決定。根據實際需求選擇應用程序設計模式。五、計算機網絡體系結構模型
1、概念
指的是主機內部集成的結構和每層協議的集合。每臺主機本身就存在一個相同的網絡體系結構。2、作用
封裝數據和解析數據。3、分類
OSI(Open System Interconnection開放系統互聯)參考模型 TCP/IP參考模型4、OSI參考模型(七層模型)
1.物理層:主要定義物理設備標準,如網線的接口類型、光纖的接口類型、各種傳輸介質的傳輸速率 等。它的主要作用是傳輸比特流(就是由1、0轉化為電流強弱來進行傳輸,到達目的地后再轉化為1、0,也就是我們常說的數模轉換與模數轉換)。這一層的數據叫做比特。 2.數據鏈路層:定義了如何讓格式化數據以幀為單位進行傳輸,以及如何讓控制對物理介質的訪問。這 一層通常還提供錯誤檢測和糾正,以確保數據的可靠傳輸。如:串口通信中使用到的115200、8、N、1 3.網絡層:在位于不同地理位置的網絡中的兩個主機系統之間提供連接和路徑選擇。Internet的發展使 得從世界各站點訪問信息的用戶數大大增加,而網絡層正是管理這種連接的層。 4.傳輸層:定義了一些傳輸數據的協議和端口號(WWW端口80等),如:TCP(傳輸控制協議,傳輸 效率低,可靠性強,用于傳輸可靠性要求高,數據量大的數據),UDP(用戶數據報協議,與TCP特性恰恰相反,用于傳輸可靠性要求不高,數據量小的數據,如QQ聊天數據就是通過這種方式傳輸的)。 主要是將從下層接收的數據進行分段和傳輸,到達目的地址后再進行重組。常常把這一層數據叫做段。 5.會話層:通過傳輸層(端口號:傳輸端口與接收端口)建立數據傳輸的通路。主要在你的系統之間發起會話或者接受會話請求(設備之間需要互相認識可以是IP也可以是MAC或者是主機名)。 6.表示層:可確保一個系統的應用層所發送的信息可以被另一個系統的應用層讀取。例如,PC程序與另一臺計算機進行通信,其中一臺計算機使用擴展二一十進制交換碼(EBCDIC),而另一臺則使用美國信息交換標準碼(ASCII)來表示相同的字符。如有必要,表示層會通過使用一種通格式來實現多種數據格式之間的轉換。 7.應用層:是最靠近用戶的OSI層。這一層為用戶的應用程序(例如電子郵件、文件傳輸和終端仿真)提供網絡服務。 口訣:物數網傳會表應 注意:OSI模型非常臃腫,處理數據效率非常低,這個模型已經被TCP/IP協議模型所取代5、TCP/IP協議模型(四層)
TCP/IP網絡協議棧分為應用層(Application)、傳輸層(Transport)、網絡層(Network)和鏈路 層(Link)四層。如下圖所示: 一般在應用開發過程中,討論最多的是TCP/IP模型。鏈路層 也可以稱之為 網絡接口層六、TCP/IP網絡模型通信過程
1、兩臺計算機通過TCP/IP協議通訊的過程
2、數據包封裝
傳輸層及其以下的機制由內核提供,應用層由用戶進程提供(后面將介紹如何使用socket API編寫應用程序),應用程序對通訊數據的含義進行解釋,而傳輸層及其以下處理通訊的細節,將數據從一臺計算機通過一定的路徑發送到另一臺計算機。應用層數據通過協議棧發到網絡上時,每層協議都要加上一個數據首部(header),稱為封裝(Encapsulation),如下圖所示: 不同的協議層對數據包有不同的稱謂,在傳輸層叫做段(segment),在網絡層叫做數據報 (datagram),在鏈路層叫做幀(frame)。數據封裝成幀后發到傳輸介質上,到達目的主機后每層 協議再剝掉相應的首部,最后將應用層數據交給應用程序處理。3、以太網幀格式
1、以太網的幀格式如下所示: 其中的源地址和目的地址是指網卡的硬件地址(也叫MAC地址),長度是48位(6個字節),是在網卡出廠時固化的。可在shell中使用ifconfig命令查看,“HWaddr 00:15:F2:14:9E:3F”部分就是硬件地 址。協議字段有三種值,分別對應IP、ARP、RARP。幀尾是CRC校驗碼。以太網幀中的數據長度規定最小46字節,最大1500字節,ARP和RARP數據包的長度不夠46字節,要在后面補填充位。最大值1500稱為以太網的最大傳輸單元(MTU),不同的網絡類型有不同的MTU,如果一個數據包從以太網路由到撥號鏈路上,數據包長度大于撥號鏈路的MTU,則需要對數據包進行分片 (fragmentation)。ifconfig命令輸出中也有“MTU:1500”。注意,MTU這個概念指數據幀中有效 載荷的最大長度,不包括幀頭長度。4、ARP數據報格式
在網絡通訊時,源主機的應用程序知道目的主機的IP地址和端口號,卻不知道目的主機的硬件地址,而數據包首先是被網卡接收到再去處理上層協議的,如果接收到的數據包的硬件地址與本機不符,則直接丟棄。因此在通訊前必須獲得目的主機的硬件地址。ARP協議就起到這個作用。源主機發出ARP請求,詢問“IP地址是192.168.0.1的主機的硬件地址是多少”,并將這個請求廣播到本地網段(以太網幀首部的硬件地址填FF:FF:FF:FF:FF:FF表示廣播),目的主機接收到廣播的ARP請求,發現其中的IP地址與本機相符,則發送一個ARP應答數據包給源主機,將自己的硬件地址填寫在應答包中。 每臺主機都維護一個ARP緩存表,可以用arp -a命令查看。緩存表中的表項有過期時間(一般為20分鐘),如果20分鐘內沒有再次使用某個表項,則該表項失效,下次還要發ARP請求來獲得目的主機的硬件地址。 ARP數據報的格式如下所示: 源MAC地址、目的MAC地址在以太網首部和ARP請求中各出現一次,對于鏈路層為以太網的情況是多余的,但如果鏈路層是其它類型的網絡則有可能是必要的。硬件類型指鏈路層網絡類型,1為以太網,協議類型指要轉換的地址類型,0x0800為IP地址,后面兩個地址長度對于以太網地址和IP地址分別為6和4(字節),op字段為1表示ARP請求,op字段為2表示ARP應答。5、IP段格式
IP數據報的首部長度和數據長度都是可變長的,但總是4字節的整數倍。對于IPv4,4位版本字段是4。4位首部長度的數值是以4字節為單位的,最小值為5,也就是說首部長度最小是4x5=20字節,也就是不帶任何選項的IP首部,4位能表示的最大值是15,也就是說首部長度最大是60字節。8位TOS字段有3個位用來指定IP數據報的優先級(目前已經廢棄不用),還有4個位表示可選的服務類型(最小延遲、最大?吐量、最大可靠性、最小成本),還有一個位總是0。總長度是整個數據報(包括IP首部和IP層payload)的字節數。每傳一個IP數據報,16位的標識加1,可用于分片和重新組裝數據報。3位標志和13位片偏移用于分片。TTL(Time to live)是這樣用的:源主機為數據包設定一個生存時間,比如64,每過一個路由器就把該值減1,如果減到0就表示路由已經太長了仍然找不到目的主機的網絡,就丟棄該包,因此這個生存時間的單位不是秒,而是跳(hop)。協議字段指示上層協議是TCP、UDP、ICMP還是IGMP。然后是校驗和,只校驗IP首部,數據的校驗由更高層協議負責。IPv4的IP地址長度為32位。 思考題:如果源主機和目的主機不在同一網段,ARP請求的廣播幀無法穿過路由器,源主機如何與目的主機通信? 參考:https://blog.csdn.net/weixin_43166958/article/details/865035066、TCP數據報格式
有源端口號和目的端口號,通訊的雙方由IP地址和端口號標識。32位序號、32位確認序號、窗口大小。4位首部長度和IP協議頭類似,表示TCP協議頭的長度,以4字節為單位,因此TCP協議頭最長可以是4x15=60字節,如果沒有選項字段,TCP協議頭最短20字節。URG、ACK、PSH、RST、SYN、FIN是六個控制位。16位檢驗和將TCP協議頭和數據都計算在內。七、傳輸層協議
TCP協議(打電話)
1、概念: 用來檢測網絡傳輸中差錯的傳輸控制協議 transmission control protocol。 是一種面向連接的傳 輸層協議,它能提供高可靠性通信(即數據無誤、數據無丟失、數據無失序、數據無重復到達的通信) 2、適用場合: 1)、對傳輸質量要求較高,以及傳輸大量數據的通信 2)、在需要可靠數據傳輸的場合,通常使用TCP協議 3)、QQ等即時通訊軟件的用戶登錄賬戶管理相關的功能,通常采用TCP協議UDP協議(信)
1、概念 UDP(User Datagram Protocol)用戶數據報協議。是不可靠的無連接的協議。在數據發送前, 因為不需要進行連接,所以可以進行高效率的數據傳輸。 2、適用場合: 1)發送小尺寸數據 2)適用于廣播/組播式通信 3) QQ等即時通訊軟件的點對點文本通訊以及音視頻通訊 常采用 UDP協議 4)網絡多媒體服務中通常采用UDP方式進行實時數據傳輸八、網絡編程中幾個重要概念
1、socket --> 插座,套接字 插座的種類繁多,就像很多個協議一樣,必須提前設置好協議。
1)概念 Socket本身有“插座”的意思,在Linux環境下,用于表示進程間網絡通信的特殊文件類型。本質為內 核借助緩沖區形成的偽文件。既然是文件,那么理所當然的,我們可以使用文件描述符引用套接字。與管道類似的,Linux系統將其封裝成文件的目的是為了統一接口,使得讀寫套接字和讀寫文件的操作一致。區別是管道主要應用于本地進程間通信,而套接字多應用于網絡進程間數據的傳遞。套接字的內核實現較為復雜,不宜在學習初期深入學習。 在TCP/IP協議中,“IP地址+TCP或UDP端口號”唯一標識網絡通訊中的一個進程。“IP地址+端口 號”就對應一個socket。欲建立連接的兩個進程各自有一個socket來標識,那么這兩個socket組成的 socket pair就唯一標識一個連接。因此可以用Socket來描述網絡連接的一對一關系。 2)套接字通信原理如下圖所示: 在網絡通信中,套接字一定是成對出現的。一端的發送緩沖區對應對端的接收緩沖區。我們使用同一個文件描述符指向發送緩沖區和接收緩沖區。 TCP/IP協議最早在BSD UNIX上實現,為TCP/IP協議設計的應用層編程接口稱為socket API。 3)特點 是一套編程的函數接口,目的是 建立套接字 無論是TCP協議,還是UDP協議,都必須使用socket socket在網絡模型中處于應用層與傳輸層之間 4)例子 使用TCP協議,那么socket建立出來的套接字就是TCP套接字 1 套接字: int sockfd = socket(TCP協議); --> 得到TCP協議套接字 2 普通文件: int fd = open(普通文件); --> fd就是文件描述符?2、IP地址
1)概念 用來標識網絡中不同的主機。通信必須要具有一個IP地址 2)分類 IPV4地址---32位 IPV6地址 ---128位 3)如何表示 常常以點分制"192.168.8.108" 4)數據包中都必須要包含目的IP地址,源IP地址。路由器依賴于此信息作為數據選擇路由3、端口號(16位)
標識同一臺主機內 不同的應用程序。 網絡 1主機1 -----------------------------------主機2qq qq 微信 微信 ......Jack.c ------------> Rose.c IP地址 192.168.8.100 192.168.8.108 ---> 雙方必須在相同的局域網端口號 50001 50001 ---> 雙方都必須使用相同的端口號 端口號占用: 1)系統占用端口號: 1 - 1023 (用戶不能再次使用端口號進行通信) 2)可用端口: 1024 - 65535 端口號選擇錯誤 --> 連接錯誤4、字節序
1)概念 一個多字節存儲單位的低地址存儲數據的高有效位 還是 低有效 位,說白了,也就是數據在計算機 內存中以什么樣的方式存儲 2)分類 小端字節序:數據的低有效位 存儲 在 內存中的 低地址 大端字節序:數據的低有效位 存儲 在 內存中的 高地址 3)為了避免不同類別主機之間在數據交換時由于對于字節序的不同而導致的差錯,引入了網絡字節序。也就是統一規定所有主機通過網絡發送數據包時轉為大端序,也就是網絡字節序。 內存地址: 低地址 -------------------------> 高地址 "hello"x86平臺 -----> ARM小端字節序: 低字節存放在低地址 1 2 3 4大端字節序: 高字節存放在低地址 4 3 2 1本地字節序: 取決于主機本身 x86是小端 ARM是大端網絡字節序: 一定是大端字節序高 低 例子: 192.168.1.120x86: 021.1.861.291 ---> 網絡: 192.168.1.120 ARM: 192.168.1.120 ---> 網絡: 192.168.1.120 總結一下: 無論本身是大端還是小端,都需要把自身的字節序轉換為網絡字節序,即大端,才能在網絡中傳輸數據。九、TCP通信
1、通信過程
2、客戶端API
1、建立套接字
1 #include <sys/types.h> /* See NOTES */ 2 #include <sys/socket.h> 3 int socket(int domain, int type, int protocol); 函數作用 建立套接字,返回套接字文件描述符 函數參數 ????????domain : 你要選擇 哪一種 地址族 ????????????????????????PF_INET / AF_INET Ipv4 網絡協議 ????????????????????????PF_INET6/AF_INET6 Ipv6 網絡協議 ????????type: 你 要選擇 哪一種 協議 ????????????????????????SOCK_STREAM 選擇TCP ----流式 套接字 ????????????????????????SOCK_DGRAM 選擇UDP ----數據報套接字 ????????protocol : 傳0表示使用默認協議 返回值 ????????成功返回 套接字文件描述符 sockfd ????????失敗返回 -12、發起連接 -撥打電話
1 #include <sys/types.h> /* See NOTES */ 2 #include <sys/socket.h> 3 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 函數參數 ????????sockfd --> 套接字文件描述符 ????????addr ---對方的IP地址 和 端口號 ????????addrlen ---地址的長度 sizeof(struct sockaddr_in ) 1 使用 man 7 ip 可以查看以下結構體 2 struct sockaddr ---舊的結構體 3 { 4 unsigned short int sa_family; ---IP地址 端口號 、 協議 5 unsigned char sa_data[14]; 6 }; 7 IPV4結構體 8 struct sockaddr_in 9 { 10 short int sin_family; /地址族 IPV4 IPV6/ 11 unsigned short int sin_port; /端口號/ 12 struct in_addr sin_addr; /IP地址/ 13 }; 14 struct in_addr { 15 in_addr_t s_addr; /in_addr_t為 32位的unsigned int,該無符號整數采用大端字節序。/ 16 }; 比如:初始化 IP地址 和端口號 --IPV4 1 struct sockaddr_in serverAddr; 2 serverAddr.sin_family = PF_INET ; 3 serverAddr.sin_port = htons (5000) ; // host to network short 4 serverAddr.sin_addr.s_addr = inet_addr("192.168.1.120"); //主機 IP地址 ---》網絡IP地 址 inet_addr5 connect(sockfd, (struct sockaddr * )&serverAddr, sizeof(struct sockaddr_in) ); 擴展函數: 1 #include <arpa/inet.h> 2 uint16_t htons(uint16_t hostshort); // 將主機端口號 轉成 網絡端口號 3 uint16_t ntohs(uint16_t netshort); // 將網絡端口號 轉成 主機端口號 4 5 #include <sys/socket.h> 6 #include <netinet/in.h> 7 #include <arpa/inet.h> 8 in_addr_t inet_addr(const char *cp); //將主機IP 轉成 網絡IP 9 char *inet_ntoa(struct in_addr in); //將網絡IP 轉成 主機IP 參數: ????????cp ---》主機IP 返回值 : ????????返回 網絡IP3、發送數據-------write send
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 ssize_t send(int sockfd, const void *buf, size_t len, int flags); 作用 ????????用于網絡中發送數據 參數 ????????sockfd: 套接字文件描述符 ????????buf : 你要發送的數據 ????????len : 你要發送數據的大小 ,以 字節為單位 ????????flags : 一般默認為 0 返回值 ????????成功返回發送的字節數 ????????失敗返回 -14、關閉文件--close
服務器端API:
1、綁定 bind
1 #include <sys/types.h> /* See NOTES */ 2 #include <sys/socket.h> 3 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 作用: ????????綁定自己的IP地址 和 端口號 參數 ????????sockfd : 套接字文件描述符 ????????addr : 自己的IP地址和端口號 ????????addrlen : 地址的大小長度 返回值2、設置鈴聲
1 #include <sys/types.h> /* See NOTES */ 2 #include <sys/socket.h> 3 int listen(int sockfd, int backlog); 作用 ????????創建 一個 未連接 隊列 ,同時最多連接的客戶端 總數 backlog 參數 ????????sockfd : 套接字文件描述符 ????????backlog :同時 最多支持連接上來的客戶端總數 返回值 ????????成功返回 0 ????????失敗返回 -1 說明 典型的服務器程序可以同時服務于多個客戶端,當有客戶端發起連接時,服務器調用的accept()返 回并接受這個連接,如果有大量的客戶端發起連接而服務器來不及處理,尚未accept的客戶端就處于連接等待狀態,listen()聲明sockfd處于監聽狀態,并且最多允許有backlog個客戶端處于連接待狀態,如果接收到更多的連接請求就忽略。 1 查看系統默認backlog 2 gec@ubuntu:/mnt/hgfs/2$ cat /proc/sys/net/ipv4/tcp_max_syn_backlog 3 1283、等待客戶端的連接 --accept
1 #include <sys/types.h> /* See NOTES */ 2 #include <sys/socket.h> 3 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 作用 ????????等待客戶端的連接 參數 ????????sockfd: 套接字文件描述符 ????????addr : 連接上來的客戶端的IP地址和端口號 ????????addrlen : 長度 //如果客戶端連接上來了,可以獲取到連接上來的客戶端的IP地址和端口號 返回值: ????????成功返回 已連接的套接字文件描述符4、接收數據 recv
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 ssize_t recv(int sockfd, void *buf, size_t len, int flags); 參數 ????????sockfd: 已連接的套接字文件描述符 connectfd ????????buf : 接收到的數據存儲到這里 ????????len : 接收數據的大小 ????????flags : 一般設置為 0 返回值: ????????成功返回 接收到的字節數 ????????失敗返回 -1 ????????返回 0 表示 客戶端斷開連接 服務器代碼: 1 #include<stdio.h> 2 #include <sys/types.h> /* See NOTES */ 3 #include <sys/socket.h> 4 #include <sys/socket.h> 5 #include <arpa/inet.h>6 #include <unistd.h> 7 #include<stdlib.h> 8 9 10 #define SERVER_ADDR "192.168.63.25" //服務器的IP地址 11 #define SERVER_PORT 20000 //port 服務器的端口號 12 13 14 int main() 15 { 16 int ret; 17 //1、買手機(建立套接字) 18 int socketFd = socket(AF_INET, SOCK_STREAM, 0); 19 if(socketFd == -1){ 20 perror("socket error"); 21 exit(0); 22 } 23 //2、綁定自己的電話號碼(綁定自己的IP地址和端口號) 24 //定義一個IPV4結構體變量,存儲IP地址和端口號 25 struct sockaddr_in serverAddr; 26 serverAddr.sin_family = AF_INET ;//IPv4 27 serverAddr.sin_port = htons(SERVER_PORT);//16端口號 --本地端口號--網絡端口號 28 serverAddr.sin_addr.s_addr = inet_addr(SERVER_ADDR); //32IP地址 --本地IP--網絡IP 29 30 ret = bind(socketFd, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr_in)); 31 if(ret == -1){ 32 perror("bind error"); 33 exit(0); 34 } 35 //3、設置鈴聲(設置監聽) 36 ret = listen(socketFd, 20); //20表示同時連接上來的客戶端的最大數量 37 if(ret == -1){ 38 perror("listen error"); 39 exit(0); 40 } 41 printf("阻塞等待新的客戶端連接......\n"); 42 //4、坐等電話(阻塞等待客戶端的連接) 43 int newClientFd = accept(socketFd, NULL,NULL); 44 if(ret == -1){45 perror("accept error"); 46 exit(0); 47 } 48 //注意 accept函數的返回值 表示 新的客戶端的文件描述符 ,后面與客戶端通信 必須使用該文件描述 符 49 printf("有新的客戶端連接上來....\n"); 50 51 //5、聊天 接收 數據 52 char buf[1024]={0}; 53 read(newClientFd,buf,sizeof(buf)); 54 55 printf("buf:%s\n",buf); 56 57 58 //6、關閉 斷開連接 59 close(socketFd); 60 close(newClientFd); 61 62 return 0; 63 } 客戶端代碼: 1 #include<stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <string.h> 7 #include <unistd.h> 8 9 #define OWNADDR "192.168.112.109" //我自己電腦的ip地址 10 #define OWNPORT 10000 //我自己電腦的該程序的端口號 11 12 #define SERVERADDR "192.168.112.109" //對方的 服務器的IP地址 13 #define SERVERPORT 11111 //對方的 服務器的端口號 14 15 int main() 16 { 17 //1、買手機(建立套接字)18 int socketfd = socket(AF_INET, SOCK_STREAM, 0); 19 if(socketfd == -1) 20 { 21 printf("沒錢了....,失敗\n"); 22 return -1; 23 } 24 //2、綁定自己的電話號碼(綁定自己的IP地址 和端口號) 25 //定義一個IPV4結構體變量,初始化自己的IP地址和端口號 26 struct sockaddr_in ownAddr; 27 ownAddr.sin_family = AF_INET;/*地址族 IPV4*/ 28 ownAddr.sin_port = htons(OWNPORT); //htons 將本地端口號轉為網絡端口號 29 ownAddr.sin_addr.s_addr = inet_addr(OWNADDR); //將本地IP地址轉為網絡IP地址 30 31 bind(socketfd, (struct sockaddr *)&ownAddr,sizeof(struct sockaddr_in)); 32 33 //3、開始打電話(發起連接) 34 struct sockaddr_in serverAddr; 35 serverAddr.sin_family = AF_INET;/*地址族 IPV4*/ 36 serverAddr.sin_port = htons(SERVERPORT); //htons 將本地端口號轉為網絡端口號 37 serverAddr.sin_addr.s_addr = inet_addr(SERVERADDR); //將本地IP地址轉為網絡IP地址 38 39 connect(socketfd,(struct sockaddr *)&serverAddr,sizeof(struct sockaddr_in)); 40 41 //4、聊天 42 while(1) 43 { 44 printf("data:"); 45 char buf[1024]={0}; 46 scanf("%s",buf); 47 //發送數據 48 send(socketfd, buf, strlen(buf), 0); 49 } 50 //5、關閉 51 close(socketfd); 52 53 return 0; 54 } 練習2: 自己ubuntu與自己的開發板通信。自己的ubuntu與同桌的ubuntu通信。 自己的開發板與同桌的ubuntu通信。 自己的開發板與同桌的開發板的通信。練習3: 修改代碼,實現TCP客戶端與TCP服務器互相可以收跟發。
服務器 #include <stdio.h> #include <unistd.h> #include <string.h> #include <pthread.h>#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>//服務器的IP地址 #define SERVER_IP "192.168.11.2" #define SERVER_PORT 60000//用來給客戶端發送數據 void *send_buf(void *arg) {//設置自己為分離屬性pthread_detach(pthread_self());int ret;char buf[1024] = {0};while(1){scanf("%s",buf); //給客戶端發送數據send(*(int *)arg,buf,strlen(buf),0); }}int main(int argc,char **argv) {//建立套接字int socket_fd;//AF_INET-->ipv4 SOCK_STREAM-->tcpsocket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd < 0){perror("socket fail");return -1;} //重復綁定本機IP可能會出現失敗,這個時候要設置端口號復用。(筆記2)int optval = 1;setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));//填充本機IP地址和端口struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);//本機端口轉化為網絡端口server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//將本機IP轉換為網絡IP//綁定本機IP和端口int ret;ret = bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));if(ret < 0){perror("bind fail");return -1;} printf("bind success [%s %d]\n",SERVER_IP,SERVER_PORT);//監聽ret = listen(socket_fd,20);if(ret < 0){perror("listen fail");return -1;} //等待客戶端的連接int socket_client;//客戶端的套接字struct sockaddr_in client_addr;//用來接受客戶端的ip地址和端口socklen_t addrlen = sizeof(client_addr);//socket_client = accept(socket_fd,NULL,NULL); //如果不解析客戶端的地址和端口可以設置地址參數為NULLsocket_client = accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);if(socket_client < 0){perror("accept fail");return -1; }//解析客戶端的ip和端口號char *ip = inet_ntoa(client_addr.sin_addr);//將網絡ip轉換為本機ipint port = ntohs(client_addr.sin_port);//將網絡端口號轉換為本機端口號printf("new client [ip:%s port:%d]\n",ip,port);//server_addr.sin_addr.s_addr//單獨開一個線程用來給客戶端發送數據pthread_t tid;ret = pthread_create(&tid,NULL,send_buf,&socket_client);//傳參客戶端的socket套接字if(ret != 0){perror("pthread_create fail");return -1;}//接受客戶端發送的數據(接受的是客戶端的套接字)char buf[1024] = {0};while(1){ memset(buf,0,sizeof(buf));ret = recv(socket_client,buf,sizeof(buf),0);//ret = read(socket_client,buf,sizeof(buf));//和recv函數的功能一樣if(ret == 0){printf("client down\n"); //客戶端掉線break;}printf("[ip:%s port:%d] buf:%s ret:%d \n",ip,port,buf,ret);}//關閉套接字close(socket_fd);return 0; }客戶端
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h>/*socket*/ #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> /*ip*/ #include <netinet/in.h> //man 3 inet_addr #include <arpa/inet.h>//服務器的IP地址 #define SERVER_IP "192.168.11.2" #define SERVER_PORT 60000//用來單獨接收的線程 void *recv_buf(void *arg) {//設置自己為分離屬性pthread_detach(pthread_self());int ret;char buf[1024] = {0};while(1){ bzero(buf,1024);//接受服務器回射的數據ret = recv(*(int *)arg,buf,sizeof(buf),0);if(ret == 0){printf("server down\n"); //服務器掉線break;} printf("recv buf:%s ret:%d \n",buf,ret); } }int main(int argc,char **argv) {//ret用來判斷返回值int ret;//建立套接字--socketint socket_fd;//AF_INET-->ipv4 SOCK_STREAM-->tcpsocket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd < 0){perror("socket fail");return -1;}#if 0//綁定本機ip和端口(可以綁定自己的端口,如果不綁定系統隨機分配)struct sockaddr_in my_addr;my_addr.sin_family = AF_INET; //ipv4my_addr.sin_port = htons(60001);//host to net(本機端口號轉網絡端口號)my_addr.sin_addr.s_addr = inet_addr("192.168.11.2");//將本機IP轉換為網絡IP bind(socket_fd,(struct sockaddr *)&my_addr,sizeof(my_addr));#endif//創建一個線程用來接受服務器發送過來的數據pthread_t tid;ret = pthread_create(&tid,NULL,recv_buf,&socket_fd);if(ret != 0){perror("pthread_create fail");return -1;}//填充IP地址(服務器)--新結構體struct sockaddr_in server_addr;server_addr.sin_family = AF_INET; //ipv4server_addr.sin_port = htons(SERVER_PORT);//host to net(本機端口號轉網絡端口號)server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//將本機IP轉換為網絡IP//連接服務器ret = connect(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));if(ret < 0){printf("connect fail\n");return -1;}//給服務器發送數據char buf[1024] = {0};while(1){ memset(buf,0,sizeof(buf));scanf("%s",buf);ret = send(socket_fd,buf,strlen(buf),0); //如果服務器掉線,客戶端繼續發送,就會導致進程退出//ret = write(socket_fd,buf,strlen(buf));printf("send success ret:%d\n",ret);}//關閉套接字close(socket_fd);return 0; }練習4: 寫一個回射服務器,這個服務器功能就是客戶端發送什么數據給服務器,服務器都會將數據回發到客戶端。
服務器
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>//服務器的IP地址 #define SERVER_IP "192.168.11.2" #define SERVER_PORT 60000int main(int argc,char **argv) {//建立套接字int socket_fd;//AF_INET-->ipv4 SOCK_STREAM-->tcpsocket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd < 0){perror("socket fail");return -1;} //重復綁定本機IP可能會出現失敗,這個時候要設置端口號復用。int optval = 1;setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));//填充本機IP地址和端口struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);//本機端口轉化為網絡端口server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//將本機IP轉換為網絡IP//綁定本機IP和端口int ret;ret = bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));if(ret < 0){perror("bind fail");return -1;} printf("bind success [%s %d]\n",SERVER_IP,SERVER_PORT);//監聽ret = listen(socket_fd,20);if(ret < 0){perror("listen fail");return -1;} //等待客戶端的連接int socket_client;//客戶端的套接字struct sockaddr_in client_addr;//用來接受客戶端的ip地址和端口socklen_t addrlen = sizeof(client_addr);socket_client = accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);if(socket_client < 0){perror("accept fail");return -1; }//解析客戶端的ip和端口號char *ip = inet_ntoa(client_addr.sin_addr);//將網絡ip轉換為本機ipint port = ntohs(client_addr.sin_port);//將網絡端口號轉換為本機端口號printf("[ip:%s port:%d]\n",ip,port);//接受客戶端發送的數據(接受的是客戶端的套接字)char buf[1024] = {0};while(1){ memset(buf,0,sizeof(buf));ret = recv(socket_client,buf,sizeof(buf),0);//ret = read(socket_client,buf,sizeof(buf));//和recv函數的功能一樣if(ret == 0){printf("client down\n");break;}printf("[ip:%s port:%d] buf:%s ret:%d \n",ip,port,buf,ret);//將接收到的數據回射給客戶端send(socket_client,buf,strlen(buf),0); }//關閉套接字close(socket_fd);return 0; }客戶端
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> /*socket*/ #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> /*ip*/ #include <netinet/in.h> //man 3 inet_addr #include <arpa/inet.h>//服務器的IP地址 #define SERVER_IP "192.168.11.2" #define SERVER_PORT 60000int main(int argc,char **argv) {//建立套接字--socketint socket_fd;//AF_INET-->ipv4 SOCK_STREAM-->tcpsocket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd < 0){perror("socket fail");return -1;}#if 0//綁定本機ip和端口(可以綁定自己的端口,如果不綁定系統隨機分配)struct sockaddr_in my_addr;my_addr.sin_family = AF_INET; //ipv4my_addr.sin_port = htons(60001);//host to net(本機端口號轉網絡端口號)my_addr.sin_addr.s_addr = inet_addr("192.168.11.2");//將本機IP轉換為網絡IP bind(socket_fd,(struct sockaddr *)&my_addr,sizeof(my_addr));#endif//填充IP地址(服務器)--新結構體struct sockaddr_in server_addr;server_addr.sin_family = AF_INET; //ipv4server_addr.sin_port = htons(SERVER_PORT);//host to net(本機端口號轉網絡端口號)server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//將本機IP轉換為網絡IP//連接服務器int ret;ret = connect(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));if(ret < 0){printf("connect fail\n");return -1;}//給服務器發送數據char buf[1024] = {0};while(1){ memset(buf,0,sizeof(buf));scanf("%s",buf);ret = send(socket_fd,buf,strlen(buf),0); //ret = write(socket_fd,buf,strlen(buf));//printf("send success ret:%d\n",ret);//接受服務器回射的數據ret = recv(socket_fd,buf,sizeof(buf),0);printf("recv buf:%s ret:%d \n",buf,ret);}//關閉套接字close(socket_fd);return 0; }總結
以上是生活随笔為你收集整理的网络编程01-TCP协议(详)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2017年工作学习计划(20170120
- 下一篇: 一站式解决方案 :OFD电子证照生成