網路協議:TCP協議和UDP協議
網路協議:
1、網路協議分層:
OSI模型是國際標準化組織ISO創立的。這是一個理論模型,並無實際產品完全符合OSI模型。制定OSI模型只是為了分析網路通訊方便而引進的一套理論。也為以後制定實用協議或產品打下基礎。
OSI模型總共分7層,各層的作用:從上到下
A、 應用層:指網路作業系統和具體的應用程式,對應WWW伺服器、FTP伺服器等應用軟體。
B、 表示層:資料語法的轉換、資料的傳送等。
C、 會話層:建立起兩端之間的會話關係,並負責資料的傳送。
D、傳輸層:負責錯誤的檢查與修復,以確保傳送的質量,是TCP工作的地方。
E、
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維持埠活躍,保持通道通暢。