網絡架構模型主要有OSI模型和TCP/IP模型java
OSI(Open System Interconnect),開放式系統互聯。OSI定義了網絡互連的七層框架(物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層、應用層)。它實際上並無人推廣,只是一種理想化的模型。程序員
應用層編程
應用層最靠近用戶的一層,是爲計算機用戶提供應用接口,也爲用戶直接提供各類網絡服務。咱們常見應用層的網絡服務協議有:HTTP,HTTPS,FTP,TELNET等。瀏覽器
傳輸層服務器
創建了主機端到端的連接,傳輸層的做用是爲上層協議提供端到端的可靠和透明的數據傳輸服務,包括處理差錯控制和流量控制等問題。該層向高層屏蔽了下層數據通訊的細節,使高層用戶看到的只是在兩個傳輸實體間的一條主機到主機的、可由用戶控制和設定的、可靠的數據通路。咱們一般說的,TCP UDP就是在這一層。端口號既是這裏的「端」。網絡
網絡層多線程
本層經過IP尋址來創建兩個節點之間的鏈接,爲源端的運輸層送來的分組,選擇合適的路由和交換節點,正確無誤地按照地址傳送給目的端的運輸層。就是一般說的IP層。這一層就是咱們常常說的IP協議層。IP協議是Internet的基礎。架構
數據鏈路層框架
物理層socket
即瀏覽器/服務器模式,B/S架構採起瀏覽器請求,服務器響應的工做模式。客戶則在須要服務時向服務器進行請求。服務器響應後及時返回,不須要實時監聽端口。
即客戶端/瀏覽器模式,通訊雙方一方做爲服務器等待客戶提出請求並予以響應。客戶則在須要服務時向服務器提出申請。服務器通常做爲守護進程始終運行,監聽網絡端口,一旦有客戶請求,就會啓動一個服務進程來響應該客戶,同時本身繼續監聽服務端口,使後來的客戶也能及時獲得服務。
傳輸控制協議(TCP,Transmission Control Protocol)是一種面向鏈接的、可靠的、基於字節流的傳輸層通訊協議。
經過TCP協議傳輸,獲得的是一個順序的無差錯的數據流。發送方和接收方的成對的兩個socket之間必須創建鏈接,當一個socket(一般都是server socket)等待創建鏈接時,另外一個socket能夠要求進行鏈接,一旦這兩個socket鏈接起來,它們就能夠進行雙向數據傳輸,雙方均可以進行發送或接收操做。
創建起一個TCP鏈接須要通過「三次握手」:
第一次握手:客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
握手過程當中傳送的包裏不包含數據,三次握手完畢後,客戶端與服務器才正式開始傳送數據。
理想狀態下,TCP鏈接一旦創建,在通訊雙方中的任何一方主動關閉鏈接以前,TCP 鏈接都將被一直保持下去。
斷開鏈接時服務器和客戶端都可以主動發起斷開TCP鏈接的請求。
UDP 爲應用程序提供了一種無需創建鏈接就能夠發送封裝的 IP 數據包的方法。
每一個數據報都是一個獨立的信息,包括完整的源地址或目的地址,它在網絡上以任何可能的路徑傳往目的地,所以可否到達目的地,到達目的地的時間以及內容的正確性都是不能被保證的。
TCP
UDP
Socket的英文原義是「孔」或「插座」。在網絡編程中,網絡上的兩個程序經過一個雙向的通訊鏈接實現數據的交換,這個鏈接的一端稱爲一個Socket。
Socket套接字是通訊的基石,是支持TCP/IP協議的網絡通訊的基本操做單元。它是網絡通訊過程當中端點的抽象表示,包含進行網絡通訊必須的五種信息:鏈接使用的協議,本地主機的IP地址,本地進程的協議端口,遠地主機的IP地址,遠地進程的協議端口。
Socket本質是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員作網絡開發所用的接口,這就是Socket編程接口;HTTP是轎車,提供了封裝或者顯示數據的具體形式;Socket是發動機,提供了網絡通訊的能力。
套接字之間的鏈接過程能夠分爲三個步驟:服務器監聽,客戶端請求,鏈接確認。
Server端Listen監聽某個端口是否有鏈接請求,Client端向Server 端發出鏈接請求,Server端向Client端發回Accept接受消息。這樣一個鏈接就創建起來了。Server端和Client端均可以經過Send,Write等方法與對方通訊。
對於一個功能齊全的Socket,都要包含如下基本結構,其工做過程包含如下四個基本的步驟:
客戶端
import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.nio.charset.StandardCharsets; //客戶端 public class TCPClient01 { public static void main(String[] args) { Socket socket = null; OutputStream os = null; try { //一、要知道服務器的地址 InetAddress serverIp = InetAddress.getByName("127.0.0.1"); //二、端口號 int port = 9999; //三、建立一個Socket鏈接 socket = new Socket(serverIp, port); //四、發送消息 os = socket.getOutputStream(); os.write("你好,歡迎光臨喵小決".getBytes(StandardCharsets.UTF_8)); } catch (Exception e) { e.printStackTrace(); }finally { try { assert os != null; os.close(); } catch (IOException e) { e.printStackTrace(); } try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
服務端
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; //服務端 public class TCPServer01 { public static void main(String[] args) { ServerSocket serverSocket = null; Socket socket = null; InputStream is = null; ByteArrayOutputStream baos = null; try { //一、擁有一個地址 serverSocket = new ServerSocket(9999); //二、等待客戶端鏈接過來 socket = serverSocket.accept(); //三、讀取客戶端的消息 is = socket.getInputStream(); //管道流 baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { baos.write(buffer, 0, len); } System.out.println(baos.toString()); } catch (IOException e) { e.printStackTrace(); }finally { //關閉資源 try { assert baos != null; baos.close(); } catch (IOException e) { e.printStackTrace(); } try { is.close(); } catch (IOException e) { e.printStackTrace(); } try { socket.close(); } catch (IOException e) { e.printStackTrace(); } try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
客戶端
import java.io.*; import java.net.InetAddress; import java.net.Socket; //客戶端 public class TCPClient02 { public static void main(String[] args) throws Exception { //一、建立一個Socket鏈接 Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000); //二、建立一個輸出流 OutputStream os = socket.getOutputStream(); //三、文件流 FileInputStream fis = new FileInputStream(new File("D:\\Project\\java\\TCP\\TCP\\src\\bg.png")); //四、寫出文件 byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { os.write(buffer, 0, len); } //通知服務器,我已經結束了 socket.shutdownOutput();//我已經傳輸完了 //肯定服務器接收完畢,纔可以斷開鏈接 InputStream is = socket.getInputStream(); //String byte[] ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer2 = new byte[1024]; int len2; while ((len2 = is.read(buffer2)) != -1) { baos.write(buffer2, 0, len2); } System.out.println(baos.toString()); //五、關閉資源 baos.close(); is.close(); fis.close(); os.close(); socket.close(); } }
服務端
import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; //服務端 public class TCPServer02 { public static void main(String[] args) throws Exception { //一、建立服務 ServerSocket serverSocket = new ServerSocket(9000); //二、監聽客戶端的鏈接 Socket socket = serverSocket.accept();//阻塞式監聽,會一直等待客戶端鏈接 //三、獲取輸入流 InputStream is = socket.getInputStream(); //四、文件輸出 FileOutputStream fos = new FileOutputStream(new File("receive.png")); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } //通知客戶端我接受完畢了 OutputStream os = socket.getOutputStream(); os.write("我接收完畢了,你能夠斷開了".getBytes(StandardCharsets.UTF_8)); //關閉資源 fos.close(); is.close(); socket.close(); serverSocket.close(); } }
客戶端
import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; //不須要鏈接服務器 public class UdpClient01 { public static void main(String[] args) throws Exception { //一、創建一個Socket DatagramSocket socket = new DatagramSocket(); //二、建一個包 String msg = "你好!,服務器"; //發送給誰 InetAddress localhost = InetAddress.getByName("localhost"); int port = 9090; //數據,數據的長度起始,要發送給誰 DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port); //三、發送一個包 socket.send(packet); //四、關閉流 socket.close(); } }
服務器
import java.net.DatagramPacket; import java.net.DatagramSocket; //仍是要等待客戶端的鏈接 public class UdpServer01 { public static void main(String[] args) throws Exception { //開放端口 DatagramSocket socket = new DatagramSocket(9090); //接收數據包 byte[] buffer = new byte[1024]; //接收 DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); socket.receive(packet);//阻塞接收 System.out.println(new String(packet.getData(),0,packet.getLength())); //關閉 socket.close(); } }
發送方
import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; public class UdpSender { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket(8888); //準備數據:控制檯讀取System.in BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); while (true) { String data = reader.readLine(); byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); DatagramPacket packet = new DatagramPacket(dataBytes, 0, dataBytes.length, new InetSocketAddress("localhost", 6666)); socket.send(packet); if (data.equals("bye")) { break; } } socket.close(); } }
接收方
import java.net.DatagramPacket; import java.net.DatagramSocket; public class UdpReceive { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket(6666); while (true) { //準備接收的包裹 byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); socket.receive(packet);//阻塞式接收包裹 //斷開鏈接 bye byte[] data = packet.getData(); String receiveData = new String(data, 0, data.length); System.out.println(receiveData); if (receiveData.equals("bye")) { break; } } socket.close(); } }
使用Socket
和Thread
來實現多線程在線質詢聊天
老師
public class TalkTeacher { public static void main(String[] args) { new Thread(new TalkSend(6666, "localhost", 8888)).start(); new Thread(new TalkReceive(9999, "學生")).start(); } }
學生
public class TalkStudent { public static void main(String[] args) { new Thread(new TalkSend(7777, "localhost", 9999)).start(); new Thread(new TalkReceive(8888, "老師")).start(); } }
發送方
import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; public class UdpSender { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket(8888); //準備數據:控制檯讀取System.in BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); while (true) { String data = reader.readLine(); byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); DatagramPacket packet = new DatagramPacket(dataBytes, 0, dataBytes.length, new InetSocketAddress("localhost", 6666)); socket.send(packet); if (data.equals("bye")) { break; } } socket.close(); } }
接收方
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; public class TalkReceive implements Runnable { DatagramSocket socket = null; private int port; private String msgFrom; public TalkReceive(int port, String msgFrom) { this.msgFrom = msgFrom; this.port = port; try { socket = new DatagramSocket(port); } catch (SocketException e) { e.printStackTrace(); } } @Override public void run() { while (true) { try { byte[] container = new byte[1024]; DatagramPacket packet = new DatagramPacket(container, 0, container.length); socket.receive(packet); byte[] data = packet.getData(); String receiveData = new String(data, 0, data.length); System.out.println(msgFrom + ":" + receiveData); if (receiveData.equals("bye")) { break; } } catch (IOException e) { e.printStackTrace(); } } socket.close(); } }