Java NIO实例-DatagramChannel实现UDP协议传输

自己的学习笔记(日后发现问题会修正)

在网上找到一些关于udp协议,学习后写了一个实例用于学习之用。


根据别人的经验,总结了以下几点内容:

TCP与UDP效率比较:

    TCP协议适用于对效率要求相对低,但对准确性要求相对高的场景下,或者是有一种连接概念的场景下;而UDP协议适用于对效率要求相对高,对准确性要求相对低的场景。

TCP与UDP应用场景:

    TCP可以用于网络数据库,分布式高精度计算系统的数据传输;UDP可以用于服务系统内部之间的数据传输,因为数据可能比较多,内部系统局域网内的丢包错包率又很低,即便丢包,顶多是操作无效,这种情况下,UDP经常被使用。


网上收集的资料:

TCP字节流与UDP数据报:http://network.51cto.com/art/201310/413326.htm

TCP和UDP的区别(转):http://www.cnblogs.com/bizhu/archive/2012/05/12/2497493.html

java nio对OP_WRITE的处理解决网速慢的连接:http://blog.sina.com.cn/s/blog_783ede0301013g5n.html

测试工具:

使用clumsy工具,可以模拟网络丢包、网络延迟等恶劣环境

下载地址:http://download.csdn.net/detail/foart/8999423


服务端

package cn;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author 
 * @date 2015-8-7 上午11:36:25
 */
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.io.*;
import java.util.*;
import java.nio.*;

public class DatagramChannelServerDemo {
	// UDP协议服务端
	private int port = 9975;
	DatagramChannel channel;
	private Charset charset = Charset.forName("UTF-8");
	private Selector selector = null;

	public DatagramChannelServerDemo() throws IOException {
		try {
			selector = Selector.open();
			channel = DatagramChannel.open();
		} catch (Exception e) {
			selector = null;
			channel = null;
			System.out.println("超时");			
		}
		System.out.println("服务器启动");
	}

	/* 编码过程 */
	public ByteBuffer encode(String str) {
		return charset.encode(str);
	}

	/* 解码过程 */
	public String decode(ByteBuffer bb) {
		return charset.decode(bb).toString();
	}

	/* 服务器服务方法 */
	public void service() throws IOException {
		if(channel==null || selector==null) return;
		channel.configureBlocking(false);
		channel.socket().bind(new InetSocketAddress(port));
		// channel.write(ByteBuffer.wrap(new String("aaaa").getBytes()));
		channel.register(selector, SelectionKey.OP_READ);
		/** 外循环,已经发生了SelectionKey数目 */
		while (selector.select() > 0) {
			System.out.println("有新channel加入");
			/* 得到已经被捕获了的SelectionKey的集合 */
			Iterator iterator = selector.selectedKeys().iterator();
			while (iterator.hasNext()) {
				SelectionKey key = null;
				try {
					key = (SelectionKey) iterator.next();
					iterator.remove();

					if (key.isReadable()) {
						reveice(key);
					}
					if (key.isWritable()) {
						// send(key);
					}
				} catch (IOException e) {
					e.printStackTrace();
					try {
						if (key != null) {
							key.cancel();
							key.channel().close();
						}
					} catch (ClosedChannelException cex) {
						e.printStackTrace();
					}
				}
			}
			/* 内循环完 */
		}
		/* 外循环完 */
	}

	/*
	 * 接收 用receive()读IO
	 * 作为服务端一般不需要调用connect(),如果未调用<span style="font-family: Arial, Helvetica, sans-serif;">connect()时调</span><span style="font-family: Arial, Helvetica, sans-serif;">用read()\write()读写,会报java.nio.channels</span>
	 * .NotYetConnectedException 只有调用connect()之后,才能使用read和write.
	 */
	synchronized public void reveice(SelectionKey key) throws IOException {
		if (key == null)
			return;
		// ***用channel.receive()获取客户端消息***//
		// :接收时需要考虑字节长度
		DatagramChannel sc = (DatagramChannel) key.channel();
		String content = "";
		// create buffer with capacity of 48 bytes
		ByteBuffer buf = ByteBuffer.allocate(1024);// java里一个(utf-8)中文3字节,gbk中文占2个字节
		buf.clear();
		SocketAddress address = sc.receive(buf); // read into buffer. 返回客户端的地址信息
		String clientAddress = address.toString().replace("/", "").split(":")[0];
		String clientPost = address.toString().replace("/", "").split(":")[1];

		buf.flip(); // make buffer ready for read
		while (buf.hasRemaining()) {
			buf.get(new byte[buf.limit()]);// read 1 byte at a time
			content += new String(buf.array());
		}
		buf.clear(); // make buffer ready for writing
		System.out.println("接收:" + content.trim());
		// 第一次发;udp采用数据报模式,发送多少次,接收多少次
		ByteBuffer buf2 = ByteBuffer.allocate(65507);
		buf2.clear();
		buf2
				.put("消息推送内容 abc..UDP是一个非连接的协议,传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端UDP是一个非连接的协议,传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端UDP是一个非连接的协议,传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端@Q"
						.getBytes());
		buf2.flip();
		channel.send(buf2, new InetSocketAddress(clientAddress,Integer.parseInt(clientPost))); // 将消息回送给客户端

		// 第二次发
		ByteBuffer buf3 = ByteBuffer.allocate(65507);
		buf3.clear();
		buf3.put("任务完成".getBytes());
		buf3.flip();
		channel.send(buf3, new InetSocketAddress(clientAddress, Integer.parseInt(clientPost))); // 将消息回送给客户端
	}

