網絡編程基礎?
為什么需要網絡編程?--豐富的網絡資源
用戶在瀏覽器中,打開在線視頻網站,如優酷看視頻,實質是通過網絡,獲取到網絡上的一個視頻資源。與本地打開視頻文件類似,只是視頻文件這個資源的來源是網絡。
相比本地資源來說,網絡提供了更為豐富的網絡資源:
所謂的網絡資源,其實就是在網絡中可以獲取的各種數據資源。而所有的網絡資源,都是通過網絡編程來進行數據傳輸的。
什么是網絡編程
網絡編程,指網絡上的主機,通過不同的進程,以編程的方式實現網絡通信:網絡數據傳輸當然,我們只要滿足進程不同就行;所以即便是同一個主機,只要是不同進程,基于網絡來傳輸數
據,也屬于網絡編程。
特殊的,對于開發來說,在條件有限的情況下,一般也都是在一個主機中運行多個進程來完成網絡編程。
但是,我們一定要明確,我們的目的是提供網絡上不同主機,基于網絡來傳輸數據資源:
進程A:編程來獲取網絡資源
進程B:編程來提供網絡資源?????
網絡編程中的基本概念?
1.發送端和接收端
在一次網絡數據傳輸時:
發送端:數據的發送方進程,稱為發送端。發送端主機即網絡通信中的源主機。
接收端:數據的接收方進程,稱為接收端。接收端主機即網絡通信中的目的主機。
收發端:發送端和接收端兩端,也簡稱為收發端。
注意:發送端和接收端只是相對的,只是一次網絡數據傳輸產生數據流向后的概念。?
2.請求和響應
一般來說,獲取一個網絡資源,涉及到兩次網絡數據傳輸:
第一次:請求數據的發送
第二次:響應數據的發送。
好比在快餐店點一份炒飯:
先要發起請求:點一份炒飯,再有快餐店提供的對應響應:提供一份炒飯3.客戶端和服務端
服務端:在常見的網絡數據傳輸場景下,把提供服務的一方進程,稱為服務端,可以提供對外提供服務。
客戶端:獲取服務的一方進程,稱為客戶端。
對于服務來說,一般是提供:客戶端獲取服務資源
Socket套接字?
概念
Socket(套接字)?是計算機網絡中用于進程間通信的一種機制,它允許不同主機或同一主機上的不同進程之間進行數據交換。Socket 是應用層與傳輸層之間的抽象接口,為程序提供了使用網絡協議(如 TCP/IP)的標準化方式。
Socket套接字,是由系統提供用于網絡通信的技術,是基于TCP/IP協議的網絡通信的基本操作單元;基于Socket套接字的網絡程序開發就是網絡編程。
分類
Socket套接字主要針對傳輸層協議劃分為如下三類:
1.流套接字:使用傳輸層TCP協議
TCP,即Transmission ControlProtocol (傳輸控制協議),傳輸層協議。
以下為TCP的特點:
有連接? ?可靠傳輸? ?面向字節流? ?有接收緩沖區,也有發送緩沖區? ?大小不限
對于字節流來說,可以簡單的理解為,傳輸數據是基于10流,流式數據的特征就是在I0流沒有關閉的情況下,是無邊界的數據,可以多次發送,也可以分開多次接收。2.數據報套接字:使用傳輸層UDP協議
UDP,即User Datagram Protocol(用戶數據報協議),傳輸層協議。
以下為UDP的特點:
無連接? ?不可靠傳輸? ?面向數據報? ?有接收緩沖區,無發送緩沖區? ?大小受限:一次最多傳輸64k
對于數據報來說,可以簡單的理解為,傳輸數據是一塊一塊的,發送一塊數據假如100個字節,必須一次發送,接收也必須一次接收100個字節,而不能分100次,每次接收1個字節。
3.原始套接字
原始套接字用于自定義傳輸層協議,用于讀寫內核沒有處理的IP協議數據。不能指望,一個數據包發送之后,100%到達對方
可靠傳輸的意思,不是保證數據包100%到達,而是盡可能的提高傳輸成功的概率.不可靠傳輸,只是把數據發了,就不管了
對于TCP來說,TCP協議中,就保存了對端的信息.
A和B通信,A和B先建立連接
讓A保存,B的信息,B保存A的信息(彼此之間知道,誰是和他建立連接的那個)
對于UDP來說,UDP協議本身不保存對方的信息--就是無連接面向字節流vs面向數據報
面向字節流,讀寫數據的時候,是以字節為單位? ?支持任意長度=>粘包問題
面向數據報,讀寫數據的時候,以一個數據報為單位(不是字符)
不存在粘包>長度限制? 一次必須讀寫一個UDP數據報,不能是半個
半雙工vS全雙工
一個通信鏈路,支持雙向通信(能讀,也能寫)
一個通信鏈路,只支持單向通信~~(要么讀,要么寫)
UDP的socket API?
Java數據報套接字通信模型
對于UDP協議來說,具有無連接,面向數據報的特征,即每次都是沒有建立連接,并且一次發送全部的數據報,一次接收全部的數據報。計算機中的"文件"通常是一個"廣義的概念"
文件還能代指一些硬件設備(操作系統管理硬件設備,也是抽象成文件,統一管理的)
網卡=>socket 文件? ?操作網卡的時候,流程和操作普通文件差不多
也會在文件描述符表中分配一個表項
操作網卡的時候流程和操作普通文件差不多? ? ?打開->讀寫->關閉
其中,打開這個操作也會在文件描述符表中分配一個表項? ? ? 操作網卡,直接操作不好操作.
把操作網卡轉換成操作socket文件.socket文件就相當于"網卡的遙控器"就是電腦的網卡--操作系統當做文件來管理
java中使用UDP協議通信,主要基于DatagramSocket類來創建數據報套接字,并使用
DatagramPacket作為發送或接收的UDP數據報。對于一次發送及接收UDP數據報的流程如下:
以上只是一次發送端的UDP數據報發送,及接收端的數據報接收,并沒有返回的數據。也就是只有請求,沒有響應。對于一個服務端來說,重要的是提供多個客戶端的請求處理及響應,流程如下:
DatagramSocket 構造方法:
這里傳輸的就是端口號,用于區分主機上的不同的應用程序
DatagramSocket 方法:
DatagramPacket 構造方法:
DatagramPacket 方法:
package thread;import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; public class UdpEchoServer {private DatagramSocket socket=null;private String process(String request){return request;}public UdpEchoServer(int port) throws SocketException {socket=new DatagramSocket(port);}public void start() throws IOException {// 啟動服務器System.out.println("服務器啟動");while (true){// 循環一次, 就相當于處理一次請求.// 處理請求的過程, 典型的服務器都是分成三個步驟的.// 1. 讀取請求并解析.// DatagramPacket 表示一個 UDP 數據報. 此處傳入的字節數組, 就保存 UDP 的載荷部分DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);socket.receive(requestPacket);String request=new String(requestPacket.getData(),0, requestPacket.getLength());// 2. 根據請求, 計算響應. (服務器最關鍵的邏輯)// 但是此處寫的是回顯服務器. 這個環節相當于省略了.String response=process(request);//3.把響應返回給客戶端// 根據 response 構造 DatagramPacket, 發送給客戶端.// 此處不能使用 response.length()DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length);// 此處還不能直接發送. UDP 協議自身沒有保存對方的信息(不知道發給誰)// 需要指定 目的 ip 和 目的端口.socket.send(responsePacket);//4,打印一個日志System.out.printf("[%s:%d] req:%s,resp:%d\n",responsePacket.getAddress().toString(),requestPacket.getPort(),request,response);}}}
DatagramPacket有三個方法.
1)getAddress 只拿到 IP
2)getPort 只拿到端口
3)getSocketAddress同時拿到IP和端口(IP和端口通過
一個lnetAddress 對象表示)后續客戶端給服務器發的數據,就是發字符串.(echo,客戶端發一個hello,服務器返回hello)
本身收到的DatagramPacket 的二進制數據從 String轉來的.只是還原回去客戶端訪問服務器,serverlp是目的ip,serverport 是目的端口
源ip客戶端所在的主機ip,源端口,應該是隨機搞一個端口(操作系統分配空閑端口)一定是不能填寫serverPort? ?必須使用無參數版本
進階--代碼解析
端口號是區分同一個主機不同的應用程序的.
同一時刻,不能有兩個程序使用同一個端口
(操作系統中,一個程序嘗試關聯一個非空閑的端口,就會關聯失敗)如果客戶端是固定端口,很可能客戶端運行的時候,這個端口被別的程序占用,就會使得當前這個程序運行失敗!!
客戶端是在用戶手里,無法控制你的用戶電腦上都運行啥樣的程序
服務器是在程序員手里的,就算出現端口沖突,程序員也方便處理構造請求的數據報.
1.載荷
2.目的IP目的端口127.0.0.1特殊的IP? ? 環回IP? ?表示當前這個主機.
無論你主機的IP真實是啥,都可以使用127.0.0.1代替.
類似于this
由于此時,客戶端和服務器在同一個主機上,就可以使用127.0.0.1來訪問
如果是不同主機,就需要填寫其他的IP了.客戶端和服務器在同一個主機上? ? ?是否聯網都是可以的.
不同主機上必須聯網了不同的主機要進行這樣的通訊:(1)在同一個局域網下(2)部署在云服務器上
若是想部署在虛擬機:取決于虛擬機的網絡是怎么設置的.
虛擬機網絡設置規格主要有三種典型:
1.橋接網絡(可以)
2.NAT網絡(有的虛擬機軟件可以,有的不行)
3.Host Only網絡(不可以)(為啥沒有云服務器不行?NAT機制搞鬼)
把程序部署到云服務器上,就可以訪問到云服務器了DatagramPacket? UDP面向數據報協議.
發送接收的時候,以數據報作為基本單位socket正常是需要釋放的
這個socket都是跟隨整個進程的.? ?此時就不需要單獨close.
進程退出,進程內部的pcb也就會銷毀,里面的文件描述符表也就釋放了.部署: 啟動服務器:類似于idea中點一下運行按鈕? ? ?java-jar jar包名
啟動客戶端(本地的電腦)? ?把連接的服務器的IP地址,改成云服務器的IP
ServerSocket? ?專門給服務器用的
Socket? ?服務器和客戶端都會用
TCP的一個核心特點,面向字節流.? ? 讀寫數據的基本單位就是字節byte雖然同一個端口,但是如果協議不同,不會沖突的
這個操作只是把數據放到"發送緩沖區"中? ? 還沒有真正寫入到網卡里.
這個發送緩存區就是指的是內存空間.flush方法來"沖刷緩沖區"
其中,println行為是自動加上\n
如果修改成了下面的這樣:
這個時候,數據是發過去了的? ?服務器收到了的? ? ?服務器沒有真正處理.
判定收到的數據中是否包含"空白符":換行,回車,空格,制表符,翻頁符.....
遇到空白符,認為是一個"完整的next"
在遇到之前,都會阻塞.暗暗約定,一個請求/響應使用\n作為結束標記
對端讀的時候,也是讀到\n就結束(認為是讀到一個完整的請求了)UDP就是以 DatagramPacket 作為單位的.,TCP則是字節為單位.
實際上一個請求,往往是由多個字節構成的
每個客戶端連接,都會創建一個新的.
每個客戶端斷開連接,這個對象也就可以不要了一個服務器要能同時給多個客戶端提供服務
accept 就是接受客戶端發的連接請求
socket 是基于TCP的 ?TCP是有連接的 可以理解為建立連接的過程
無法同時等待accept和等待用戶請求,等待用戶發請求的時候,沒法等accept.
這個時候,有新的客戶端連過來了,也無法接通電話.如果客戶端1不發請求,服務器就會阻塞在hasNext 這里
此時就需要使用多線程來解決這個問題