(一)Udp通信
網絡通訊協議:
udp協議
tcp協議
在java中不管是用哪種協議通信,計算機與計算機之間的通信都統稱爲Socket(插座)通信,通信的兩端 計算機都必須要安裝上Socket。
在不同的協議下應該用不同的Socket.
udp協議的特點
1.將數據及其源和目的封裝爲數據包,不需要建立連接。
2.每個數據包大小限制在64k中,基於數據包進行傳輸。
3.因爲無連接,所以不可靠(會出現數據丟失)。
4.因爲不需要建立連接,所有速度快。
5.udp協議不分客戶端與服務器,只分發送端與接收端。
玩遊戲是udp,卡機其實是數據包丟失。
比如:對講機、飛Q通信、視頻會議
udp通信:
DatagramSocket(udp協議的服務類)
DatagramPacket(數據包類)
DatagramPacket(byte[] buf,int length,InetAddress address,int port)
buf:當前數據的字節數組的表現形式。
length:字節數組的長度。
address:發送的ip地址。
port:端口號。
//udp的發送端
public class demo1 {
public static void main(String[] args) throws IOException {
//第一步:建立udp的服務
DatagramSocket socket=new DatagramSocket();
//第二步:準備數據,把數據封裝到數據包中
String data="這個是我的第一個udp的例子!";
byte[] buf=data.getBytes();
DatagramPacket packet=new DatagramPacket(buf, buf.length,InetAddress.getLocalHost(),9090);//InetAddress.getByName(ip地址)指定接收端ip地址
//第三步:調用udp的服務,發送數據
socket.send(packet);
//第四步:關閉資源(釋放端口號)(io中關閉資源是釋放資源文件)
socket.close();
}
}
//先啓動接收端,再啓動發送端
//udp服務的接收端
public class demo2_receiver {
public static void main(String[] args) throws IOException {
//第一步:建立udp的服務,並且壓迫監聽一個端口
DatagramSocket socket=new DatagramSocket(9090);
//第二步:準備一個空的數據包
byte[] buf=new byte[1024];
DatagramPacket packet=new DatagramPacket(buf,buf.length);
//第三步:調用udp的服務接收數據包,數據其實是存入了字節數組中,數據包依賴於字節數組存儲數據。
socket.receive(packet);//receive()方法是一個阻塞型方法,如果沒有接收到數據,會一直等待下去
//Scanner的next方法是阻塞型方法,只有接收到數據纔會繼續進行,否則會一致等待
System.out.println(packet.getAddress().getHostAddress()+"接收端接收到的數據:"+new String(buf,0,packet.getLength()));//getLength是獲取本次接收到的字節個數);
//packet.getAddress().getHostAddress()獲取到發送方的ip地址 getAddress是獲取對方的IP地址對象 getHostAddress()獲取ip地址
//第四步:關閉資源
socket.close();
}
}
udp協議可能導致數據丟失的情況:
1.帶寬不足。
2.cpu處理能力不足。
(二)Tcp通信
tcp協議的特點:
1.面向連接,在傳輸數據之前一定要建立連接,tcp的客戶端一旦建立,馬上要與服務端建立連接。
2.在連接中可以傳輸大量數據,基於IO流進行傳輸。
3.通過三次握手機制連接,是可靠協議(保證數據傳輸的完整性)。
4.因爲tcp是面向連接的,所有效率稍低。
5.tcp協議分客戶端與服務端。
tcp協議下的Socket:
Socket(客戶端類)
ServerSocket(服務端類)
比如:qq文件傳輸、打電話
//客戶端
public class demo1 {
public static void main(String[] args) throws UnknownHostException, IOException {
//第一步:建立tcp的客戶端服務
Socket socket=new Socket(InetAddress.getLocalHost(),9090);
//第二步:準備數據(基於io流傳輸數據),獲取對應的流 通道
String data="這是第一個tcp例子";
//數據相對當前客戶端來說是流出的,用輸出流
OutputStream outputStream=socket.getOutputStream();
//第三步:把數據寫出
outputStream.write(data.getBytes());
//客戶端要接收服務端回送的數據
//獲取socket的輸入流
InputStream input=socket.getInputStream();
byte[] buf=new byte[1024];
int length=input.read(buf);
System.out.println("客戶端接收到的內容:"+new String(buf,0,length));
//第四步:關閉資源
//outputStream.close();//io中關閉輸出流是因爲佔用了文件資源,這裏不需要關,從socket中獲得,直接關socket即可
socket.close();
}
}
//先啓動服務端,再啓動接收端
//服務端 ServerSocket
public class demo2_server {
public static void main(String[] args) throws IOException {
//第一步:建立tcp的服務端
ServerSocket serverSocket=new ServerSocket(9090);
//第二步:接受客戶端的連接
Socket socket=serverSocket.accept();//accept是阻塞型方法,沒有客戶端與其連接時,一直等待下去。
//第三步:獲取對應的輸入流通道
InputStream inputStream=socket.getInputStream();
//第四步:通過輸入流通道讀取數據
byte[] buf=new byte[1024];
int length=inputStream.read(buf);
System.out.println("服務端接收到的數據:"+new String(buf,0,length));
//服務端給客戶端回送數據
//獲取socket的輸出流
OutputStream output=socket.getOutputStream();
output.write("客戶端辛苦啦。。".getBytes());
//第五步:關閉資源
serverSocket.close();
}
}
練習:
/*
一個服務端可以與多個客戶端連接。
需求:編寫一個服務端可以與多個客戶端進行連接,客戶端一旦連接成功,馬上輸送一張圖片數據給客戶端。
再統計接收到的客戶端數,同一個客戶端只算一次。
服務端需要多線程
*/
//多線程服務端 提供圖片
public class demo3 extends Thread{
Socket socket;
static HashSet ips=new HashSet();
public demo3(Socket socket) {
this.socket=socket;
}
@Override
public void run() {
try {
//第一步:建立tcp服務端的服務
//ServerSocket serverSocket=new ServerSocket(9090);
//第二步:接收用戶的請求連接
//Socket socket=serverSocket.accept();
//第三步:獲取Socket輸出字節流(服務端輸出圖片)
OutputStream socketOut=socket.getOutputStream();
//第四步:獲取文件的輸入流,讀物文件的數據,把文件數據寫出給客戶端
FileInputStream fileInputStream=new FileInputStream("E:\\1.jpg");
byte[] buf=new byte[1024];
int length=0;
while((length=fileInputStream.read(buf))!=-1) {
socketOut.write(buf,0,length);
}
String ip=socket.getInetAddress().getHostAddress();//獲取到對方(接收方)的ip地址
if(ips.add(ip)) {//如果可以存儲到集合中,意味着這是一個新的ip地址
System.out.println("恭喜"+ip+"下載成功、、");
System.out.println("當前下載的人數:"+ips.size());
}
//第五步:關閉資源
fileInputStream.close();
socket.close();//每個socket都會佔用端口,用完需關閉
}catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
//第一步:建立tcp服務端的服務,只需要一個服務端對象
ServerSocket serverSocket=new ServerSocket(9090);
while(true) {
Socket socket=serverSocket.accept();//不斷地接收用戶的連接請求
new demo3(socket).start();//如果產生了一個Socket就意味着有一個客戶與服務端連接,馬上開啓一個線程爲其服務
}
}
}
//客戶端接收圖片
public class demo4 {
public static void main(String[] args) throws IOException {
//建立tcp的客戶端服務
Socket socket=new Socket(InetAddress.getLocalHost(),9090);//客戶端寫的是本機
//獲取socket的輸入流對象
InputStream inputStream=socket.getInputStream();
//建立一個文件的輸出字節流對象
FileOutputStream fileOutputStream=new FileOutputStream("E:\\小貓.jpg");
//邊讀邊寫
byte[] buf=new byte[1024];
int length=0;
while((length=inputStream.read(buf))!=-1) {
fileOutputStream.write(buf,0,length);
}
//關閉資源
fileOutputStream.close();
socket.close();
}
}