牛骨文教育服务平台(让学习变的简单)

Java编程那些事儿106——网络编程技术5

陈跃峰

出自:http://blog.csdn.net/mailbomb

该示例的功能是实现将客户端程序的系统时间发送给服务器端,服务器端接收到时间以后,向客户端反馈字符串“OK”。实现该功能的客户端代码如下所示:

package udp;

import java.net.*;

import java.util.*;

/**

 * 简单的UDP客户端,实现向服务器端发生系统时间功能

 */

public class SimpleUDPClient {

            public static void main(String[] args) {

                     DatagramSocket ds = null;  //连接对象

                     DatagramPacket sendDp; //发送数据包对象

                DatagramPacket receiveDp; //接收数据包对象

                     String serverHost = "127.0.0.1"; //服务器IP

                int serverPort = 10010;  //服务器端口号

                     try{

                        //建立连接

                        ds = new DatagramSocket();

                        //初始化发送数据

                        Date d = new Date(); //当前时间

                        String content = d.toString(); //转换为字符串

                        byte[] data = content.getBytes();

                        //初始化发送包对象

                        InetAddress address = InetAddress.getByName(serverHost);

                        sendDp = new DatagramPacket(data,data.length,address,serverPort);

                        //发送

                        ds.send(sendDp);

                                                                           

                        //初始化接收数据

                        byte[] b = new byte[1024];

                        receiveDp = new DatagramPacket(b,b.length);

                        //接收

                        ds.receive(receiveDp);

                        //读取反馈内容,并输出

                        byte[] response = receiveDp.getData(); 

                        int len = receiveDp.getLength();

                        String s = new String(response,0,len);

                        System.out.println("服务器端反馈为:" + s);

                }catch(Exception e){

                        e.printStackTrace();

                }finally{

                        try{

                           //关闭连接

                           ds.close();

                        }catch(Exception e){}

                }

            }

        }

在该示例代码中,首先建立UDP方式的网络连接,然后获得当前系统时间,这里获得的系统时间是客户端程序运行的本地计算机的时间,然后将时间字符串以及服务器端的IP和端口,构造成发送数据包对象,调用连接对象ds的send方法发送出去。在数据发送出去以后,构造接收数据的数据包对象,调用连接对象ds的receive方法接收服务器端的反馈,并输出在控制台。最后在finally语句块中关闭客户端网络连接。

和下面将要介绍的服务器端一起运行时,客户端程序的输出结果为:

服务器端反馈为:OK

下面是该示例程序的服务器端代码实现:

package udp;

        import java.net.*;

        /**

         * 简单UDP服务器端,实现功能是输出客户端发送数据,

           并反馈字符串“OK"给客户端

         */

        public class SimpleUDPServer {

            public static void main(String[] args) {

                     DatagramSocket ds = null;  //连接对象

                     DatagramPacket sendDp; //发送数据包对象

                     DatagramPacket receiveDp; //接收数据包对象

                     final int PORT = 10010; //端口

                                               try{

                        //建立连接,监听端口

                        ds = new DatagramSocket(PORT);

                        System.out.println("服务器端已启动:");

                        //初始化接收数据

                        byte[] b = new byte[1024];

                        receiveDp = new DatagramPacket(b,b.length);

                        //接收

                        ds.receive(receiveDp);

                        //读取反馈内容,并输出

                        InetAddress clientIP = receiveDp.getAddress();

                        int clientPort = receiveDp.getPort();

                        byte[] data = receiveDp.getData();

                        int len = receiveDp.getLength();

                        System.out.println("客户端IP:" + clientIP.getHostAddress());

                        System.out.println("客户端端口:" + clientPort);

                        System.out.println("客户端发送内容:" + new String(data,0,len));

                                                                           

                        //发送反馈

                        String response = "OK";

                        byte[] bData = response.getBytes();

                        sendDp = new DatagramPacket(bData,bData.length,clientIP,clientPort);

                        //发送

                        ds.send(sendDp);

                                               }catch(Exception e){

                        e.printStackTrace();

                                               }finally{

                        try{

                           //关闭连接

                           ds.close();

                        }catch(Exception e){}

                                               }

            }

        }

在该服务器端实现中,首先监听10010号端口,和TCP方式的网络编程类似,服务器端的receive方法是阻塞方法,如果客户端不发送数据,则程序会在该方法处阻塞。当客户端发送数据到达服务器端时,则接收客户端发送过来的数据,然后将客户端发送的数据内容读取出来,并在服务器端程序中打印客户端的相关信息,从客户端发送过来的数据包中可以读取出客户端的IP以及客户端端口号,将反馈数据字符串“OK”发送给客户端,最后关闭服务器端连接,释放占用的系统资源,完成程序功能示例。

和前面TCP方式中的网络编程类似,这个示例也仅仅是网络编程的功能示例,也存在前面介绍的客户端无法进行多次数据交换,以及服务器端不支持多个客户端的问题,这两个问题也需要对于代码进行处理才可以很方便的进行解决。

在解决该问题以前,需要特别指出的是UDP方式的网络编程由于不建立虚拟的连接,所以在实际使用时和TCP方式存在很多的不同,最大的一个不同就是“无状态”。该特点指每次服务器端都收到信息,但是这些信息和连接无关,换句话说,也就是服务器端只是从信息是无法识别出是谁发送的,这样就要求发送信息时的内容需要多一些,这个在后续的示例中可以看到。

