package cn.ac.yangge.service;
import cn.ac.yangge.unity.AnalysisData;
import cn.ac.yangge.unity.ByteBufferPrint;
import org.springframework.stereotype.Service;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* Created by yangge on 2017/5/26 0026.
*/
@Service("tcpService")
public class TcpService {
// 主入口
public static void main() throws IOException {
sockectChannelSever();
}
//使用serversocketchannel 非程阻塞
public static void sockectChannelSever() {
int port = 8888;//端口
ServerSocketChannel serverSocketChannel;
Selector selector;
try {
serverSocketChannel = ServerSocketChannel.open();
ServerSocket ss = serverSocketChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
ss.bind(address);
serverSocketChannel.configureBlocking(false);
selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
return;
}
while (true) {
try {
selector.select();
} catch (IOException e) {
e.printStackTrace();
break;
}
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
System.out.println("Accepted connection from " + client);
client.configureBlocking(false);
SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ);
ByteBuffer buffer = ByteBuffer.allocate(100);
clientKey.attach(buffer);
}
if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
client.read(output);
output.flip();
byte[] byteArray=output.array();
for(byte b:byteArray){
System.out.println(b);
}
System.out.println(ByteBufferPrint.getString(output));
}
} catch (IOException ex) {
key.cancel();
try {
key.channel().close();
} catch (IOException e) {
}
}
}
}
}
}
Java NIO中的SocketChannel是一个连接到TCP网络套接字的通道。可以通过以下2种方式创建SocketChannel:
- 打开一个SocketChannel并连接到互联网上的某台服务器。
- 一个新连接到达ServerSocketChannel时,会创建一个SocketChannel。
打开 SocketChannel
下面是SocketChannel的打开方式:
1
SocketChannel socketChannel = SocketChannel.open();
2
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));
关闭 SocketChannel
当用完SocketChannel之后调用SocketChannel.close()关闭SocketChannel:
socketChannel.close();
从 SocketChannel 读取数据
要从SocketChannel中读取数据,调用一个read()的方法之一。以下是例子:
1
ByteBuffer buf = ByteBuffer.allocate(48);
2
int bytesRead = socketChannel.read(buf);
首先,分配一个Buffer。从SocketChannel读取到的数据将会放到这个Buffer中。
然后,调用SocketChannel.read()。该方法将数据从SocketChannel 读到Buffer中。read()方法返回的int值表示读了多少字节进Buffer里。如果返回的是-1,表示已经读到了流的末尾(连接关闭了)。
写入 SocketChannel
写数据到SocketChannel用的是SocketChannel.write()方法,该方法以一个Buffer作为参数。示例如下:
01
String newData = "New String to write to file..." + System.currentTimeMillis();
02
03
ByteBuffer buf = ByteBuffer.allocate(48);
04
buf.clear();
05
buf.put(newData.getBytes());
06
07
buf.flip();
08
09
while(buf.hasRemaining()) {
10
channel.write(buf);
11
}
注意SocketChannel.write()方法的调用是在一个while循环中的。Write()方法无法保证能写多少字节到SocketChannel。所以,我们重复调用write()直到Buffer没有要写的字节为止。
非阻塞模式
可以设置 SocketChannel 为非阻塞模式(non-blocking mode).设置之后,就可以在异步模式下调用connect(), read() 和write()了。
connect()
如果SocketChannel在非阻塞模式下,此时调用connect(),该方法可能在连接建立之前就返回了。为了确定连接是否建立,可以调用finishConnect()的方法。像这样:
1
socketChannel.configureBlocking(false);
2
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));
3
4
while(! socketChannel.finishConnect() ){
5
//wait, or do something else...
6
}
write()
非阻塞模式下,write()方法在尚未写出任何内容时可能就返回了。所以需要在循环中调用write()。前面已经有例子了,这里就不赘述了。
read()
非阻塞模式下,read()方法在尚未读取到任何数据时可能就返回了。所以需要关注它的int返回值,它会告诉你读取了多少字节。
调试的时候想要转化byteBuffer?
提供给你两个方法
String 转换 ByteBuffer:
public static ByteBuffer getByteBuffer(String str) {
return ByteBuffer.wrap(str.getBytes());
}
ByteBuffer 转换 String:
public static String getString(ByteBuffer buffer) {
Charset charset = null;
CharsetDecoder decoder = null;
CharBuffer charBuffer = null;
try {
charset = Charset.forName("UTF-8");
decoder = charset.newDecoder();
//用这个的话,只能输出来一次结果,第二次显示为空
// charBuffer = decoder.decode(buffer);
charBuffer = decoder.decode(buffer.asReadOnlyBuffer());
return charBuffer.toString();
} catch (Exception ex) {
ex.printStackTrace();
return "error";
}
}
想要直接转化为byte[] ?
可以直接使用byteBuffer.array();
遇到的坑:
SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ);
在配置selectionKey的时候,之前是用了Selection.OP_WRITE的,这导致keys的事件集合中一直存在一个isWritable事件,原因是:
key.isWritable()是表示Socket可写,网络不出现阻塞情况下,一直是可以写的,所认一直为true.一般我们不注册OP_WRITE事件.