TCP/IP協議
IP、TCP、UDP 都是TCP/IP協議的一部分。而Socket是應用層與TCP/IP協議通信抽象出來的接口。
TCP/IP.jpg
TCP/IP(Transmission Control Protocol/Internet Protocol)即傳輸控制協議/網間協議,是一個工業標準的協議集,它是為廣域(WANs)設計的。包括運輸層、網絡層、鏈路層。
TCP與UDP的區別
TCP
- TCP面向連接(三次握手);UDP是無連接的,即發送數據之前不需要建立連接。
- TCP傳輸可靠而UDP不可靠。也就是說,通過TCP連接傳送的數據,無差錯,不丟失,不重復,且按序到達;而UDP則不保證,可能丟包,也不按順序到達。因此UDP更快,效率高,TCP相反。
- TCP面向字節流,實際上是TCP把數據看成一連串無結構的字節流。UDP是面向報文的。
- 通信方式:每一條TCP連接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通信。
- 基于以上這些區別,因此使用場景不同。TCP用在瀏覽器(Http)、文件傳輸(FTP)、接發郵件(SMTP,POP)、遠程登錄(telnet、ssh)。UDP用在視頻通話、語音通話等,適用于多播和廣播的應用場景。
總之,TCP傳播數據準確但速度較慢,用在文件傳輸等對準確性要求較高的地方。UDP相反,用在視頻流等大流量對速度要求較高的地方。
TCP和UDP的Socket實現(JAVA)
Java為Socket編程封裝了幾個重要的類。其中Socket和ServerSocket用于TCP通信。DatagramSocket和DatagramPacket用于UDP通信。
socket.png
基于TCP的Socket編程
Server端
服務器端首先實例化ServerSocket對象,然后為其綁定一個本機地址,并開始監聽。一直阻塞狀態下等待客戶端請求,當獲得客戶端連接請求后,返回一個socket對象。然后用這個socket接收一條消息,并發送一條消息。代碼如下:
package com.chenxuri.java.network;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Created by chenxuri on 2017/12/21.
*/
public class SocketTcpServer {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("Connecting to client ...");
/*
接收客戶端數據
*/
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
BufferedReader socketIn = new BufferedReader(new InputStreamReader(inputStream));
String temp;
while ((temp = socketIn.readLine()) != null) {
System.out.println(temp);
}
/*
發送數據
*/
OutputStream outputStream = socket.getOutputStream();
PrintWriter socketOut = new PrintWriter(outputStream);
socketOut.print("Hi,I have received your message!");
socketOut.flush();
socketOut.close();
socketIn.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client端
客戶端首先實例化一個socket對象,用這個對象連接服務器端。連接成功后,發送一條消息,然后等待接收一條消息。代碼如下:
package com.chenxuri.java.network;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
/**
* Created by chenxuri on 2017/12/21.
*/
public class SocketTcpClient {
public static void main(String[] args) {
try {
Socket socket = new Socket();
/*
設置地址和連接超時時間,有的地方說不設置連接時間可能會造成無限期阻塞。
其實不會,因為操作系統底層有超時限制,windows是20秒。
但仍建議設置一個較短的時間,尤其是需要頻繁連接的時候。
*/
socket.connect(new InetSocketAddress("127.0.0.1",8888), 5*1000);
//設置讀取超時時間,該時間若不設置,服務器等出現問題時可能會一直處于阻塞狀態。
socket.setSoTimeout(10*1000);
/*
往服務端發送數據
*/
OutputStream outputStream = socket.getOutputStream();
PrintWriter socketOut = new PrintWriter(outputStream);
String str = "Hello,I come from client!";
socketOut.write(str);
socketOut.flush();
socket.shutdownOutput(); //半關閉,告訴服務端發送完畢,可以接受輸入過來的數據
/*
接收回應數據
*/
InputStream inputStream = socket.getInputStream();
BufferedReader socketIn = new BufferedReader(new InputStreamReader(inputStream));
String temp;
while ((temp = socketIn.readLine()) != null) {
System.out.println(temp);
}
socketIn.close();
socketOut.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
基于UDP的Socket編程
Server端
服務器端首先實例化DatagramSocket對象,然后為其綁定一個端口,并開始監聽。一直阻塞狀態下等待從客戶端接收數據報。然后從數據報中獲取數據報的源地址,然后用這個源地址作為目的地址打包一個數據報,然后發送出去。代碼如下:
package com.chenxuri.java.network;
import java.io.*;
import java.net.*;
/**
* Created by chenxuri on 2017/12/21.
*/
public class SocketUdpServer {
public static void main(String[] args) {
try {
DatagramSocket socket = new DatagramSocket(8888);
/*
接收客戶端的數據
*/
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes,bytes.length);
socket.receive(packet);
String receiveStr = new String(bytes);
System.out.println("From client: " + receiveStr);
/*
發送回應數據
*/
int port = packet.getPort();
InetAddress addr = packet.getAddress();
String sendStr = "Hello! I'm Server";
byte[] sendBuf = sendStr.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendBuf , sendBuf.length , addr , port );
socket.send(sendPacket);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client端
客戶端首先實例化一個DatagramSocket對象。利用服務器地址和端口號作為目的地址打包一個數據報,并發送。然后等待從服務器回復的數據報。代碼如下:
package com.chenxuri.java.network;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
/**
* Created by chenxuri on 2017/12/21.
*/
public class SocketUdpClient {
public static void main(String[] args) {
try {
DatagramSocket socket = new DatagramSocket();
/*
發送數據
*/
String sendStr = "I'm client, this is the message for server.";
byte[] bytes = sendStr.getBytes();
DatagramPacket packet = new DatagramPacket(bytes,bytes.length);
packet.setSocketAddress(new InetSocketAddress("127.0.0.1",8888));
socket.send(packet);
/*
接收返回數據
*/
byte[] backbuf = new byte[1024];
DatagramPacket backPacket = new DatagramPacket(backbuf, backbuf.length);
socket.receive(backPacket);
System.out.println("From server: "+ new String(backbuf));
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
以上代碼都經過親自測試,可以運行。