下面是实现客户端多次发送以及服务器端支持多个数据包同时处理的程序结构,实现的原理和TCP方式类似,在客户端将数据的发送和接收放入循环中,而服务器端则将接收到的每个数据包启动一个专门的线程进行处理。实现的代码如下:

package udp;

    import java.net.*;

    import java.util.*;

    /**

     * 简单的UDP客户端,实现向服务器端发生系统时间功能

     * 该程序发送3次数据到服务器端

     */

    public class MulUDPClient {

                  public static void main(String[] args) {

                 DatagramSocket ds = null;  //连接对象

                                     DatagramPacket sendDp; //发送数据包对象

                                     DatagramPacket receiveDp; //接收数据包对象

                                     String serverHost = "127.0.0.1"; //服务器IP

                                     int serverPort = 10012;  //服务器端口号

                                     try{

                    //建立连接

                    ds = new DatagramSocket();

                    //初始化

                               InetAddress address = InetAddress.getByName(serverHost);

                    byte[] b = new byte[1024];

                    receiveDp = new DatagramPacket(b,b.length);

                    System.out.println("客户端准备完成");

                    //循环30次,每次间隔0.01秒

                    for(int i = 0;i < 30;i++){

                                                        //初始化发送数据

                                                        Date d = new Date(); //当前时间

                                                        String content = d.toString(); //转换为字符串

                                                        byte[] data = content.getBytes();

                                                        //初始化发送包对象

                                                        sendDp = new DatagramPacket(data,data.length,address, serverPort);

                                                        //发送

                                                        ds.send(sendDp);

                                                        //延迟

                                                        Thread.sleep(10);

                                                        //接收

                                                        ds.receive(receiveDp);

                                                        //读取反馈内容,并输出

                                                        byte[] response = receiveDp.getData(); 

                                                        int len = receiveDp.getLength();

                                                        String s = new String(response,0,len);

                                                        System.out.println("服务器端反馈为:" + s);

                     }

                 }catch(Exception e){

                     e.printStackTrace();

                 }finally{

                     try{

                                                        //关闭连接

                                                        ds.close();

                     }catch(Exception e){}

                 }

         }

     }

在该示例中,将和服务器端进行数据交换的逻辑写在一个for循环的内部,这样就可以实现和服务器端的多次交换了,考虑到服务器端的响应速度,在每次发送之间加入0.01秒的时间间隔。最后当数据交换完成以后关闭连接,结束程序。

实现该逻辑的服务器端程序代码如下:

package udp;

import java.net.*;

/**

* 可以并发处理数据包的服务器端

* 功能为:显示客户端发送的内容,并向客户端反馈字符串“OK”

*/

public class MulUDPServer {

public static void main(String[] args) {

DatagramSocket ds = null;  //连接对象

DatagramPacket receiveDp; //接收数据包对象

final int PORT = 10012; //端口

byte[] b = new byte[1024];

receiveDp = new DatagramPacket(b,b.length);

try{

//建立连接,监听端口

ds = new DatagramSocket(PORT);

System.out.println("服务器端已启动:");

while(true){

//接收

ds.receive(receiveDp);

//启动线程处理数据包

new LogicThread(ds,receiveDp);

}

}catch(Exception e){

         e.printStackTrace();

}finally{

try{

//关闭连接

ds.close();

}catch(Exception e){}

}

}

}

该代码实现了服务器端的接收逻辑,使用一个循环来接收客户端发送过来的数据包,当接收到数据包以后启动一个LogicThread线程处理该数据包。这样服务器端就可以实现同时处理多个数据包了。

实现逻辑处理的线程代码如下:

package udp;

import java.net.*;

/**

 * 逻辑处理线程

 */

public class LogicThread extends Thread {

/**连接对象*/

DatagramSocket ds;

/**接收到的数据包*/

DatagramPacket dp;

 

public LogicThread(DatagramSocket ds,DatagramPacket dp){

this.ds = ds;

this.dp = dp;

start(); //启动线程

}

 

public void run(){

try{

//获得缓冲数组

byte[] data = dp.getData();

//获得有效数据长度

int len = dp.getLength();

//客户端IP

InetAddress clientAddress = dp.getAddress();

//客户端端口

int clientPort = dp.getPort();

//输出

System.out.println("客户端IP:" + clientAddress.getHostAddress());

System.out.println("客户端端口号:" + clientPort);

System.out.println("客户端发送内容:" + new String(data,0,len));

//反馈到客户端

byte[] b = "OK".getBytes();

DatagramPacket sendDp = new DatagramPacket(b,b.length,clientAddress,clientPort);

//发送

ds.send(sendDp);

}catch(Exception e){

e.printStackTrace();

}

}

}

在该线程中,只处理一次UDP通讯,当通讯结束以后线程死亡,在线程内部,每次获得客户端发送过来的信息,将获得的信息输出到服务器端程序的控制台,然后向客户端反馈字符串“OK”。

由于UDP数据传输过程中可能存在丢失,所以在运行该程序时可能会出现程序阻塞的情况。如果需要避免该问题,可以将客户端的网络发送部分也修改成线程实现。

关于基础的UDP网络编程就介绍这么多了,下面将介绍一下网络协议的概念。