	int y = 0;

	public void send(SelectionKey key) {
		if (key == null)
			return;
		// ByteBuffer buff = (ByteBuffer) key.attachment();
		DatagramChannel sc = (DatagramChannel) key.channel();
		try {
			sc.write(ByteBuffer.wrap(new String("aaaa").getBytes()));
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		System.out.println("send2() " + (++y));
	}

	/* 发送文件 */
	public void sendFile(SelectionKey key) {
		if (key == null)
			return;
		ByteBuffer buff = (ByteBuffer) key.attachment();
		SocketChannel sc = (SocketChannel) key.channel();
		String data = decode(buff);
		if (data.indexOf("get") == -1)
			return;
		String subStr = data.substring(data.indexOf(" "), data.length());
		System.out.println("截取之后的字符串是 " + subStr);
		FileInputStream fileInput = null;
		try {
			fileInput = new FileInputStream(subStr);
			FileChannel fileChannel = fileInput.getChannel();
			fileChannel.transferTo(0, fileChannel.size(), sc);
			fileChannel.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				fileInput.close();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
	}

	public static void main(String[] args) throws IOException {
		new DatagramChannelServerDemo().service();
	}
}


客户端

package cn;

import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author 
 * @date 2015-8-7 上午11:36:25
 */
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.io.*;
import java.util.*;
import java.nio.*;

public class DatagramChannelClientDemo {
	// UDP协议客户端
	private String serverIp = "127.0.0.1";
	private int port = 9975;
	// private ServerSocketChannel serverSocketChannel;
	DatagramChannel channel;
	private Charset charset = Charset.forName("UTF-8");
	private Selector selector = null;

	public DatagramChannelClientDemo() throws IOException {		
		try {
			selector = Selector.open();
			channel = DatagramChannel.open();
		} catch (Exception e) {
			selector = null;
			channel = null;
			System.out.println("超时");			
		}
		System.out.println("客户器启动");
	}

	/* 编码过程 */
	public ByteBuffer encode(String str) {
		return charset.encode(str);
	}

	/* 解码过程 */
	public String decode(ByteBuffer bb) {
		return charset.decode(bb).toString();
	}

	/* 服务器服务方法 */
	public void service() throws IOException {
		if(channel==null || selector==null) return;
		channel.configureBlocking(false);
		channel.connect(new InetSocketAddress(serverIp, port));// 连接服务端
		channel.write(ByteBuffer.wrap(new String("客户端请求获取消息").getBytes()));
		channel.register(selector, SelectionKey.OP_READ);
		/** 外循环,已经发生了SelectionKey数目 */
		while (selector.select() > 0) {
			/* 得到已经被捕获了的SelectionKey的集合 */
			Iterator iterator = selector.selectedKeys().iterator();
			while (iterator.hasNext()) {
				SelectionKey key = null;
				try {
					key = (SelectionKey) iterator.next();
					iterator.remove();
					if (key.isReadable()) {
						reveice(key);
					}
					if (key.isWritable()) {
						// send(key);
					}
				} catch (IOException e) {
					e.printStackTrace();
					try {
						if (key != null) {
							key.cancel();
							key.channel().close();
						}
					} catch (ClosedChannelException cex) {
						e.printStackTrace();
					}
				}
			}
			/* 内循环完 */
		}
		/* 外循环完 */
	}

//	/* 
//	 * 接收	用read()读IO
//	 *  */
//	synchronized public void reveice2(SelectionKey key) throws IOException {
//		if (key == null)
//			return;
//		// ***用channel.read()获取消息***//
//		// :接收时需要考虑字节长度
//		DatagramChannel sc = (DatagramChannel) key.channel();
//		String content = "";
//		// create buffer with capacity of 48 bytes
//		ByteBuffer buf = ByteBuffer.allocate(3);// java里一个(utf-8)中文3字节,gbk中文占2个字节
//		int bytesRead = sc.read(buf); //read into buffer.
//		
//		while (bytesRead >0) {
//		  buf.flip();  //make buffer ready for read
//		  while(buf.hasRemaining()){				      
//			  buf.get(new byte[buf.limit()]); // read 1 byte at a time	
//		      content += new String(buf.array());
//		  }
//		  buf.clear(); //make buffer ready for writing		
//		  bytesRead = sc.read(buf);	
//		}				
//		System.out.println("接收:" + content);				
//	}

	/* 接收 */
	synchronized public void reveice(SelectionKey key) throws IOException {
		String threadName = Thread.currentThread().getName();
		if (key == null)
			return;
		try {
			// ***用channel.receive()获取消息***//
			// :接收时需要考虑字节长度
			DatagramChannel sc = (DatagramChannel) key.channel();
			String content = "";
			//第一次接;udp采用数据报模式,发送多少次,接收多少次
			ByteBuffer buf = ByteBuffer.allocate(65507);// java里一个(utf-8)中文3字节,gbk中文占2个字节
			buf.clear();
			SocketAddress address = sc.receive(buf); // read into buffer.
			String clientAddress = address.toString().replace("/", "").split(":")[0];
			String clientPost = address.toString().replace("/", "").split(":")[1];
			System.out.println(threadName + "\t" + address.toString());
			buf.flip(); // make buffer ready for read
			while (buf.hasRemaining()) {
				buf.get(new byte[buf.limit()]);// read 1 byte at a time
				byte[] tmp = buf.array();
				content += new String(tmp);
			}
			buf.clear(); // make buffer ready for writing次
			System.out.println(threadName + "接收:" + content.trim());
			//第二次接
			content = "";
			ByteBuffer buf2 = ByteBuffer.allocate(65507);// java里一个(utf-8)中文3字节,gbk中文占2个字节
			buf2.clear();	
			SocketAddress address2 = sc.receive(buf2); // read into buffer.
			buf2.flip(); // make buffer ready for read
			while (buf2.hasRemaining()) {
				buf2.get(new byte[buf2.limit()]);// read 1 byte at a time
				byte[] tmp = buf2.array();
				content += new String(tmp);
			}
			buf2.clear(); // make buffer ready for writing次
			System.out.println(threadName + "接收2:" + content.trim());
			
		} catch (PortUnreachableException ex) {
			System.out.println(threadName + "服务端端口未找到!");
		}
		send(2);		
	}

	boolean flag = false;

	public void send(int i) {
		if (flag)
			return;
		try {
			// channel.write(ByteBuffer.wrap(new String("客户端请求获取消息(第"+i+"次)").getBytes()));
			// channel.register(selector, SelectionKey.OP_READ );
			ByteBuffer buf2 = ByteBuffer.allocate(48);
			buf2.clear();
			buf2.put(("客户端请求获取消息(第" + i + "次)").getBytes());
			buf2.flip();
			channel.write(buf2);
			channel.register(selector, SelectionKey.OP_READ );
//			int bytesSent = channel.send(buf2, new InetSocketAddress(serverIp,port)); // 将消息回送给服务端
		} catch (IOException e) {
			e.printStackTrace();
		}
		flag = true;
	}

	int y = 0;

	public void send(SelectionKey key) {
		if (key == null)
			return;
		// ByteBuffer buff = (ByteBuffer) key.attachment();
		DatagramChannel sc = (DatagramChannel) key.channel();
		try {
			sc.write(ByteBuffer.wrap(new String("aaaa").getBytes()));
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		System.out.println("send2() " + (++y));
	}

	/* 发送文件 */
	public void sendFile(SelectionKey key) {
		if (key == null)
			return;
		ByteBuffer buff = (ByteBuffer) key.attachment();
		SocketChannel sc = (SocketChannel) key.channel();
		String data = decode(buff);
		if (data.indexOf("get") == -1)
			return;
		String subStr = data.substring(data.indexOf(" "), data.length());
		System.out.println("截取之后的字符串是 " + subStr);
		FileInputStream fileInput = null;
		try {
			fileInput = new FileInputStream(subStr);
			FileChannel fileChannel = fileInput.getChannel();
			fileChannel.transferTo(0, fileChannel.size(), sc);
			fileChannel.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				fileInput.close();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
	}

	public static void main(String[] args) throws IOException {
		new Thread(new Runnable() {			
			public void run() {
				try {
					new DatagramChannelClientDemo().service();
				} catch (IOException e) {					
					e.printStackTrace();
				}
			}
		}).start();
		
//		new Thread(new Runnable() {			
//			public void run() {
//				try {
//					new DatagramChannelClientDemo().service();
//				} catch (IOException e) {					
//					e.printStackTrace();
//				}
//			}
//		}).start();
		
	}
}




  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值