Java UDP Socket编程

UDP协议

UDP协议提供的服务不同于TCP协议的端到端服务,它是面向非连接的,属不可靠协议,UDP套接字在使用前不需要进行连接。实际上,UDP协议实现了两个功能:

1)在IP协议的基础上添加了端口;

2)对传输过程中可能产生的数据错误进行了检测,并抛弃已经损坏的数据。

UDP的Java支持

Java通过DatagramPacket类和DatagramSocket类来使用UDP套接字,客户端和服务器端都通过DatagramSocket的send()方法和receive()方法来发送和接收数据,用DatagramPacket来包装需要发送或者接收到的数据。发送信息时,Java创建一个包含待发送信息的DatagramPacket实例,并将其作为参数传递给DatagramSocket实例的send()方法;接收信息时,Java程序首先创建一个DatagramPacket实例,该实例预先分配了一些空间,并将接收到的信息存放在该空间中,然后把该实例作为参数传递给DatagramSocket实例的receive()方法。在创建DatagramPacket实例时,要注意:如果该实例用来包装待接收的数据,则不指定数据来源的远程主机和端口,只需指定一个缓存数据的byte数组即可(在调用receive()方法接收到数据后,源地址和端口等信息会自动包含在DatagramPacket实例中),而如果该实例用来包装待发送的数据,则要指定要发送到的目的主机和端口。

UDP的通信建立的步骤

UDP客户端首先向被动等待联系的服务器发送一个数据报文。一个典型的UDP客户端要经过下面三步操作:

1、创建一个DatagramSocket实例,可以有选择地对本地地址和端口号进行设置,如果设置了端口号,则客户端会在该端口号上监听从服务器端发送来的数据;

2、使用DatagramSocket实例的send()和receive()方法来发送和接收DatagramPacket实例,进行通信;

3、通信完成后,调用DatagramSocket实例的close()方法来关闭该套接字。

由于UDP是无连接的,因此UDP服务端不需要等待客户端的请求以建立连接。另外,UDP服务器为所有通信使用同一套接字,这点与TCP服务器不同,TCP服务器则为每个成功返回的accept()方法创建一个新的套接字。一个典型的UDP服务端要经过下面三步操作:

1、创建一个DatagramSocket实例,指定本地端口号,并可以有选择地指定本地地址,此时,服务器已经准备好从任何客户端接收数据报文;

2、使用DatagramSocket实例的receive()方法接收一个DatagramPacket实例,当receive()方法返回时,数据报文就包含了客户端的地址,这样就知道了回复信息应该发送到什么地方;

3、使用DatagramSocket实例的send()方法向服务器端返回DatagramPacket实例。

这里有一点需要注意:

UDP程序在receive()方法处阻塞,直到收到一个数据报文或等待超时。由于UDP协议是不可靠协议,如果数据报在传输过程中发生丢失,那么程序将会一直阻塞在receive()方法处,这样客户端将永远都接收不到服务器端发送回来的数据,但是又没有任何提示。为了避免这个问题,我们在客户端使用DatagramSocket类的setSoTimeout()方法来制定receive()方法的最长阻塞时间,并指定重发数据报的次数,如果每次阻塞都超时,并且重发次数达到了设置的上限,则关闭客户端。

