1. 程式人生 > >網路協議:TCP協議和UDP協議

網路協議:TCP協議和UDP協議

網路協議:

1、網路協議分層:

    OSI模型是國際標準化組織ISO創立的。這是一個理論模型,並無實際產品完全符合OSI模型。制定OSI模型只是為了分析網路通訊方便而引進的一套理論。也為以後制定實用協議或產品打下基礎。

    OSI模型總共分7層,各層的作用:從上到下

        A、 應用層:指網路作業系統和具體的應用程式,對應WWW伺服器、FTP伺服器等應用軟體。

        B、 表示層:資料語法的轉換、資料的傳送等。

        C、 會話層:建立起兩端之間的會話關係,並負責資料的傳送。

        D、傳輸層:負責錯誤的檢查與修復,以確保傳送的質量,是TCP工作的地方。

        E、

網路層:提供了編址方案,IP協議工作的地方(資料包)。包括路由器裝置,路由器就是幫助如何選擇IP通訊線路的硬體裝置,本質上是一個電腦,具有雙網絡卡。進行路線的選擇。

        F、 資料鏈路層:將由物理層傳來的未經處理的位資料包裝成資料幀。包括交換機、網絡卡等裝置。

        G、物理層:對應網線、光纖、介面等物理裝置。

    傳輸層向上才是跟程式設計有關係,向下都是跟硬體裝置有關係,不涉及程式設計。

2、IP地址及含義:

    IP地址有32位。通常被分割為4個8位的二進位制。

傳輸層協議:

1、TCP和UDP協議:

    TCP(傳輸控制協議)和UDP(使用者報文協議)協議屬於傳輸層協議。

        A、 TCP提供IP環境下的資料可靠傳輸,它提供的服務包括資料流傳送、可靠性、有效流控、全雙工操作和多路複用。通過面向連線、端到端和可靠的資料包傳送。面向連線就是在正式通訊前必須要與對方建立起連線,比如,你給人打電話,必須等線路接通了,對方拿起話筒才能相互通話。

        B、 UDP是使用者資料報協議,是OSI參考模型中一種無連線的傳輸層協議,提供面向事務的簡單不可靠資訊傳送服務。是與TCP相對應的協議。是面向非連線的協議,不與對方建立連線,而是直接把資料包傳送過去。

2、埠號的概念:

    一般指TCP/IP協議中的埠,埠號的範圍是0到65535。通過“IP地址+埠號”來區分不同的服務。

3、TCP協議與UDP協議的區別:

    1、 TCP協議需要建立連線,而UDP協議則不需要。

    2、 TCP是可靠的傳輸協議,而UDP是不可靠的。

    3、 TCP適合傳輸大量的資料,而UDP適合傳輸少量資料。

    4、 TCP的速度慢,而UDP的速度快。

應用層協議:都是建立在TCP和UDP之上的。

1、HTTP協議:

HTTP協議叫做超文字傳輸協議,是用於從WWW伺服器傳輸超文字到本地瀏覽器的傳送協議。它可以使瀏覽器更加高效,使網路傳輸減少。它不僅保證計算機正確快速的傳輸超文字文件,還確定傳輸文件中的哪一部分,以及哪一部分內容首先顯示。HTTP是一個應用層協議,由請求和響應構成,是一個標準的客戶端伺服器模型,是一個無狀態協議。

2、FTP協議:

FTP是TCP/IP協議組中的協議之一,是檔案傳輸協議。該協議是Internet檔案傳送的基礎,它由一系列規格說明文件組成,目標是提高檔案的共享性,提供非直接遠端計算機,使儲存介質對使用者透明和可靠高效的傳輸資料。

3、SMTP協議:

SMTP即是簡單郵件傳輸協議,是一種提供可靠且有效電子郵件傳輸的協議。SMTP是建立在FTP檔案傳輸服務上的一種郵件服務,主要用於傳輸系統之間的郵件資訊並提供與來信有關的通知。

TCP通訊:

1、 Socket原理

2、 Socket通訊模型

Socket原理:

1、Socket簡介:

