Java Socket 通信(二)

1. 前言

基於傳輸層協議的UDP的網絡通信是不可靠的、無序的、無差錯控制的。使用DatagramSocket類表示UDP通信節點的套接字,使用DatagramPacket表示節點之間發送和接收的數據報。基於UDP通信的節點之間不需要建立任何連接。組播也是一種基於UDP的通信。

2. DatagramSocket 類

每個DatagramSocket對象會綁定本地IP地址和一個UDP端口號,它可以和任意其他DatagramSocket對象之間有通信行爲,但不會建立實時的網絡連接。

2.1 構造DatagramSocket

  • DatagramSocket():創建一個DatagramSocket對象,但沒有設置綁定的端口號,相當於綁定了本地的任意一個可用的端口
  • DatagramSocket(int port):創建一個DatagramSocket對象,並綁定本地端口號port。
  • DatagramSocket(int port,InetAddress addr):創建一個DatagramSocket對象,並綁定本地地址addr和端口號port。IP地址爲網絡接口地址,如果設置爲0.0.0.0,就是通配地址
  • DatagramSocket(SocketAddress bindaddr):創建一個DatagramSocket對象,並綁定套接字地址bindaddr
  • void bind(SocketAddress addr):如果使用無參數構造方法創建一個DatagramSocket對象,可以進行一些選項的設置,之後使用bind方法綁定IP地址和端口

2.2 DatagramSocket 類的常用方法

2.2.1 發送數據

DatagramSocket的send方法負責發送一個數據報,該方法的定義:void send(DatagramPacket p),其中參數p包含要發送的數據、數據長度、目的IP地址和端口。

//1234本地端口號
DatagramSocket ds = new DatagramSocket(1234);
InetAddress receiver = InetAddress.getLocalHost();
byte[] b = "send a message".getBytes();
//5678目的端口號
DatagramPacket dp = new DatagramPacket(b, b.length, receiver, 5678);
ds.send(dp)

2.2.2 接收數據

void receive(DatagramPacket p): DatagramSocket類的receive方法用於接收消息。消息並不是以返回值的形式得到的,而是存在於p緩衝區中,p中還包括髮送者的IP地址和端口信息。
receive方法是一個阻塞的方法,調用receive的時候,如果沒有收到數據報會一直阻塞,直到收到一個數據報。

byte[] b = new byte[100];
DatagramPacket dp = new DatagramPacket(b,100);
DatagramSocket ds = new DatagramSocket(1234);
ds.receive(dp);

2.2.3 建立固定通信關係

UDP的節點之間是不建立實時連接的,但是卻可以建立這樣一種固定關係:一個節點的DatagramSocket,只能同另一個固定的節點(由IP地址和端口號確定)進行通信

DatagramSocket ds = new DatagramSocket();
ds.connect(InetSocketAddress.createUnresolved("www.foo.com",1234));

2.2.4 解除固定通信關係

DatagramSocket ds = new DatagramSocket(1234);
...
ds.disconnect();

2.2.5 關閉 DatagramSocket

void close():關閉,並釋放所有相關的資源。

...
ds.close();

補充:

  • isBound(): 判斷DatagramSocket對象的綁定狀態
  • isConnected():判斷DatagramSocket對象是否處於固定通信連接狀態
  • isClosed():判斷DatagramSocket對象是否關閉

2.3 設置 DatagramSocket 的選項

選項 含義
SO_BROADCAST 廣播地址
SO_TIMEOUT 設定接收數據報的等待超時時間
SO_RECBUF 表示接收數據緩衝區的大小
SO_REUSEADDR 表示是否允許重用DatagramSocket所綁定的本地地址

3. DatagramPacket 類

3.1 DatagramPacket 類的構造方法

  1. 發送數據的DatagramPacket對象
  • DatagramPacket(byte[] buf, int length, InetAddress address, int port):用於發送的DatagramPacket對象,包括目的節點的IP地址address和端口號port。
  • DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):設置了發送數據的起始位置爲:data[offset]
  • DatagramPacket(byte[] buf, int offset, int length, SocketAddress address):將目的地址和端口號合併在了一起
  • DatagramPacket(byte[] buf, int length, SocketAddress address)
  1. 接收數據的DatagramPacket對象
  • DatagramPacket(byte[] buf, int length):用於接收數據,只需設置接收緩衝區buf,並指定讀取的字節數length
  • DatagramPacket(byte[] buf, int offset, int length):設置了接收緩衝區buf的起始位置偏移量

3.2 DatagramPacket 類的常用方法

3.2.1 查詢 DatagramPacket

  • InetAddress getAddress():對於發送數據報,返回目的節點的主機IP地址。對於接收數據報,返回的是數據的來源主機IP地址。總之,返回的是遠程主機的IP地址
  • int getPort():返回的是遠程主機的UDP端口號。
  • byte[] getData():對於發送數據報,返回的是發送緩衝區從offset開始的數據,對於接收數據,返回的是接收緩衝區的數據。
  • int getOffset():返回的是發送或者接收緩衝區的數據偏移量offset
  • int getLength():返回的是發送或者接收緩衝區中數據的長度
  • SocketAddress getSocketAddress():返回的是遠程主機IP地址和UDP端口號

