我以前经常写的是基于TCP的网络编程,由于TCP建立连接鼻血要经过三次握手连接,服务器端需要阻塞式等待客户端的连接。而UDP则是可以直接向目的地址的目的端口上发送数据包,由于它只负责发送出去就好,不管对方是否正确接受到与否,所以当网络性能不好时它容易出现丢包的问题。(注意:UDP是基于数据报为单位进行传输的,而TCP是一种基于流进行传输的)

  但是UDP很好的模拟了我们呢平时聊天的方式,可以很好的实现连续多次发送和接受,也就是简单的QQ聊天的功能。

  现在来简要介绍Java中有关UDP编程相关的类:

  一个是DatagramPacket,主要用来将数据字节填充到UDP数据报中,用来解包接受数据的,用来收和发UDP数据报。一个是DatagramSocket主要用来是建立客户端和服务器端,用来接受和发送数据报数据的。

  (1)DatagramPacket类

  UDP首部向IP首部添加了8个字节,包含了源端口和目标端口,IP首部以后内容的长度和校验和,最多有65507个字节数。UDP所使用的端口和TCP使用的端口不一样的,是一个final类。

  对于接受数据,将接受到的数据存储到DatagramPacket,然后从该对象中读取数据。

  对于发送数据,将发送的数据先存到DatagramPacket中,然后将该对象发送。

 


  接受数据报构造函数:


DatagramPacket(byte[] buffer,int length) 
 
 
 

     DatagramPacket(byte[] buffer,int offset,int length)



  socket将接受到的数据部分存储到buffer,一般buffer的大小最多定义为8192或者512大小即可




  发送数据报构造函数:



  由于DatagramPacket将数据填充到UDP数据报中,而数据报需要源端口和目标端口,客户端一般创建的端口是匿名的,会在填充的过程中自动的加上,而源端口必须要首先显式的在    DatagramPacket中设置,这样才能填充到UDP数据报中。 发送数据都必须要把数据以字节形式发送。以API 1.6


DatagramPacket(byte[] buffer,int length,InetAddress dest,int port) 
 
 
 
 
    DatagramPacket(byte[] buffer,int offset,int length,InetAddress dest,int port) 
 
 
 

     DatagramPacket(byte[] buffer,int length,SocketAddress dest) 
 
 
 

     DatagramPacket(byte[] buffer,int offset,int length,SocketAddress dest) 
 
 
 
 
     注意:SocketAddress是一个抽象类,主要保存了主机名、IP和端口号, 
 
 
 

          一般可以SocketAddress address=new InetSocketAddress("www.baidu.com",2000) 
 
 
 

          InetAddress只是用来保存主机名和IP的。 
 
 
 

     InetAddress getAddress()、getPort()获取发送数据目标地址、端口和接受到数据的源地址、端口 
 
 
 
 
    SocketAddress getSocketAddress()(最常用的)一样的意思。



  利用 getData()获取接受到的数据报数据的字节数组,一般利用



String res=new String(packet.getData(),"ASCII")或 
 
 
 

     String s=new String(packet.getData(),packet.getOffset(),packet.getLength(),"UTF-8");


getLength()返回UDP中数据的字节数。getOffset()数据报中数据开始的点。


  

  对于已经构造好的DatagramPacket,可以在发送之前更改它的一些状态。修改数据报的属性,一般是先创建一个接受数据报的构造函数,然后对其报进行更改属性。



 

setData(byte[] data) 
  
 
  

      setData(byte[] data,int offset,int length)  
   可以用来连续发送大量的数据块 
  
 
  

      setAddress(InetAddress remote) 
  
 
  

      setPort(int port)


  setAddress(SocketAddress remote)相当于上面两个方法。可以利用这些方法来设置一个数据报发送不同的ip和端口。


  setLength(int length)设置包的长度,主要是指定缓冲区中 将要发送的字节数,或用来 将要接收数据的包数据缓冲区的字节数。


  (2)DatagramSocket类


  在UDP中没有明显的客户端类和服务器端类,客户端既可以发消息也可以接受消息。均在创建的过程会有SocketException异常。创建UDP客户端构造器

  DatagramSocket();创建一个对于客户端来说匿名的端口,发送UDP的过程会自动加上该端口号,服务器接受消息也会按照该端口号发送。



  创建UDP服务器构造器



DatagramSocket(int port) 
  
 
  
 
     DatagramSocket(int port,InetAddress interface);用于一个主机有多个网络接口的时候 
  
 
  
 
     DatagramSocket(SocketAddress interface)


  发送数据报


  直接调用send(DatagramPacket dp),异常有IOException


  接受数据报


  receive( DatagramPacket dp ); 异常有IOException

  从网络中接受一个UDP数据报,存储在某一个DatagramPacket。在数据未到达之前会一直阻塞后面的运行,一般在无限循环中接受。


  管理连接

  connect(InetAddress host,int port)来设置其只对指定的远程主机和指定远程的接受和发送包

  getPort()、getInetAddress()返回其连接的远程主机。一般在接受到消息后,需要调用

  packet.getSocketAddress(),获取远程主机基本信息

   以下是我写的简单的一个聊天程序:

Send_Thread.java

package Chat_UDP;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Scanner;

public class Send_Thread extends Thread{
    
    //发送的socket端
    private DatagramSocket sender = null;
    //待发送的目标地址
    private InetSocketAddress address = null;
    //从键盘输入
    Scanner scan = new Scanner(System.in);
    
    public Send_Thread(DatagramSocket sender,InetSocketAddress address)
    {
        this.sender = sender;
        this.address = address;
    }
    
    @Override
    public void run() {
        // TODO Auto-generated method stub
        try
        {
            while(true)
            {
                //输入待发送的内容
                String input = scan.nextLine();
                if(input.equals("exit"))
                    break;
                byte[] data = null;
                data = input.getBytes("UTF-8");
                //创建UDP数据报
                DatagramPacket pack = new DatagramPacket(data, data.length,address);
                
                sender.send(pack);    
            }
            
            System.out.println("Exit!");
            
        }catch(IOException e)
        {
            System.out.println("IOException");
        }
    }

}

 

Receive_Thread.java

package Chat_UDP;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class Receive_Thread extends Thread {
    
    private static final int MAX_RECEIVE_BUFFER = 1024;
    private DatagramSocket server;
    private DatagramPacket packet;
    byte[] buffer = new byte[MAX_RECEIVE_BUFFER];
    
    public Receive_Thread(DatagramSocket server)
    {
        this.server = server;
        packet = new DatagramPacket(buffer, buffer.length);
    }
    
    @Override
    public void run() {
        // TODO Auto-generated method stub
        try
        {
            while(true)
            {
                //接收数据包
                server.receive(packet);
                String s = new String(packet.getData(),packet.getOffset(),packet.getLength(),"UTF-8");
                System.out.println(packet.getAddress()+" at port "+packet.getPort()+"  Says :"+s);
                packet.setLength(buffer.length);
            }
        }
        catch(IOException e)
        {
            System.out.println("IOException");
        }
    }
}

Chat_Client.java

1 package Chat_UDP;
 2 
 3 import java.net.DatagramSocket;
 4 import java.net.InetSocketAddress;
 5 
 6 public class Chat_Client {
 7     
 8     private static final int DEST_PORT = 9000;
 9     private static final int SEND_PORT = 10000;
10     private static final int RECE_PORT = 8888;
11     private static final String IP = "127.0.0.1";
12     
13     public static void main(String[] args)
14     {
15         try{
16             Send_Thread send_thread = null;
17             Receive_Thread rece_thread = null;
18             InetSocketAddress address = null;
19             //创建待接受数据包的目的机的端口号和IP地址
20             address = new InetSocketAddress(IP, DEST_PORT);
21             //创建发送的Socket端
22             DatagramSocket sendsocket = new DatagramSocket(SEND_PORT);
23             //创建接受的Socket端
24             DatagramSocket recesocket = new DatagramSocket(RECE_PORT);
25             //发送线程建立
26             send_thread = new Send_Thread(sendsocket, address);
27             //接受线程的建立
28             rece_thread = new Receive_Thread(recesocket);
29             
30             send_thread.start();
31             rece_thread.start();
32             
33         }catch(Exception e)
34         {
35             System.out.println("Exception!");
36         }
37     
38     }
39     
40 
41 }

 

 Chat_Server.java

1 package Chat_UDP;
 2 
 3 import java.net.DatagramSocket;
 4 import java.net.InetSocketAddress;
 5 
 6 public class Chat_Server {
 7     
 8     private static final int DEST_PORT = 8888;
 9     private static final int SEND_PORT = 10001;
10     private static final int RECE_PORT = 9000;
11     private static final String IP = "127.0.0.1";
12     
13     public static void main(String[] args)
14     {
15         try{
16             Send_Thread send_thread = null;
17             Receive_Thread rece_thread = null;
18             InetSocketAddress address = null;
19             //创建待接受数据包的目的机的端口号和IP地址
20             address = new InetSocketAddress(IP, DEST_PORT);
21             //创建发送的Socket端
22             DatagramSocket sendsocket = new DatagramSocket(SEND_PORT);
23             //创建接受的Socket端
24             DatagramSocket recesocket = new DatagramSocket(RECE_PORT);
25             //发送线程建立
26             send_thread = new Send_Thread(sendsocket, address);
27             //接受线程的建立
28             rece_thread = new Receive_Thread(recesocket);
29             send_thread.start();
30             rece_thread.start();
31         }catch(Exception e)
32         {
33             System.out.println("Exception!");
34         }
35     
36     }
37     
38 }