下面给出一个客户端服务端UDP通信的Demo(没有用多线程),该客户端在本地2222端口监听接收到的数据,并将字符串"Hello UDPserver"发送到本地服务器的3000端口,服务端在本地3222端口监听接收到的数据,如果接收到数据,则返回字符串"Hello UDPclient"到该客户端的2222端口。在客户端,由于程序可能会一直阻塞在receive()方法处,因此这里我们在客户端用DatagramSocket实例的setSoTimeout()方法来指定receive()的最长阻塞时间TIMEOUT ,并设置重发数据的次数MAXNUM ,如果最终依然没有接收到从服务端发送回来的数据,我们就关闭客户端。

 1 import java.io.IOException;
 2 import java.io.InterruptedIOException;
 3 import java.net.DatagramPacket;
 4 import java.net.DatagramSocket;
 5 import java.net.InetAddress;
 6
 7 public class UdpClient {
 8     private static final int MAXNUM = 5; // 设置重发数据的最多次数
 9     private static final int TIMEOUT = 5000;  //设置接收数据的超时时间
10     private static final int CLIENT_PORT = 2222;
11     private static final int SERVER_PORT = 3222;
12     private static final int REV_SIZE = 1024; //接收数据的存储空间大小
13
14     public static void main(String[] args) throws IOException {
15         String str_send = "Hello UDPserver"; //要发送的字串
16         byte[] buf_rev = new byte[REV_SIZE];     //要接收的存储空间
17
18         /*第一步 实例化DatagramSocket*/
19         DatagramSocket mSoc = new DatagramSocket(CLIENT_PORT);
20         mSoc.setSoTimeout(TIMEOUT);              //设置接收数据时阻塞的最长时间
21
22         /*第二步 实例化用于发送的DatagramPacket和用于接收的DatagramPacket*/
23         InetAddress inetAddress = InetAddress.getLocalHost();
24         DatagramPacket data_send = new DatagramPacket(str_send.getBytes(),
25                 str_send.length(), inetAddress, SERVER_PORT);
26
27         DatagramPacket data_rev = new DatagramPacket(buf_rev, REV_SIZE);
28
29
30         /*第三步 DatagramPacket send发送数据,receive接收数据*/
31         int send_count = 0; // 重发数据的次数
32         boolean revResponse = false; // 是否接收到数据的标志位
33         while (!revResponse && send_count < MAXNUM) {
34             try {
35                 mSoc.send(data_send); //发送数据
36                 mSoc.receive(data_rev);//接收数据
37                 if (!data_rev.getAddress().getHostAddress()
38                         .equals(InetAddress.getLocalHost().getHostAddress())) {
39                     throw new IOException(
40                             "Received packet from an umknown source");
41                 }
42                 revResponse = true;
43             } catch (InterruptedIOException e) {
44                 // 如果接收数据时阻塞超时,重发并减少一次重发的次数
45                 send_count += 1;
46                 System.out.println("Time out," + (MAXNUM - send_count)
47                         + " more tries...");
48             }
49         }
50         if (revResponse) {
51             // 如果收到数据,则打印出来
52             System.out.println("client received data from server:");
53             String str_receive = new String(data_rev.getData(), 0,
54                     data_rev.getLength())
55                     + " from "
56                     + data_rev.getAddress().getHostAddress()
57                     + ":"
58                     + data_rev.getPort();
59             System.out.println(str_receive);
60             // 由于dp_receive在接收了数据之后,其内部消息长度值会变为实际接收的消息的字节数,
61             // 所以这里要将dp_receive的内部消息长度重新置为1024
62             data_rev.setLength(REV_SIZE);
63         } else {
64             // 如果重发MAXNUM次数据后,仍未获得服务器发送回来的数据,则打印如下信息
65             System.out.println("No response -- give up.");
66         }
67
68         /*第四步 关闭DatagramPacket*/
69         mSoc.close();
70     }
71
72 }

UdpClient

 1 import java.io.IOException;
 2 import java.net.DatagramPacket;
 3 import java.net.DatagramSocket;
 4 import java.net.InetAddress;
 5
 6 public class UdpServer {
 7
 8     private static final int SERVER_PORT = 3222;
 9     private static final int REV_SIZE = 1024; // 接收数据的存储空间大小
10
11     public static void main(String[] args) throws IOException {
12         byte[] buf_rev = new byte[REV_SIZE];
13         String str_send = "Hello UDPclient";
14         /* 第一步 实例化DatagramSocket */
15         DatagramSocket mSoc = new DatagramSocket(SERVER_PORT);
16
17         /* 第二步 实例化用于接收的DatagramPacket 并从DatagramSocket接收数据 */
18         DatagramPacket data_rev = new DatagramPacket(buf_rev, REV_SIZE);
19         boolean f = true;
20         while (f) {
21             mSoc.receive(data_rev);
22             InetAddress inetAddress = data_rev.getAddress();
23             int port = data_rev.getPort();
24             System.out.println("server received data from client:");
25             String str_rev = new String(data_rev.getData(), 0,
26                     data_rev.getLength())
27                     + " from " + inetAddress.getHostAddress() + ":" + port;
28             System.out.println(str_rev);
29
30             /* 第三步 实例化用于发送的DatagramPacket,并从DatagramSocket中发送出去 */
31             DatagramPacket data_send = new DatagramPacket(str_send.getBytes(),
32                     str_send.length(), inetAddress, port);
33             mSoc.send(data_send);
34
35             /*
36              * 由于dp_receive在接收了数据之后,其内部消息长度值会变为实际接收的消息的字节数,
37              * 所以这里要将dp_receive的内部消息长度重新置为1024
38              */
39             data_rev.setLength(REV_SIZE);
40         }
41         mSoc.close();
42
43     }
44 }