Socket通常叫做“套接字”,用於描述IP地址和埠,是一個通訊鏈的控制代碼。在Internet上的主機一般運行了多個服務軟體,同時提供幾種服務。每種服務都開啟一個Socket,並繫結到一個埠上,不同的埠對應於不同的服務。

應用程式通常通過“套接字”向網路發出請求或者應答網路請求。Socket和ServerSocket類位於java.net包中。ServerSocket用於服務端,Socket是建立網路連線時使用的。在連線成功時,應用程式兩端都會產生一個Socket例項,操作這個例項,完成所需的會話。

2、獲取本地地址和埠號:

Java.net.Socket為套接字,其提供了很多方法,其中我們可以通過Socket獲取本地的地址以及埠。

        1---intgetLocalPort()

        該方法用於獲取本地使用的埠號。在客戶端就是客戶端本地,伺服器端就是伺服器端地址。

        2---InetAddressgetLocalAddress()

        該方法用於獲取套接字繫結的本地地址。

        使用InetAddress獲取本地的地址方法:

        1---StringgetCanonicalHostName()

        獲取此IP地址的完全限定域名

        2---StringgetHostAddress()

        返回IP地址字串(以文字表現形式)

3、獲取遠端地址和埠號:

    通過Socket獲取遠端的地址以及埠號:

    ---intgetPort()

    該方法用於獲取遠端使用的埠號

    ---InetAddress.getInetAddress()

    該方法用於獲取套接字繫結的遠端地址

4、獲取網路輸入流和網路輸出流:

    通過Socket獲取輸入流與輸出流,這兩個方法是使用Socket通訊的關鍵方法

    ---InputStreamgetInputStream()

    該方法用於返回此套接字的輸入流

    ---OutputStreamgetOutputStream()

    該方法用於返回此套接字的輸出流

5、Close方法:

Socket通訊模型:

