自己的学习笔记(日后发现问题会修正)
在网上找到一些关于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();
}
}