3.2.2 設置 DatagramPacket

  • void setData(byte[] buf):設置數據報的緩衝區數據
  • void setData(byte[] data, int offset, int length):數據從buf[offset]開始,長度爲length
  • void setAddress(InetAddress iaddr):發送數據報時,使用參數iaddr設置目的主機的IP地址
  • void setPort(int iport):發送數據報時,使用參數iport設置目的主機的UDP端口號。
  • void setSocketAddress(SocketAddress address):發送數據報時,使用參數iaddr設置目的主機的套接字地址
  • void setLength(int length):設置數據報的長度

4. 程序實例

4.1 UDPService

package udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.text.SimpleDateFormat;
import java.util.Date;

public class UDPService {

    DatagramSocket ds = null;

    public UDPService() throws Exception{
        ds = new DatagramSocket(5678);
        System.out.println("服務啓動");
    }

    public void service(){
        new Thread(){
            public void run(){
                while(true){
                    try {
                        byte[] b = new byte[100];
                        DatagramPacket dp =  new DatagramPacket(b, b.length);
                        ds.receive(dp);
                        String msg = new String(b, 0, dp.getLength());
                        System.out.println("從" + dp.getAddress() + ":" + dp.getPort() + "收到:" + msg);
                        if(msg.equalsIgnoreCase("date")){
                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年mm月dd日");
                            dp.setData(("date:" + sdf.format(new Date())).getBytes());
                        }else if(msg.equalsIgnoreCase("time")){
                            SimpleDateFormat sdf = new SimpleDateFormat("HH:MM:SS");
                            dp.setData(("time:"+ sdf.format(new Date())).getBytes());
                        }
                        ds.send(dp);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

    public static void main(String[] args) throws Exception {
        new UDPService().service();
    }
}

4.2 UDPClient

package udp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;

public class UDPClient {
    public static void main(String[] args) {
        try {
            InetAddress server = InetAddress.getByName("localhost");
            DatagramSocket ds = new DatagramSocket();
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            String msg = null;
            while((msg = br.readLine())!= null){
                byte[] b = msg.getBytes();
                DatagramPacket dp = new DatagramPacket(b, b.length, server, 5678);
                ds.send(dp);
                DatagramPacket sp = new DatagramPacket(new byte[100], 100);
                ds.receive(sp);
                msg = new String(sp.getData(),0, sp.getLength());
                if(msg.equalsIgnoreCase("bye")){
                    break;
                }
                System.out.println("服務器:" + msg);
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {

        }
    }
}

4.3 運行截圖

在這裏插入圖片描述在這裏插入圖片描述

5. 組播Socket

組播也叫多播,組播組內的所有主機共享同一個D類IP地址,這種地址稱爲組播地址。一臺主機可以自由決定何時加入或離開一個組播組。組播地址是範圍在244.0.0.0~239.255.255.255之間的IP地址

5.1 MulticastSocket類

MulticastSocket類實際上是DatagramSocket類的子類,包含了DatagramSocket類的所有域和方法,還額外定義了與組播有關的一些方法。

5.2 構造 MulticastSocket

  • MulticastSocket():創建MulticastSocket對象
  • MulticastSocket(int port):創建綁定到端口port的MulticastSocket對象
  • MulticastSocket(SocketAddress bindAddress):創建綁定到端口套接字地址bindAddress的MulticastSocket對象。

5.3 MulticastSocket 的常用方法

5.3.1 加入組播組

  • void joinGroup(InetAddress mcastaddr) 其中mcastaddr是D類組播IP地址,如果要接收發送到組播組的數據,就必須要加入組播組
  • void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) 其中netIf參數設置了使用哪個網絡接口加入到組播組

5.3.2 離開組播組

  • void leaveGroup(InetAddress mcastaddr) 其中,mcastaddr是D類組播IP地址,調用leaveGroup之前,MulticastSocket對象應該已經加入了某個組播組。
  • void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) 其中netIf設置了哪個網絡接口離開組播組。

5.3.3 設置網絡接口

  • void setInterface(InetAddress inf) 假設主機有多個網絡接口,通過該方法設置究竟是哪一個接口參與組播操作。

5.3.4 查詢網絡接口

  • InetAddress getInterface() 此方法返回用於組播的網路接口地址

6. 程序實例

6.1 MulticastSender

package udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;

public class MulticastSender {
    public static void main(String[] args) throws IOException {
        InetAddress group = InetAddress.getByName("226.0.0.1");
        MulticastSocket ms = new MulticastSocket();
        ms.joinGroup(group);
        String msg = "Hello, everybody!";
        byte[] b = msg.getBytes();
        DatagramPacket dp = new DatagramPacket(b, b.length, group, 5678);
        ms.send(dp);
        System.out.println("發送問候給:"+ group + ":" + 5678);
        ms.close();
    }
}

6.2 MulticastReceiver

package udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class MulticastReceiver {
    public static void main(String[] args) throws IOException {
        InetAddress group = InetAddress.getByName("226.0.0.1");
        MulticastSocket ms = new MulticastSocket(5678);
        ms.joinGroup(group);
        byte[] b = new byte[100];
        DatagramPacket dp = new DatagramPacket(b, b.length);
        ms.receive(dp);
        String str = new String(dp.getData(), 0, dp.getLength());
        System.out.println("從" + dp.getAddress().toString() + ":"+dp.getPort()+"收到消息");
        System.out.println(str);
        ms.leaveGroup(group);
        ms.close();
    }
}

6.3 運行截圖

在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章