1、Server端ServerSocket監聽:

    Java.net.ServerSocket是運行於伺服器端應用程式中。通常建立ServerSocket需要指定服務埠號,之後監聽Socket的連線:

    ServerSocketserver=new ServerSocket(8080)只要一new就會繫結一個埠號,這個埠號只能執行一遍,再執行第2遍就會說這個埠號已經被佔用了。

    Socketsocket=server.accept()accept()方法是阻塞式方法,等待客戶端的連線

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PipedWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {

	ServerSocket ss;
	public void start() throws Exception{
		ss=new ServerSocket(8000);
		//main方法所在的執行緒是主執行緒,由main方法執行的方法就是主執行緒執行的方法,監聽客戶端的連線
		while(true) {//不斷的監聽是否有客戶端連線進來
			Socket s=ss.accept();//連線進來就有一個Socket物件
			ClientHandler handler=new ClientHandler(s);//將客戶端交給執行緒
			Thread t=new Thread(handler);
			//啟動客戶端處理執行緒,當前執行緒返回去監聽
			t.start();//開啟子執行緒,主執行緒繼續監聽
		}
	}
	
	class ClientHandler implements Runnable{//使用內部類控制起來方便,可以共享外部類的資源
		Socket socket;
		public ClientHandler(Socket s) {
			socket=s;
		}
		public void run() {
			try {
				InputStream in=socket.getInputStream();
				OutputStream out=socket.getOutputStream();
				BufferedReader reader=new BufferedReader(new InputStreamReader(in,"utf-8"));
				PrintWriter writer=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);
				                                                       //true是自動刷出
				String str=reader.readLine();
				System.out.println("從客戶收到:"+str);
				writer.println("是你啦");//回給客戶一個
				socket.close();
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) throws Exception{
		TCPServer server=new TCPServer();
		server.start();//主執行緒start,啟動子執行緒的run(),主執行緒start之後又返回去監聽客戶端

	}

}

2、Client端Socket連線:

    當服務端建立ServerSocket並通過accept()方法偵聽後,我們就可以通過在客戶端應用程式中建立Socket來向服務端發起連線。

    需要注意的是,建立Socket的同時就發起連線,若連線異常會丟擲異常。

    Socket socket=newSocket(“localhost”,8080)

    引數1:伺服器的IP地址或者域名   引數2:埠號

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

public class TCPClientDemo {

	public static void main(String[] args) throws Exception{
		//連線到伺服器,如果沒有找到(也就是IP.port錯了)就會丟擲異常,如果連線成功就建立Socket物件
		Socket s=new Socket("localhost",8000);//伺服器的IP地址和埠號
		System.out.println("客戶端:");
		System.out.println(s.getLocalAddress());
		System.out.println(s.getLocalPort());
		System.out.println(s.getInetAddress());
		System.out.println(s.getPort());
		
		//工廠方法
		OutputStream out=s.getOutputStream();//返回套接字的輸出流
		InputStream in=s.getInputStream();//返回套接字的輸入流
		
		BufferedReader reader=new BufferedReader(new InputStreamReader(in,"utf-8"));
		//BufferedReader每次讀取一行
		PrintWriter writer=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);
		//具有自動重新整理的緩衝字元輸出流
		writer.println("小麗呀?");//必須是println()
		String str=reader.readLine();//接收到回車為止
		System.out.println("收到:"+str);
		
		s.close();

	}

}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServerDemo {

	public static void main(String[] args) throws Exception {
		//繫結TCP服務埠8000
		ServerSocket ss=new ServerSocket(8000);//如果TCP 8000端口占用了就會丟擲異常
		
		//開始監聽服務埠,accept是阻塞方法,等待有客戶端的連線,沒有連線一直阻塞,有連線,成功以後,結束阻塞返回Socket物件
		Socket s=ss.accept();
		System.out.println("伺服器:");
		System.out.println(s.getLocalAddress());//獲取伺服器端的地址
		System.out.println(s.getLocalPort());//獲取伺服器端的埠號
		System.out.println(s.getInetAddress());//獲取遠端地址
		System.out.println(s.getPort());//獲取遠端埠號
		
		OutputStream out=s.getOutputStream();
		InputStream in=s.getInputStream();
		
		BufferedReader reader=new BufferedReader(new InputStreamReader(in,"utf-8"));
		PrintWriter writer=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);
		String str=reader.readLine();//等待訊息一直到回車為止
		System.out.println(str);
		writer.println("我是呀!");
				
		s.close();

	}

}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PipedWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {

	ServerSocket ss;
	public void start() throws Exception{
		ss=new ServerSocket(8000);
		//main方法所在的執行緒是主執行緒,由main方法執行的方法就是主執行緒執行的方法,監聽客戶端的連線
		while(true) {//不斷的監聽是否有客戶端連線進來
			Socket s=ss.accept();//連線進來就有一個Socket物件
			ClientHandler handler=new ClientHandler(s);//將客戶端交給執行緒
			Thread t=new Thread(handler);
			//啟動客戶端處理執行緒,當前執行緒返回去監聽
			t.start();//開啟子執行緒,主執行緒繼續監聽
		}
	}
	
	class ClientHandler implements Runnable{//使用內部類控制起來方便,可以共享外部類的資源
		Socket socket;
		public ClientHandler(Socket s) {
			socket=s;
		}
		public void run() {
			try {
				InputStream in=socket.getInputStream();
				OutputStream out=socket.getOutputStream();
				BufferedReader reader=new BufferedReader(new InputStreamReader(in,"utf-8"));
				PrintWriter writer=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);
				                                                       //true是自動刷出
				String str=reader.readLine();
				System.out.println("從客戶收到:"+str);
				writer.println("是你啦");//回給客戶一個
				socket.close();
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) throws Exception{
		TCPServer server=new TCPServer();
		server.start();//主執行緒start,啟動子執行緒的run(),主執行緒start之後又返回去監聽客戶端
	}
}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

public class TCPClientDemo {

	public static void main(String[] args) throws Exception{
		//連線到伺服器,如果沒有找到(也就是IP.port錯了)就會丟擲異常,如果連線成功就建立Socket物件
		Socket s=new Socket("localhost",8000);//伺服器的IP地址和埠號
		System.out.println("客戶端:");
		System.out.println(s.getLocalAddress());
		System.out.println(s.getLocalPort());
		System.out.println(s.getInetAddress());
		System.out.println(s.getPort());
		
		//工廠方法
		OutputStream out=s.getOutputStream();//返回套接字的輸出流
		InputStream in=s.getInputStream();//返回套接字的輸入流
		
		BufferedReader reader=new BufferedReader(new InputStreamReader(in,"utf-8"));
		//BufferedReader每次讀取一行
		PrintWriter writer=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);
		//具有自動重新整理的緩衝字元輸出流
		writer.println("小麗呀?");//必須是println()
		String str=reader.readLine();//接收到回車為止
		System.out.println("收到:"+str);
		
		s.close();

	}

}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class TalkServer {
	
	private List<PrintWriter> users;//登陸使用者佇列
	private BlockingQueue<String> queue;//訊息佇列
	private ServerSocket ss;
	
	//啟動TCP監聽,和等待客戶端連線
	public void start() throws Exception{//啟動監聽
		users=new ArrayList<PrintWriter>();
		queue=new LinkedBlockingQueue<String>(100);//佇列中最多放100個訊息
		ss=new ServerSocket(8000);
		//先啟動小兵
		Thread bing=new Thread(new Bing());
		bing.start();
		//再進行客戶連線監聽
		while(true) {
			Socket s=ss.accept();//等待使用者的登入
			PrintWriter out=new PrintWriter(s.getOutputStream(),true);//開啟自動刷出
			BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));
			users.add(out);//已經登入的使用者的輸出流
			//使用者的輸出流交給ClientHandler處理
			new Thread(new ClientHandler(in)).start();
		}	
	}
	//負責接收每個使用者的訊息,儲存到佇列中
	class ClientHandler implements Runnable{//負責接收使用者訊息
		BufferedReader in;
		public ClientHandler(BufferedReader in) {
			this.in=in;
		}
		public void run() {//從客戶流裡面讀一個訊息放到佇列裡,讀錯了就不讀了,執行緒就結束
			while(true) {
				try {
					//從客戶端讀取訊息
					String str=in.readLine();
					//網路斷開的時候,readLine會返回null
					if(str==null) {
						break;
					}
					//傳送到queue中
					queue.put(str);
				}catch(Exception e) {
					e.printStackTrace();
					break;
				}
			}
			try {
				in.close();
			}catch(Exception e) {
				
			}
		}
			
	}
	
	//小兵負責訊息的轉發,從佇列中取訊息傳送到user
	class Bing implements Runnable{
		public void run() {
			//從佇列中讀取訊息,轉發給每個客戶
			while(true) {
				try {
					String msg=queue.take();
					for(PrintWriter out:users) {
						out.println(msg);
					}
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
			}
		}	
	}
	
	public static void main(String[] args) throws Exception{	
		TalkServer server=new TalkServer();
		//啟動伺服器
		server.start();

	}

}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class TalkClient1 {
	Socket socket;
	//啟動客戶端:連線到伺服器,啟動兩個執行緒
	public void start() throws Exception{
		socket=new Socket("localhost",8000);
		
		new Sender(socket.getOutputStream()).start();
		new Receiver(socket.getInputStream()).start();
	}
	//從控制檯讀取訊息,傳送到伺服器
	class Sender extends Thread{
		OutputStream out;
		public Sender(OutputStream out){
			this.out=out;
		}
		public void run() {
			//從控制檯讀訊息,傳送到伺服器
			Scanner in=new Scanner(System.in);
			//out是傳送到伺服器的流
			PrintWriter out=new PrintWriter(this.out,true);
			String ip=socket.getLocalAddress().getHostAddress();//獲得本機IP
			while(true) {
				String str=in.nextLine();
				out.println(ip+":"+str);
			}
		}
	}
	//從伺服器接收訊息,寫到控制檯
	class Receiver extends Thread{
		InputStream in;
		public Receiver(InputStream in) {
			this.in=in;
		}
		public void run() {
			//從伺服器接收訊息,寫到控制檯
			BufferedReader in=new BufferedReader(new InputStreamReader(this.in));
			while(true) {
				try {
					String str=in.readLine();
					if(str==null) {//如果null,網路斷開了
						break;
					}
					System.out.println(str);
				}catch(Exception e) {
					e.printStackTrace();
					break;
				}
			}
			try {
				socket.close();
			}catch(Exception e) {
				
			}
		}
	}
	
	public static void main(String[] args) throws Exception {
		TalkClient1 client=new TalkClient1();
		client.start();
	}
}

