UDP 多线程服务端 和 简单客户端

原文地址: http://cuisuqiang.iteye.com/blog/1726644


首先来了解UDP协议的几个特性
(1)UDP是一个无连接协议,传输数据之前源端和终端不建立连接,当UDP它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。
(2) 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。
(3) UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。
(4) 吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。 (5)UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态表(这里面有许多参数)。 (6)UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小。

 

UDP是无状态的,之前的做的TCP接到客户端请求后马上做一个线程,将连接对象传递进去进行处理!

但是UDP的话是没有连接对象的,只要消息包的概念!

这就好像两个人在一条河边干活,TCP是架桥搬运货物,而UDP是直接把货物仍过去了,至于货物是否到达只能通过对岸的人喊一声收到了!

 

这里我模拟的时候收到的消息包就直接做一个线程进行处理了,理想的话应该是根据客户端的地址来创建线程!

这样的话就好像每个客户端都有自己的链路了一样,总服务端服务收包,然后根据包是给谁的就扔给某线程去处理!这和快递公司的处理流程差不多,总站是总服务端,而快递员是子服务端!

 

来看一下代码:

Java代码   收藏代码
  1. package udpUpload;  
  2. import java.io.IOException;  
  3. import java.net.DatagramPacket;  
  4. import java.net.DatagramSocket;  
  5. import java.net.InetSocketAddress;  
  6. import java.net.SocketException;  
  7. import java.util.Arrays;  
  8. /** 
  9.  * @说明 UDP连接服务端,这里一个包就做一个线程处理 
  10.  * @author 崔素强(http://cuisuqiang.iteye.com/) 
  11.  * @version 1.0 
  12.  * @since 
  13.  */  
  14. public class UdpService {  
  15.     public static void main(String[] args) {  
  16.         try {  
  17.             init();  
  18.             while(true){  
  19.                 try {  
  20.                     byte[] buffer = new byte[1024 * 64]; // 缓冲区  
  21.                     DatagramPacket packet = new DatagramPacket(buffer, buffer.length);  
  22.                     receive(packet);  
  23.                     new Thread(new ServiceImpl(packet)).start();  
  24.                 } catch (Exception e) {  
  25.                 }  
  26.                 Thread.sleep(1 * 1000);  
  27.             }  
  28.         } catch (Exception e) {  
  29.             e.printStackTrace();  
  30.         }  
  31.     }  
  32.     /** 
  33.      * 接收数据包,该方法会造成线程阻塞 
  34.      * @return 
  35.      * @throws Exception  
  36.      * @throws IOException 
  37.      */  
  38.     public static DatagramPacket receive(DatagramPacket packet) throws Exception {  
  39.         try {  
  40.             datagramSocket.receive(packet);  
  41.             return packet;  
  42.         } catch (Exception e) {  
  43.             throw e;  
  44.         }  
  45.     }  
  46.     /** 
  47.      * 将响应包发送给请求端 
  48.      * @param bt 
  49.      * @throws IOException 
  50.      */  
  51.     public static void response(DatagramPacket packet) {  
  52.         try {  
  53.             datagramSocket.send(packet);  
  54.         } catch (Exception e) {  
  55.             e.printStackTrace();  
  56.         }  
  57.     }  
  58.     /** 
  59.      * 初始化连接 
  60.      * @throws SocketException 
  61.      */  
  62.     public static void init(){  
  63.         try {  
  64.             socketAddress = new InetSocketAddress("localhost"2233);  
  65.             datagramSocket = new DatagramSocket(socketAddress);  
  66.             datagramSocket.setSoTimeout(5 * 1000);  
  67.             System.out.println("服务端已经启动");  
  68.         } catch (Exception e) {  
  69.             datagramSocket = null;  
  70.             System.err.println("服务端启动失败");  
  71.             e.printStackTrace();  
  72.         }  
  73.     }  
  74.     private static InetSocketAddress socketAddress = null// 服务监听个地址  
  75.     private static DatagramSocket datagramSocket = null// 连接对象  
  76. }  
  77. /** 
  78.  * @说明 打印收到的数据包,并且将数据原封返回,中间设置休眠表示执行耗时 
  79.  * @author 崔素强(http://cuisuqiang.iteye.com/) 
  80.  * @version 1.0 
  81.  * @since 
  82.  */  
  83. class ServiceImpl implements Runnable {  
  84.     private DatagramPacket packet;  
  85.     public ServiceImpl(DatagramPacket packet){  
  86.         this.packet = packet;  
  87.     }  
  88.     public void run() {  
  89.         try {  
  90.             byte[] bt = new byte[packet.getLength()];  
  91.             System.arraycopy(packet.getData(), 0, bt, 0, packet.getLength());  
  92.             System.out.println(packet.getAddress().getHostAddress() + ":" + packet.getPort() + ":" + Arrays.toString(bt));  
  93.             Thread.sleep(5 * 1000); // 5秒才返回,标识服务端在处理数据  
  94.             // 设置回复的数据,原数据返回,以便客户端知道是那个客户端发送的数据  
  95.             packet.setData(bt);  
  96.             UdpService.response(packet);  
  97.         } catch (Exception e) {  
  98.             e.printStackTrace();  
  99.         }  
  100.     }  
  101. }   

 

这里子服务端中间停留了五秒钟,这是模拟程序正在处理,处理完毕后再拿一些数据通过总服务端连接对象仍出去!

因为消息包内是包含客户端连接进来时的连接信息的,所以这里只需要设置要回复的数据即可!

 

我们再来看一下客户端,为了更形象模拟多客户访问的场景,这里客户端是一些子线程来完成,每个线程都有自己的连接对象,IP 一样但是端口不一样!

来看一下代码:

Java代码   收藏代码
  1. package udpUpload;  
  2. import java.io.IOException;  
  3. import java.net.DatagramPacket;  
  4. import java.net.DatagramSocket;  
  5. import java.net.InetAddress;  
  6. import java.net.SocketException;  
  7. import java.util.Arrays;  
  8. import java.util.Random;  
  9. import java.util.UUID;  
  10. /** 
  11.  * @说明 UDP连接客户端 
  12.  * @author 崔素强(http://cuisuqiang.iteye.com/) 
  13.  * @version 1.0 
  14.  * @since 
  15.  */  
  16. public class UdpClient {  
  17.     public static void main(String[] args) {  
  18.         try {  
  19.             for(int i=0;i<5;i++){  
  20.                 new Thread(new ClientImpl()).start();  
  21.             }  
  22.         } catch (Exception e) {  
  23.             e.printStackTrace();  
  24.         }  
  25.     }  
  26. }  
  27. /** 
  28.  * @说明 线程创建自己的UDP连接,端口动态,发送一组数据然后接收服务端返回 
  29.  * @author 崔素强(http://cuisuqiang.iteye.com/) 
  30.  * @version 1.0 
  31.  * @since 
  32.  */  
  33. class ClientImpl implements Runnable{  
  34.     private Random random = new Random();  
  35.     private String uuid = UUID.randomUUID().toString();  
  36.     public void run() {  
  37.         try {  
  38.             init();  
  39.             byte[] buffer = new byte[1024 * 64]; // 缓冲区  
  40.             // 发送随机的数据  
  41.             byte[] btSend = new byte[]{(byte)random.nextInt(127), (byte)random.nextInt(127), (byte)random.nextInt(127)};  
  42.             DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("localhost"), 2233);  
  43.             packet.setData(btSend);  
  44.             System.out.println(uuid + ":发送:" + Arrays.toString(btSend));  
  45.             try {  
  46.                 sendDate(packet);  
  47.             } catch(Exception e){  
  48.                 e.printStackTrace();  
  49.             }  
  50.             receive(packet);  
  51.             byte[] bt = new byte[packet.getLength()];  
  52.             System.arraycopy(packet.getData(), 0, bt, 0, packet.getLength());  
  53.             if(null != bt && bt.length > 0){  
  54.                 System.out.println(uuid + ":收到:" + Arrays.toString(bt));  
  55.             }  
  56.             Thread.sleep(1 * 1000);  
  57.         } catch (Exception e) {  
  58.             e.printStackTrace();  
  59.         }  
  60.     }  
  61.     /** 
  62.      * 接收数据包,该方法会造成线程阻塞 
  63.      * @return 
  64.      * @throws IOException 
  65.      */  
  66.     public void receive(DatagramPacket packet) throws Exception {  
  67.         try {  
  68.             datagramSocket.receive(packet);  
  69.         } catch (Exception e) {  
  70.             throw e;  
  71.         }  
  72.     }  
  73.     /** 
  74.      * 发送数据包到指定地点 
  75.      * @param bt 
  76.      * @throws IOException 
  77.      */  
  78.     public void sendDate(DatagramPacket packet) {  
  79.         try {  
  80.             datagramSocket.send(packet);  
  81.         } catch (Exception e) {  
  82.             e.printStackTrace();  
  83.         }  
  84.     }  
  85.     /** 
  86.      * 初始化客户端连接 
  87.      * @throws SocketException 
  88.      */  
  89.     public void init() throws SocketException{  
  90.         try {  
  91.             datagramSocket = new DatagramSocket(random.nextInt(9999));  
  92.             datagramSocket.setSoTimeout(10 * 1000);  
  93.             System.out.println("客户端启动成功");  
  94.         } catch (Exception e) {  
  95.             datagramSocket = null;  
  96.             System.out.println("客户端启动失败");  
  97.             e.printStackTrace();  
  98.         }  
  99.     }  
  100.     private DatagramSocket datagramSocket = null// 连接对象  
  101. }  

 

先运行服务端,然后运行客户端,来看一下打印信息:

服务端:

Java代码   收藏代码
  1. 服务端已经启动  
  2. 127.0.0.1381:[5651]  
  3. 127.0.0.11569:[4543124]  
  4. 127.0.0.19969:[3697117]  
  5. 127.0.0.19937:[211080]  
  6. 127.0.0.13420:[481980]  

 

客户端:

Java代码   收藏代码
  1. 客户端启动成功  
  2. 客户端启动成功  
  3. 客户端启动成功  
  4. 客户端启动成功  
  5. 客户端启动成功  
  6. 6d62c2ae-e693-4e35-a295-9a385244cbf0:发送:[5651]  
  7. 49fa0ae9-59c3-4db9-97e7-930d9ada50fb:发送:[4543124]  
  8. 3338ddd0-dfa1-4001-80b1-8e663f7d502f:发送:[3697117]  
  9. 7b7fc365-e865-4b7c-bd8e-22a5ea095516:发送:[211080]  
  10. 53f2c5fc-3194-4e90-9c47-dbcbdef0ccc4:发送:[481980]  
  11. 6d62c2ae-e693-4e35-a295-9a385244cbf0:收到:[5651]  
  12. 49fa0ae9-59c3-4db9-97e7-930d9ada50fb:收到:[4543124]  
  13. 3338ddd0-dfa1-4001-80b1-8e663f7d502f:收到:[3697117]  
  14. 7b7fc365-e865-4b7c-bd8e-22a5ea095516:收到:[211080]  
  15. 53f2c5fc-3194-4e90-9c47-dbcbdef0ccc4:收到:[481980]  

 

请您到ITEYE网站看原创,谢谢!

http://cuisuqiang.iteye.com/ !  

 

可以看到,客户端同时启动了五个端口与服务端通信!

服务端也收到了来自不同客户端的消息!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值