UdpServer

如果服务器端没有运行,则receive()会失败,此时运行结果如下图所示:

如果服务端先运行,客户端后运行,客户端将向服务端发送数据,并接受从服务端发送回来的数据,此时运行结果如下图所示:

几个需要注意的地方

1、UDP套接字和TCP套接字的一个微小但重要的差别:UDP协议保留了消息的边界信息。

DatagramSocket的每一次receive()调用最多只能接收调用一次send()方法所发送的数据,而且,不同的receive()方法调用绝对不会返回同一个send()方法所发送的额数据。

当在TCP套接字的输出流上调用write()方法返回后,所有调用者都知道数据已经被复制到一个传输缓存区中,实际上此时数据可能已经被发送,也有可能还没有被传送,而UDP协议没有提供从网络错误中恢复的机制,因此,并不对可能需要重传的数据进行缓存。这就意味着,当send()方法调用返回时,消息已经被发送到了底层的传输信道中。

2、UDP数据报文所能负载的最多数据,亦及一次传送的最大数据为65507个字节

当消息从网络中到达后,其所包含的数据被TCP的read()方法或UDP的receive()方法返回前,数据存储在一个先进先出的接收数据队列中。对于已经建立连接的TCP套接字来说,所有已接受但还未传送的字节都看作是一个连续的字节序列。然而,对于UDP套接字来说,接收到的数据可能来自不同的发送者,一个UDP套接字所接受的数据存放在一个消息队列中,每个消息都关联了其源地址信息,每次receive()调用只返回一条消息。如果receive()方法在一个缓存区大小为n的DatagramPacket实例中调用,而接受队里中的第一条消息的长度大于n,则receive()方法只返回这条消息的前n个字节,超出部分会被自动放弃,而且对接收程序没有任何消息丢失的提示!

出于这个原因,接受者应该提供一个有足够大的缓存空间的DatagramPacket实例,以完整地存放调用receive()方法时应用程序协议所允许的最大长度的消息。一个DatagramPacket实例中所允许传输的最大数据量为65507个字节,也即是UDP数据报文所能负载的最多数据。因此,可以用一个65600字节左右的缓存数组来接受数据。

3、DatagramPacket的内部消息长度值在接收数据后会发生改变,变为实际接收到的数据的长度值。

每一个DatagramPacket实例都包含一个内部消息长度值,其初始值为byte缓存数组的长度值,而该实例一旦接受到消息,这个长度值便会变为接收到的消息的实际长度值,这一点可以用DatagramPacket类的getLength()方法来测试。如果一个应用程序使用同一个DatagramPacket实例多次调用receive()方法,每次调用前就必须显式地将其内部消息长度重置为缓存区的实际长度,以免接受的数据发生丢失。

4、DatagramPacket的getData()方法总是返回缓冲区的原始大小,忽略了实际数据的内部偏移量和长度信息。

由于DatagramPacket的getData()方法总是返回缓冲数组的原始大小,即刚开始创建缓冲数组时指定的大小,在上面程序中,该长度为1024,因此如果我们要获取接收到的数据,就必须截取getData()方法返回的数组中只含接收到的数据的那一部分。

我们可以使用Arrays.copyOfRange()方法来实现,只需一步便可实现以上功能:

byte[] destbuf = Arrays.copyOfRange(data_rev.getData(),data_rev.getOffset(),

data_rev.getOffset() + data_rev.getLength());

当然,如果要将接收到的字节数组转换为字符串的话,也可以采用本程序中直接new一个String对象的方法:

new String(data_rev.getData(),data_rev.getOffset(),

data_rev.getOffset() + data_rev.getLength());

参考文章:

【Java TCP/IP Socket】UDP Socket(含代码) http://blog.csdn.net/ns_code/article/details/14128987

《Java TCP/IP Socket 编程》

时间: 2024-12-16 16:37:27

Java UDP Socket编程的相关文章

