Cyh的博客

Email:kissyan4916@163.com
posts - 26, comments - 19, trackbacks - 0, articles - 220

网络编程>>UDP编程

Posted on 2009-12-17 22:59 啥都写点 阅读(856) 评论(0)  编辑  收藏 所属分类: J2SE
  上面的例子都用Socket建立连接,属于TCP(Transmission Control Protocol)连接,本节实例实现UDP(User Data Protocol)编程,包括发送和接收UDP报文。 首先来看看UDP与TCP的区别:

  TCP是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接,这与打电话的机制相似。在前面几节的编程中,客户端都与服务器请求建立Socket连接,当服务器端的ServerSocket的accept方法接受连接时,客户端与服务器的连接便确立了,由于要事先建立好连接,所以用TCP传输数据速度相对比较慢,但是比较稳定。
  UDP是面向非连接的协议,也就是说,在正式通信前不必与对方先建立连接,不管对方状态就直接发送,这与用手机发短信的机制非常相似。由于不用建立连接,所以传输速度比较快,但是传输的可靠性较差。

在Java中实现UDP编程的关键技术如下:

  java.net.DatagramSocket和java.net.DatagramPacket类可以实现UDP编程,前者实现与目标机的连接(这种连接不需要目标主机的认可),后者用于封装UDP包。
  发送UDP包时,先将数据包装成DatagramPacket对象,然后建立一个DatagramSocket,调用它的send方法,将DatagramPacket发送给目标主机
  收取UDP包时,建立一个侦听本地端口的DatagramSocket,创建一个空的DatagramPacket对象,以存放收到的报文,调用DatagramSocket的receive方法将收到的UDP包写入到DatagramPacket对象中。
  DatagramPacket 的getAddress方法能获得UDP消息发送者的网络地址信息。

/** */

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * 该程序接收来自指定端口得UDP报文。
 *
*/

public class UDPReceive {
    
// 帮助信息
    public static final String usage = "Usage: java book.net.udp.UDPReceive <port>";

    
public static void main(String args[]) {
        
try {
            
if (args.length != 1){
                
throw new IllegalArgumentException("Wrong number of args");
            }

            
// 从命令行中获取端口号参数
            int port = Integer.parseInt(args[0]);

            
// 创建一个socket,侦听这个端口。
            DatagramSocket dsocket = new DatagramSocket(port);

            
// 保存接收到的UDP报文的字节数组
            byte[] buffer = new byte[2048];

            
// 创建一个DatagramPacket,将收到的报文写入buffer中。
            
// 注意,这里指定了报文的长度,如果收到的UDP报文比2048大,多余的信息被舍弃
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

            
// 不断循环,接收数据
            for ( ; ;) {
                
// 等待收到一个数据包
                dsocket.receive(packet);

                
// 将收到的报文的字节数组封装成字符串。
                String msg = new String(buffer, 0, packet.getLength());
                
// 从数据包中取得消息来源的地址
                System.out.println("Receive: " + packet.getAddress().getHostAddress() + ""
                        
+ msg);

                
// 如果收到QUIT指令,则退出循环。
                if (msg.equals("QUIT")){
                    System.out.println(
"Exit the UDPReceive!");
                    
break;
                }

                
                
// 重设数据包的长度
                packet.setLength(buffer.length);
            }

            
// 关闭socket
            dsocket.close();
        }
 catch (Exception e) {
            System.err.println(e);
            System.err.println(usage);
        }

    }

}




/**--------------------------------------------UDPSend.java---------------------------------------------------*/


import java.io.File;
import java.io.FileInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * 该实例实现一个发送UDP报文的类。
 * UDP与TCP不同在于,UDP在发送报文前无需建立连接,直接发送;而TCP需要建立连接。
 * 即TCP比UDP更可靠。另外,UDP报文会出现舍弃的情况,因为发送端和接收端的报文大小可能不一致。
 * 体现在编程时,UDP编程无需ServerSocket和Socket,只需要DatagramSocket类即可。
 *
*/

public class UDPSend {
    
public static final String usage = 
    
"Usage: java book.net.udp.UDPSend <hostname> <port> <msg>\n" +
    
"   or: java book.net.udp.UDPSend <hostname> <port> -f <file>";

    
public static void main(String args[]) {
        
try 
            
// 检查参数个数
            if (args.length < 3)
                
throw new IllegalArgumentException("Wrong number of args");
            }

            
            
// 域名和端口
            String host = args[0];
            
int port = Integer.parseInt(args[1]);
        
            
// 下面构造待发送报文的字节数组  
            byte[] message;
            
if (args[2].equals("-f")) {
                
// 如果第三个参数为 -f,则表示将文件的内容以UDP方式发送
                
// 获得待发送的文件对象以及文件的长度
                File f = new File(args[3]);
                
int len = (int)f.length(); 
                
// 创建一个足够容纳文件内容的字节数组
                message = new byte[len]; 
                FileInputStream in 
= new FileInputStream(f);
                
// 将文件内容以字节的方式读到字节数组中
                int bytes_read = 0, n;
                
do {
                    n 
= in.read(message, bytes_read, len-bytes_read);
                    bytes_read 
+= n;
                }
 while((bytes_read < len)&& (n != -1));
            }

            
else 
                
// 如果第三个参数不是 -f,则将后面的参数当作消息发送
                String msg = args[2];  
                
for (int i = 3; i < args.length; i++){
                    msg 
+= " " + args[i];
                }

                message 
= msg.getBytes();
            }

            
            
// 根据域名获取IP地址
            InetAddress address = InetAddress.getByName(host);
        
            
// 初始化一个UDP包。
            
// DatagramPacket的构造方法中必须使用InetAddress,而不能是IP地址或者域名
            DatagramPacket packet = new DatagramPacket(message, message.length,
                               address, port);
        
            
// 创建一个DatagramSocket,以发送UDP包
            DatagramSocket dsocket = new DatagramSocket();
            dsocket.send(packet);
            System.out.println(
"send: " + new String(message));
            dsocket.close();
            
            
// 注意:如果在构造DatagramPacket时,不提供IP地址和端口号,
            
// 则需要调用DatagramSocket的connect方法,否则无法发送UDP包
            packet = new DatagramPacket(message, message.length);
            dsocket 
= new DatagramSocket();
            dsocket.connect(address, port);
            dsocket.send(packet);
            System.out.println(
"Send: " + new String(message));
            dsocket.close();
            
        }
 catch (Exception e) {
            System.err.println(e);
            System.err.println(usage);
        }

    }

}



首先在命令行中输入“java book.net.udp.UDPReceive 8888”,以启动UDPReceive进程,接收来自8888端口的UDP报文,然后另开一个命令行,输入"java book.net.udp.UDPSend 127.0.0.1 8888 This is the UDP message!" ,将发送一条消息给本地的8888端口。接着再发送一条“QUIT”消息,停止UDPReceive 进程。  





                                                                                                       --    学海无涯