Java實(shí)現(xiàn)UDP通信

UDP(User Datagram Protocol)平痰,即用戶數(shù)據(jù)報(bào)協(xié)議握巢,UDP只提供數(shù)據(jù)的不可靠傳遞靶剑,它一旦把應(yīng)用程序發(fā)給網(wǎng)絡(luò)層的數(shù)據(jù)發(fā)送出去畜侦,就不保留數(shù)據(jù)備份(所以UDP有時(shí)候也被認(rèn)為是不可靠的數(shù)據(jù)報(bào)協(xié)議)。UDP在IP數(shù)據(jù)報(bào)的頭部僅僅加入了復(fù)用和數(shù)據(jù)校驗(yàn)(字段)掐场。

雖然UDP被認(rèn)為是不可靠的往扔,但是可以通過應(yīng)用程序來負(fù)責(zé)傳輸?shù)目煽啃裕鏔TP等都是用了UDP協(xié)議熊户。另外一個(gè)特點(diǎn)是UDP是基于數(shù)據(jù)包萍膛,也就是說數(shù)據(jù)會(huì)被打成包發(fā)送。所以包的大小會(huì)有限制嚷堡,一般認(rèn)為最大是64KB

Java中UDP主要涉及了DatagramPacket和 DatagramSocket兩個(gè)類蝗罗。前者被認(rèn)為是信息的載體,后者被認(rèn)為是收發(fā)的實(shí)體。也就是串塑,DatagramPacket攜帶數(shù)據(jù)沼琉,并通過DatagramSocket收發(fā)。

下面就來實(shí)現(xiàn)一下服務(wù)端與客戶端拟赊。

服務(wù)端:

public class UDPService {
    public static final String SERVICE_IP = "127.0.0.1";

    public static final int SERVICE_PORT = 10101;

    public static final int MAX_BYTES = 2048;

    private DatagramSocket service;

    public static void main(String[] args) {
        UDPService udpService = new UDPService();
        udpService.startService(SERVICE_IP,SERVICE_PORT);//啟動(dòng)服務(wù)端
    }

    private void startService(String ip, int port) {
        try {
            //包裝IP地址
            InetAddress address = InetAddress.getByName(ip);
            //創(chuàng)建服務(wù)端的DatagramSocket對(duì)象刺桃,需要傳入地址和端口號(hào)
            service = new DatagramSocket(port,address);

            byte[] receiveBytes = new byte[MAX_BYTES];
            //創(chuàng)建接受信息的包對(duì)象
            DatagramPacket receivePacket = new DatagramPacket(receiveBytes,receiveBytes.length);

            //開啟一個(gè)死循環(huán)粹淋,不斷接受數(shù)據(jù)
            while(true){
                try{
                    //接收數(shù)據(jù)吸祟,程序會(huì)阻塞到這一步,直到收到一個(gè)數(shù)據(jù)包為止
                    service.receive(receivePacket);
                }catch (Exception e){
                    e.printStackTrace();
                }

                //解析收到的數(shù)據(jù)
                String receiveMsg = new String(receivePacket.getData(),0,receivePacket.getLength());
                //解析客戶端地址
                InetAddress clientAddress = receivePacket.getAddress();
                //解析客戶端端口
                int clientPort = receivePacket.getPort();

                //組建響應(yīng)信息
                String response = "Hello world " + System.currentTimeMillis() + " " + receiveMsg;
                byte[] responseBuf = response.getBytes();
                //創(chuàng)建響應(yīng)信息的包對(duì)象桃移,由于要發(fā)送到目的地址屋匕,所以要加上目的主機(jī)的地址和端口號(hào)
                DatagramPacket responsePacket = new DatagramPacket(responseBuf,responseBuf.length,clientAddress,clientPort);

                try{
                    //發(fā)送數(shù)據(jù)
                    service.send(responsePacket);
                }catch (Exception e){
                    e.printStackTrace();
                }

            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //關(guān)閉DatagramSocket對(duì)象
            if(service!=null){
                service.close();
                service = null;
            }
        }
    }

}

客戶端:

public class UDPClient {
    private DatagramSocket client;