UDP通訊:

1、 DatagramPacket

2、 DatagramSocket

3、 UDP穿透

DatagramPacket:資料報文

 1、構建接收包:

    DatagramPacket:UDP資料報基於IP建立的,每臺主機有65535個埠號可以使用,資料報中位元組數限制為65536-8

構造接受包:

    1、---DatagramPacket(byte[] buf,int length):

    將資料包中length長的資料裝進buf陣列

    2、---DatagramPacker(byte[] buf,int offstet,int length):

    將資料包從offset開始、length長的資料裝進buf陣列

DatagramSocket:傳送和接收

1、服務端接收:

    DatagramSocket用於接收和傳送UDP的Socket例項

    ---DatagramSocket(int port):

    建立例項,並固定監聽port埠的報文,通常用於服務端。

    ---receive(DatagramSocket d):

    接收資料報文到d中。receive方法產生“阻塞”。

2、 客戶端傳送

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPClient {

	public static void main(String[] args) throws Exception{
		//準備資料,相當於信件內容
		String str="您好!";
		byte[] buf=str.getBytes("utf-8");
		
		//建立資料封包,包含傳送目標地址,相當於包信封
		InetAddress ip=InetAddress.getByName("localhost");
		//InetAddress表示網際網路IP地址,根據域名來獲得IP地址
		DatagramPacket data=new DatagramPacket(buf,buf.length,ip,8899);
		                             //packet data、packet length、目標地址、目標埠號
		
		//傳送UDP資料,投遞
		DatagramSocket socket=new DatagramSocket();
		socket.send(data);
		
		//準備接受伺服器返回的資料
		buf=new byte[1024];
		data=new DatagramPacket(buf,1024);
		socket.receive(data);
		int length=data.getLength();
		str=new String(buf,0,length,"utf-8");
		System.out.println(str);		
	}
}
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPServer {

	public static void main(String[] args) throws Exception{
		//建立空白空間
		byte[] buf=new byte[1024];
		
		//建立空接收資料包,如果收到資料會填充到陣列
		DatagramPacket data=new DatagramPacket(buf,1024);
		
		//開始準備接收資料
		DatagramSocket socket=new DatagramSocket(8899);
		
		//開始監聽,receive()方法是阻塞方法,有資料收到就結束Block
		socket.receive(data);
		
		//接收資料的數量
		int length=data.getLength();
		String str=new String(buf,0,length,"utf-8");//buf是將byte轉為String的陣列,從0到length
		System.out.println(str);
		
		//從接收到的資料包中獲得客戶端的IP和port
		InetAddress clientIP=data.getAddress();
		int port=data.getPort();
		System.out.println(clientIP+":"+port);
		
		//原路送回訊息,傳送訊息到客戶端
		//準備資料
		buf="我是小麗呀!".getBytes("utf-8");
		//封裝信封,地址是客戶端地址
		data=new DatagramPacket(buf,buf.length,clientIP,port);
		//傳送
		socket.send(data);
	}
}

UDP穿透:

1、 UDP穿透參考例項

    1、 內網客戶端機器A、B,向伺服器S傳送資料包C2SRegister。

        允許S--->A,S--->B

    2、 A首先向B的NAT埠傳送資料包 C2CHoleStart

        允許B--->A

    3、 A然後向伺服器傳送資料包C2SHoleRequest,請求伺服器通知B。

    4、 伺服器向B傳送資料包S2CHoleCommand,指令B向A的NAT埠傳送資料包C2CHoleAnswer.

        允許A--->B

    5、 A收到B的資料包,表示穿透成功

    6、 A傳送正式訊息資料包C2CTextMessage給B

    7、 A,B各自發送心跳包C2CHeartbeat,C2SHeartbeat維持埠活躍,保持通道通暢。