Java TCP/UDP socket 编程流程总结

最近正好学习了一点用java socket编程的东西.感觉整体的流程虽然不是很繁琐,但是也值得好好总结一下. Socket Socket可以说是一种针对网络的抽象,应用通过它可以来针对网络读写数据.就像通过一个文件的file handler就可以都写数据到存储设备上一样.根据TCP协议和UDP协议的不同,在网络编程方面就有面向两个协议的不同socket,一个是面向字节流的一个是面向报文的. 对socket的本身组成倒是比较好理解.既然是应用通过socket通信,肯定就有一个服务器端和一个客户端.

java UDP网路编程

大家都知道java中的socket网络编程,而其采用的协议分别有tcp和udp协议两种. 通常的理解tcp协议类似于打电话,udp类似于发短信.前者是线程安全的,但是效率比较低.后者则刚好相反. 今天就做个demo来了解一下udp网络编程. 首先是客户端: package javasocket.demo; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddres

基于java的socket编程及API解析

一.socket通讯过程 1.socket与socket编程简介: socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式.通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据. 我们所说的socket 编程,是站在传输层的基础上,所以可以使用 TCP/UDP 协议,但是不能进行访问网页,因为访问网页所需要的 http 协议位于应用层.作为一个应用程序是能实现该层以下的内容,而不能实现在该层之上的内容. 2.socket通讯过程: (

基于java的socket编程

#开头的废话#学习java已经半个月了,原本在抠教材里面的字眼时,觉得教材好厚,要看完不知道要到猴年马月去了.突然在网上看到一个教程,里面老师说学编程语言书不用太细看,看个大概,知道里面讲些什么就好,不用全记得,然后你一个劲地编,使劲地编,编的时候不懂再回来看就好了,这是最快的方法.心里一琢磨,还真是这样,根据以前学C语言的情况不就这样吗.所以便加速看,把一些书里介绍的方法,类飞速地浏览过了,刷到网络这一章,觉得socket编程应该是得试一下手,不要只看不做假把式. 此文为原创,转摘请注明转摘自

Java基础&mdash;socket编程&mdash;UDP发送及接收

简介:在同一主机上测试,同一IP地址,不同的端口,使用UDP发收数据 使用socket编程,通过UDP协议发送数据 需求:使用UDP协议向某IP的某端口发送一条数据 UdpSend.java package com.Train; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.ne

java网络socket编程详解

7.2 面向套接字编程    我们已经通过了解Socket的接口,知其所以然,下面我们就将通过具体的案例,来熟悉Socket的具体工作方式 7.2.1使用套接字实现基于TCP协议的服务器和客户机程序    依据TCP协议,在C/S架构的通讯过程中,客户端和服务器的Socket动作如下: 客户端: 1.用服务器的IP地址和端口号实例化Socket对象. 2.调用connect方法,连接到服务器上. 3.将发送到服务器的IO流填充到IO对象里,比如BufferedReader/PrintWriter

【转】java的socket编程

转自:http://www.cnblogs.com/linzheng/archive/2011/01/23/1942328.html 一,网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一地确定Internet上的一台主机. 而TCP层则提供面向应用的可靠(tcp)的或非可靠(UDP)的数据传输机制,这是网络编程的主要对象,一般不需要关心IP层是

java之socket编程

Socket就是为网络服务提供的一种机制,网络通信的两端都有Socket,一个Socket由一个IP地址和一个端口号唯一确定.数据在两个Socket间通过IO传输. 1.其传输方式有两种,UDP传输和TCP传输: UDP传输:在发送数据前不需要先建立连接,速度快:是不可靠协议:每个数据包的大小限制在64kb内.主要应用在网络视屏会议,桌面共享等场合,丢点数据不会对结果产生影响. TCP传输:发送数据前要先建立连接,是可靠协议:但效率较低:可以进行大数据量传输.主要应用在文件传输,下载等场合,对数

【JAVA】Socket 编程

对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket.服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了.首先ServerSocket将在服务端监听某个端口,当发现客户端有Socket来试图连接它时,它会accept该Socket的连接请求,同时在服务端建立一个对应的Socket与之进行通信.这样就有两个Socket了,客户端和服务端各一个. 对于Socket之间的通信其实很简单,服务端往Socket的输出流里面写东西,客户端就可以