    public static void main(String[] args){
        UDPClient client = new UDPClient();
        Scanner scanner = new Scanner(System.in);
        //建立死循環(huán),不斷發(fā)送數(shù)據(jù)
        while(true){
            String msg = scanner.nextLine();
            if("##".equals(msg))
                break;
            //打印響應(yīng)的數(shù)據(jù)
            System.out.println(client.sendAndReceive(UDPService.SERVICE_IP,UDPService.SERVICE_PORT,msg));
        }
    }

    private String sendAndReceive(String ip, int port, String msg) {
        String responseMsg = "";

        try {
            //創(chuàng)建客戶端的DatagramSocket對(duì)象借杰,不必傳入地址和對(duì)象
            client = new DatagramSocket();
            byte[] sendBytes = msg.getBytes();
            //封裝要發(fā)送目標(biāo)的地址
            InetAddress address = InetAddress.getByName(ip);
            //封裝要發(fā)送的DatagramPacket的對(duì)象过吻,由于要發(fā)送到目的主機(jī),所以要加上地址和端口號(hào)
            DatagramPacket sendPacket = new DatagramPacket(sendBytes,sendBytes.length,address,port);

            try {
                //發(fā)送數(shù)據(jù)
                client.send(sendPacket);
            }catch (Exception e){
                e.printStackTrace();
            }

            byte[] responseBytes = new byte[UDPService.MAX_BYTES];
            //創(chuàng)建響應(yīng)信息的DatagramPacket對(duì)象
            DatagramPacket responsePacket = new DatagramPacket(responseBytes,responseBytes.length);
            try {
                //等待響應(yīng)信息蔗衡,同服務(wù)端一樣纤虽,客戶端也會(huì)在這一步阻塞,直到收到一個(gè)數(shù)據(jù)包
                client.receive(responsePacket);
            }catch (Exception e){
                e.printStackTrace();
            }
            
            //解析數(shù)據(jù)包內(nèi)容
            responseMsg = new String(responsePacket.getData(),0,responsePacket.getLength());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //關(guān)閉客戶端
            if(client != null){
                client.close();
                client = null;
            }
        }

        return responseMsg;
    }
}

最后介紹一下涉及的幾個(gè)類绞惦。

1. InetAddress
InetAddress類用來代表IP地址逼纸,InetAddress下還有2個(gè)子類:Inet4Address、Inet6Address,它們分別代表IPv4地址和IPv6地址济蝉。

這個(gè)類沒有提供公開的構(gòu)造方法杰刽,而是提供了如下兩個(gè)靜態(tài)方法來獲取InetAddress實(shí)例:

InetAddress getByAddress(byte[] addr)//根據(jù)原始IP地址來獲取對(duì)應(yīng)的InetAddress對(duì)象
InetAddress getByName(String host)//根據(jù)主機(jī)獲取對(duì)應(yīng)的InetAddress對(duì)象

其他一些方法

String getCanonicalHostName()//獲取此 IP 地址的全限定域名。
String getHostAddress()//返回該InetAddress實(shí)例對(duì)應(yīng)的IP地址字符串(以字符串形式)王滤。
String getHostName()//獲取此 IP 地址的主機(jī)名贺嫂。
InetAddress getLocalHost() //獲取本機(jī)IP地址對(duì)應(yīng)的InetAddress實(shí)例.
boolean isReachable(int timeout) //測試是否可以到達(dá)該地址

2. DatagramPacket
有以下幾種構(gòu)造

DatagramPacket(byte[] buf, int length)
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
DatagramPacket(byte[] buf, int offset, int length)
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)
DatagramPacket(byte[] buf, int length, SocketAddress address)

方法:

