TCP與UDP的主要區(qū)別:
TCP—傳輸控制協(xié)議,提供的是面向連接浸间、可靠的字節(jié)流服務(wù)拒逮。當(dāng)客戶和服務(wù)器彼此交換數(shù)據(jù)前伐蒋,必須先在雙方之間建立一個TCP連接物臂,之后才能傳輸數(shù)據(jù)舷蟀。TCP提供超時重發(fā)池凄,丟棄重復(fù)數(shù)據(jù)贮尖,檢驗數(shù)據(jù)蹬蚁,流量控制等功能薇组,保證數(shù)據(jù)能從一端傳到另一端
UDP—用戶數(shù)據(jù)報協(xié)議外臂,是一個簡單的面向數(shù)據(jù)報的運輸層協(xié)議。UDP不提供可靠性律胀,它只是把應(yīng)用程序傳給IP層的數(shù)據(jù)報發(fā)送出去宋光,但是并不能保證它們能到達(dá)目的地。由于UDP在傳輸數(shù)據(jù)報前不用在客戶和服務(wù)器之間建立一個連接炭菌,且沒有超時重發(fā)等機(jī)制罪佳,故而傳輸速度很快.
在Java數(shù)據(jù)通信中UDP編程
UDP協(xié)議(用戶數(shù)據(jù)報協(xié)議)是無連接的、不可靠的黑低、無序的,速度快
進(jìn)行數(shù)據(jù)傳輸時赘艳,首先將要傳輸?shù)臄?shù)據(jù)定義成數(shù)據(jù)報(Datagram),大小限制在64k克握,在數(shù)據(jù)報中指明數(shù)據(jù)索要達(dá)到的Socket(主機(jī)地址和端口號)蕾管,然后再將數(shù)據(jù)報發(fā)送出去
DatagramPacket類:表示數(shù)據(jù)報包
DatagramSocket類:進(jìn)行端到端通信的類
服務(wù)器端實現(xiàn)步驟
① 創(chuàng)建DatagramSocket,指定端口號
② 創(chuàng)建DatagramPacket
③ 接受客戶端發(fā)送的數(shù)據(jù)信息
④ 讀取數(shù)據(jù)
public class UDPServer {
public static void main(String[] args) throws IOException {
/*
* 接收客戶端發(fā)送的數(shù)據(jù)
*/
//1.創(chuàng)建服務(wù)器端DatagramSocket菩暗,指定端口
DatagramSocket socket=new DatagramSocket(8800);
//2.創(chuàng)建數(shù)據(jù)報掰曾,用于接收客戶端發(fā)送的數(shù)據(jù)
byte[] data =new byte[1024];//創(chuàng)建字節(jié)數(shù)組,指定接收的數(shù)據(jù)包的大小
DatagramPacket packet=new DatagramPacket(data, data.length);
//3.接收客戶端發(fā)送的數(shù)據(jù)
System.out.println("****服務(wù)器端已經(jīng)啟動停团,等待客戶端發(fā)送數(shù)據(jù)");
socket.receive(packet);//此方法在接收到數(shù)據(jù)報之前會一直阻塞
//4.讀取數(shù)據(jù)
String info=new String(data, 0, packet.getLength());
System.out.println("我是服務(wù)器旷坦,客戶端說:"+info);
/*
* 向客戶端響應(yīng)數(shù)據(jù)
*/
//1.定義客戶端的地址掏熬、端口號、數(shù)據(jù)
InetAddress address=packet.getAddress();
int port=packet.getPort();
byte[] data2="歡迎您!".getBytes();
//2.創(chuàng)建數(shù)據(jù)報塞蹭,包含響應(yīng)的數(shù)據(jù)信息
DatagramPacket packet2=new DatagramPacket(data2, data2.length, address, port);
//3.響應(yīng)客戶端
socket.send(packet2);
//4.關(guān)閉資源
socket.close();
}
}
客戶端實現(xiàn)步驟
① 定義發(fā)送信息
② 創(chuàng)建DatagramPacket孽江,包含將要發(fā)送的信息
③ 創(chuàng)建DatagramSocket
④ 發(fā)送數(shù)據(jù)
public class UDPClient {
public static void main(String[] args) throws IOException {
/*
* 向服務(wù)器端發(fā)送數(shù)據(jù)
*/
//1.定義服務(wù)器的地址、端口號番电、數(shù)據(jù)
InetAddress address=InetAddress.getByName("localhost");
int port=8800;
byte[] data="用戶名:jinbin;密碼:1997".getBytes();
//2.創(chuàng)建數(shù)據(jù)報岗屏,包含發(fā)送的數(shù)據(jù)信息
DatagramPacket packet=new DatagramPacket(data, data.length, address, port);
//3.創(chuàng)建DatagramSocket對象
DatagramSocket socket=new DatagramSocket();
//4.向服務(wù)器端發(fā)送數(shù)據(jù)報
socket.send(packet);
/*
* 接收服務(wù)器端響應(yīng)的數(shù)據(jù)
*/
//1.創(chuàng)建數(shù)據(jù)報,用于接收服務(wù)器端響應(yīng)的數(shù)據(jù)
byte[] data2=new byte[1024];
DatagramPacket packet2=new DatagramPacket(data2, data2.length);
//2.接收服務(wù)器響應(yīng)的數(shù)據(jù)
socket.receive(packet2);
//3.讀取數(shù)據(jù)
String reply=new String(data2, 0, packet2.getLength());
System.out.println("我是客戶端漱办,服務(wù)器說:"+reply);
//4.關(guān)閉資源
socket.close();
}
}
下面進(jìn)行測試
同樣的这刷,需要先啟動服務(wù)端再啟動客戶端
啟動完可以看到服務(wù)端控制臺顯示如下
下面來啟動客戶端
再來看看服務(wù)端
這樣就實現(xiàn)了服務(wù)端與單個客戶端的通信
下面來通過多線程實現(xiàn)服務(wù)端與多個客戶端的通信
服務(wù)器端線程處理類UDPServerThread
public class UDPServerThread extends Thread{
DatagramPacket packet;
DatagramSocket socket;
public UDPServerThread(DatagramPacket packet, DatagramSocket socket) {
super();
this.packet = packet;
this.socket = socket;
}
@Override
public void run(){
try {
//獲取客戶端信息
byte[] data = packet.getData();
String info1 = new String(data, 0, packet.getLength());
System.out.println("我是服務(wù)器,客戶端說:" + info1);
//響應(yīng)客戶端
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data1 = "歡迎您娩井!".getBytes();
DatagramPacket packet1 = new DatagramPacket(data1, data1.length, address, port);
socket.send(packet1);
}catch (IOException e) {
e.printStackTrace();
}
}
}
服務(wù)端代碼相應(yīng)改下
public class UDPServer {
public static void main(String[] args) throws IOException {
/*
* 接收客戶端發(fā)送的數(shù)據(jù)
*/
//1.創(chuàng)建服務(wù)器端DatagramSocket暇屋,指定端口
DatagramSocket socket=new DatagramSocket(8800);
//2.創(chuàng)建數(shù)據(jù)報,用于接收客戶端發(fā)送的數(shù)據(jù)
byte[] data =new byte[1024];//創(chuàng)建字節(jié)數(shù)組洞辣,指定接收的數(shù)據(jù)包的大小
System.out.println("****服務(wù)器啟動咐刨,等待客戶端連接****");
int count=1;
while(true) {
DatagramPacket packet = new DatagramPacket(data, data.length);
socket.receive(packet);
UDPServerThread serverThread = new UDPServerThread(packet, socket);
serverThread.start();
System.out.println("客戶端數(shù)量:" + count++);
}
}
}
這里的socket.receive()
如果不寫的話會產(chǎn)生死循環(huán)
同樣客戶端也是不用改
下面進(jìn)行測試
1.啟動服務(wù)端
2.啟動一個客戶端
3.服務(wù)端并沒有停止,并且接收到客戶端傳來的信息
4.再啟動下客戶端扬霜,控制臺顯示客戶端數(shù)量為2定鸟,證明可以與對個客戶端通信
注意:UDP多個客戶端通信的時候socket是不關(guān)閉的,我也試過關(guān)閉的話
會出異常Exception in thread "main" java.net.SocketException: socket closed
因為在UDPServer類的while是死循環(huán)著瓶,無法重新創(chuàng)建socket,所以這里不能關(guān)閉socket,否則無法進(jìn)行下一個客戶端的監(jiān)聽
總結(jié):
UDP相較于TCP联予,不需要進(jìn)行復(fù)雜的設(shè)定輸入輸出流,只需要設(shè)定數(shù)據(jù)報材原,即DatagramPacket
沸久。而TCP的發(fā)送以及接收消息,是通過socket.getInputStream()
或者getOutputStream()
方法余蟹,而UDP是直接設(shè)置了卷胯,DatagramSocket
,通過其send()
或者receive()
方法來接收或發(fā)送消息威酒。
TCP的關(guān)鍵組成有服務(wù)端的ServerSocket.accept()
方法诵竭,這個方法是直到接收到了客戶端的連接才會返回一個Socket
,用于接下來的輸入和輸出兼搏。