基礎知識
客戶端:指瀏覽器或者自定義的客戶端。
服務端:像Tomcat伺服器或者自定義客戶端。
TCP/IP
TCP:傳輸層協議。
IP:網路層協議。
TCP/UDP
TCP與UDP區別
TCP使用案例:用於RPC介面呼叫,傳送電子郵件等需要可靠性傳輸的事情。
UDP使用案例:用於視訊這類的傳輸。
相同點:
- TCP與UDP都是網路層的通訊協議,只是通訊的協議不同。
不同點:
- TCP協議在通訊前要先通過三次握手的方式建立連線,保證通訊的可靠性。而UDP不需要建立連線接可以傳送資料包,因此接收方不一定能收到,因此是不可靠的。
- TCP協議需要建立連線和釋放連線,因此傳輸效率低。而UDP不需要釋放資源,因此開銷小,速度快。
- UDP可以進行廣播傳送,而TCP是點對點通訊。
- TCP在建立連線後可以進行大資料量的傳輸,而UDP會限制每個資料包的傳輸大小在64K以內。
TCP
三次握手
客戶端建立連線請求時的三次握手:
第一次握手:客戶端向服務端傳送syn報文和序號x;
第二次握手:服務端接收到客戶端的請求,傳送ack=x+1報文,併傳送序號y,syn報文。
第三次握手:客戶端接收到服務端的請求,傳送ack= y+1報文,併傳送序號z。
為什麼是三次握手而不是二次或者四次五次呢?
因此根據三次握手我們客戶端和服務端都可以知道自己傳送正常,對方接收正常;而二次握手的話,服務端並不知道自己傳送正常,只能知道客戶端傳送接收正常,自己接收正常,而不知道自己傳送正常。而三次握手剛好就滿足了這個條件。既然三次握手就滿足了,四次五次就會顯得多餘了,雖然更多次的握手可以更加保證通訊的正常,但是正常來說三次握手就能保證通訊99%是可靠的,再多次的握手可靠性的提高並不高,意義不大。
四次揮手
客戶端服務端都可以通過四次揮手關閉連線,但一般都是客戶端發起四次揮手請求來關閉連線,因為我們服務端一般是24小時線上服務的。
以客戶端發起斷開連線為例:
客戶端告訴服務端我要斷開連線了。
服務端相應客戶端說我收到你的斷開連線請求了。
服務端斷開連線,併傳送請求告訴客戶端我和你斷開連線了。
客戶端收到斷開連線請求斷開了連線,併傳送確認斷開的請求告訴服務我和你斷開連線了,這時候服務端就接收不到客戶端的請求了,如果接收到了就表示沒有真正斷開連線。
示例小結
public class TCPTest {
/**
* 客戶端
*/
@Test
public void client() throws IOException {
Socket socket = null;
OutputStream os = null;
try {
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
//1建立Socke連線物件,指明要連線的服務端的ip和埠號
socket = new Socket(inetAddress, 9000);
//2獲取輸出流,用於輸出資料
os = socket.getOutputStream();
//3寫資料
os.write("我叫客戶端,你好啊".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4關閉流資源
if (os != null) {
os.close();
}
if (socket != null) {
socket.close();
}
}
}
@Test
public void server() throws IOException {
InputStream is = null;
ByteArrayOutputStream baos = null;
ServerSocket serverSocket = null;
Socket socket = null;
try {
//建立服務端的ServerSocket,指明自己的埠號,給客戶端通過指定的ip和該埠號確定程式進行連線
serverSocket = new ServerSocket(9000);
//accept()用於接收來自客戶端的連線
socket = serverSocket.accept();
//獲取輸入流
is = socket.getInputStream();
//讀取輸入流中的資料,ByteArrayOutputStream該流維護動態陣列儲存寫入的資料
baos = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while ((len = is.read(buff)) != -1) {
baos.write(buff, 0, len);
}
System.out.println(baos.toString());
System.out.println("收到客戶端請求了!");
} catch (IOException e) {
e.printStackTrace();
} finally {
//關閉資源
baos.close();
is.close();
socket.close();
serverSocket.close();
}
}
/**
* 我叫客戶端,你好啊
* 收到客戶端請求了!
*/
}
示例小結——TCP傳送接收檔案
public class TCPFileTest {
/**
* 客戶端給服務端發生檔案
*/
@Test
public void client() throws IOException {
Socket socket = null;
OutputStream os = null;
FileInputStream fis = null;
ByteArrayOutputStream baos = null;
try {
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
//1建立Socke連線物件,指明要連線的服務端的ip和埠號
socket = new Socket(inetAddress, 9000);
//2獲取輸出流,用於輸出資料
os = socket.getOutputStream();
fis = new FileInputStream("是大臣.jpg");
//3寫資料
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
System.out.println("向服務端傳送圖片成功!");
//關閉資料的輸出,不然服務端會一直阻塞著接收不往下走
socket.shutdownOutput();
//接收服務端的應答
InputStream is = socket.getInputStream();
baos = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int length;
while ((length = is.read(buff)) != -1) {
baos.write(buff, 0, length);
}
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4關閉流資源
if (os != null) {
os.close();
}
if (socket != null) {
socket.close();
}
}
}
/**
* 向服務端傳送圖片成功!
* 客戶端,你的檔案收到了
*/
/**
* 服務端接收檔案儲存到本地
*
* @throws IOException
*/
@Test
public void server() throws IOException {
InputStream is = null;
FileOutputStream fos = null;
ServerSocket serverSocket = null;
Socket socket = null;
OutputStream os = null;
try {
//建立服務端的ServerSocket,指明自己的埠號,給客戶端通過指定的ip和該埠號確定程式進行連線
serverSocket = new ServerSocket(9000);
//accept()用於接收來自客戶端的連線
socket = serverSocket.accept();
//獲取輸入流
is = socket.getInputStream();
//開啟輸出流寫檔案
fos = new FileOutputStream("是大臣-copy5.jpg");
byte[] buff = new byte[1024];
int len;
while ((len = is.read(buff)) != -1) {
fos.write(buff, 0, len);
}
System.out.println("收到客戶端請求了!");
//給客戶端寫出應答
os = socket.getOutputStream();
os.write("客戶端,你的檔案收到了".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//關閉資源
fos.close();
is.close();
socket.close();
serverSocket.close();
os.close();
}
}
/**
* 收到客戶端請求了!
*/
}
UDP
示例小結
public class UDPTest {
/**
* 發生端
*/
@Test
public void sender() throws IOException {
//1建立socket,傳送端不需要指定要發生的ip和埠,直接在要發生的包裡指定接收的地址埠即可。
DatagramSocket socket = new DatagramSocket();
byte[] data = "我是傳送端,這是給你的一封信!".getBytes();
//資料包要傳送的地址
InetAddress address = InetAddress.getByName("127.0.0.1");
//2建立傳送包
DatagramPacket packet = new DatagramPacket(data, 0, data.length, address, 9999);
//3傳送
socket.send(packet);
System.out.println("傳送資料成功!");
//4關閉流
socket.close();
}
/**
* 接收端
*/
@Test
public void receiver() throws IOException {
//1建立接收端socket,要指定接收端的埠號,才能接收
DatagramSocket socket = new DatagramSocket(9999);
//2建立用於接收資料流的陣列容器
byte[] buffer = new byte[1024];
//2建立接收包,用於接收資料包
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
//3傳送
socket.receive(packet);
//4列印接收到的資料
System.out.println(new String(packet.getData(),0,packet.getLength()));
System.out.println("接收資料成功!");
//5關閉流
socket.close();
}
}
URL
示例小結
public class URLTest {
/**
* 使用url去下載網路檔案
*
* @throws IOException
*/
@Test
public void urlTest() throws IOException {
//URL相當於種子
URL url = new URL("https://pics4.baidu.com/feed/d009b3de9c82d1586f9efd2871ac9ad1bd3e42bf.jpeg?token=dd219671927c0cb92aebbd8808110a70");
//根據不同的協議強轉不同的urlConnection,HttpsURLConnection或者HttpURLConnection
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
InputStream is = urlConnection.getInputStream();
//寫到本地
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("pic.jpeg"));
byte[] buff = new byte[1024];
int len;
while ((len = is.read(buff)) != -1) {
bos.write(buff, 0, len);
}
System.out.println("下載成功了!");
//關閉流
is.close();
bos.close();
urlConnection.disconnect();
}
}
參考
618-630