InetAddress getAddress() //獲取地址,可能是發(fā)送方的也可能是接收方的
byte[] getData()  //獲取數(shù)據(jù)
int getLength()  //獲取長度
int getOffset() //獲取偏移量
int getPort()  //獲取端口
SocketAddress getSocketAddress()  //獲取SocketAddress 
void setAddress(InetAddress iaddr)  //設(shè)置地址
void setData(byte[] buf)  //設(shè)置數(shù)據(jù)
void setData(byte[] buf, int offset, int length)  //設(shè)置數(shù)據(jù)雁乡,指定長度與偏移
void setLength(int length) //設(shè)置長度
void setPort(int iport) //設(shè)置端口
void setSocketAddress(SocketAddress address)  //設(shè)置SocketAddress 

3. DatagramSocket
該類常用的構(gòu)造方法如下:

DatagramSocket()  //默認(rèn)本機(jī)ip地址第喳,隨機(jī)選一個(gè)可用端口
DatagramSocket(int port)  //默認(rèn)本機(jī)IP地址,指定端口
DatagramSocket(int port, InetAddress laddr)  //指定ip和端口

通常第一個(gè)可作為客戶端踱稍,作為服務(wù)端一般需要指定端口墩弯,確保其他客戶端可以發(fā)送到該服務(wù)器.

常用方法:

void send(DatagramPacket p)  //發(fā)送一個(gè)數(shù)據(jù)包,數(shù)據(jù)包要包含目的主機(jī)的地址和端口
void receive(DatagramPacket p)  //接受一個(gè)數(shù)據(jù)包寞射,數(shù)據(jù)包只要包含一個(gè)byte數(shù)組和長度即可

另外還有一個(gè)connect(InetAddress address,int port)渔工,我們都知道UDP是面向無連接的,這個(gè)connect方法其實(shí)只是指定一個(gè)主機(jī)桥温,相當(dāng)于綁定引矩,但沒有任何實(shí)質(zhì)連接,而接下來都只能從這個(gè)主機(jī)收發(fā)數(shù)據(jù),而且在發(fā)數(shù)據(jù)時(shí)旺韭,DatagramPacket 不必?cái)y帶目的主機(jī)的地址和端口號(hào)氛谜。并且如果DatagramPacket攜帶的主機(jī)地址與端口號(hào)和connect綁定的不一致,會(huì)拋出IllegalArgumentException区端。

另外getInnetAddress和getPort都是返回connect后綁定的主機(jī)信息值漫,若要取到本機(jī)的需要調(diào)用getLocalPort和getLocalAddress。若沒有connect织盼,則getInnetAddress和getPort返回null與-1.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末杨何,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子沥邻,更是在濱河造成了極大的恐慌危虱,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件唐全,死亡現(xiàn)場離奇詭異埃跷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)邮利,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門弥雹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人延届,你說我怎么就攤上這事剪勿。” “怎么了祷愉?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵窗宦,是天一觀的道長。 經(jīng)常有香客問我二鳄,道長赴涵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任订讼,我火速辦了婚禮髓窜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘欺殿。我一直安慰自己寄纵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布脖苏。 她就那樣靜靜地躺著程拭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棍潘。 梳的紋絲不亂的頭發(fā)上恃鞋,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天崖媚,我揣著相機(jī)與錄音,去河邊找鬼恤浪。 笑死畅哑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的水由。 我是一名探鬼主播荠呐,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼砂客!你這毒婦竟也來了泥张?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤鞭盟,失蹤者是張志新(化名)和其女友劉穎圾结,沒想到半個(gè)月后瑰剃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體齿诉,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年晌姚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了粤剧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挥唠,死狀恐怖抵恋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宝磨,我是刑警寧澤弧关,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站唤锉,受9級(jí)特大地震影響世囊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜窿祥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一株憾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晒衩,春花似錦嗤瞎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至靠胜,卻和暖如春掉瞳,著一層夾襖步出監(jiān)牢的瞬間届惋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國打工菠赚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脑豹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓衡查,卻偏偏與公主長得像瘩欺,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拌牲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353