在做通信系统的开发过程中,经常需要使用Socket通信。java新的io机制给我提供了一个很好的异步socket通信方式,这段时间用java写了一个客户端用来连接server。发现运行效率还比较让人满意。下面是我实现的部分功能。
连接服务器的socket,多线程启动。如果连接失败就重连。
public class CommonSocket extends Thread {
private SocketChannel socketChannel;
private boolean stop = false;
private int port = 0;
private String ip = "";
private Selector selector = null;
private SocketAddress socketAddress = null;
private Logger logger = Logger.getLogger(CommonSocket.class);
public CommonSocket() {
this.ip = SocketInfoUtils.TCP_IP;
this.port = SocketInfoUtils.TCP_PORT;
}
public void run() {
while (!stop) {
socketConnet();
try {
sleep(5000);
} catch (InterruptedException e) {
logger.error("SocketConnect run error: InterruptedException");
}
}
}
public void socketBuilder() {
try {
selector = Selector.open();
} catch (IOException e) {
e.printStackTrace();
logger.error("Open to selector failed: IOException");
}
}
private void openSocketChannel() {
try {
socketAddress = new InetSocketAddress(ip, port);
socketChannel = SocketChannel.open();
socketChannel.socket().setReuseAddress(true);
socketChannel.connect(socketAddress);
} catch (ClosedChannelException e) {
logger.warn("Channel is closed: ClosedChannelException");
} catch (IOException e) {
logger
.warn("Connet is failed or time out,the system will automatically re-connected : IOException");
}
}
/**
* do ClientBuilder if socket conncte success
*/
public void socketConnet() {
try {
openSocketChannel();
if (socketChannel.isOpen()) {
this.stop = true;
socketBuilder();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ
| SelectionKey.OP_WRITE);
PackageBuilder clientBuilder = new PackageBuilder(socketChannel,
selector);
clientBuilder.start();
logger.info("Has been successfully connected to " + ip
+ "and port: " + port);
} else {
socketChannel.close();
}
} catch (ClosedChannelException e) {
logger.warn("Channel is closed: ClosedChannelException");
} catch (IOException e) {
logger
.warn("Connet is failed or time out,the system will automatically re-connected : IOException");
}
}
}
发送和接收事件处理,NIO是基于事件的驱动模型,这个类就是专门处理收发的。
public class PackageBuilder extends Thread{
private SocketChannel socketChannel = null;
private Selector selector = null;
private boolean stop = false;
private byte[] array = new byte[1024];
private ByteBuffer byteBuffer;
private PackageQueue packageQueue;
private Logger logger = Logger.getLogger(PackageBuilder.class);
public PackageBuilder(SocketChannel socketChannel,Selector selectore){
this.socketChannel = socketChannel;
this.selector = selectore;
packageQueue=new PackageQueue();
}
public void run(){
try {
while (!stop) {
Thread.sleep(1);
if(!socketChannel.isOpen()){
reconnect();//通道没打开或者断开执行重连工作(Channel did not open the work of the implementation of re-connection )
break;
}
if (selector.select(30) > 0) {
doSelector();
}
}
} catch (IOException e) {
logger.error("CameraBuilder run error: IOException");
} catch (InterruptedException e){
logger.error("CameraBuilder run error: InterruptedException");
}
}
public void doSelector(){
for(SelectionKey key:selector.selectedKeys()){
selector.selectedKeys().remove(key);
if(!key.isValid()){
continue;
}
doKeys(key);
}
}
public void doKeys(SelectionKey key){
SocketChannel channel = (SocketChannel)key.channel();
if(key.isReadable()){
readResponse(channel);
}
if(key.isWritable()){
sendRequest(channel);
}
}
private void readResponse(SocketChannel channel) {
byteBuffer=ByteBuffer.wrap(array);
byteBuffer.clear();
int count = 0;
try {
count = channel.read(byteBuffer);
} catch (IOException e) {
reconnect();//通道没打开或者断开执行重连工作(Channel did not open the work of the implementation of re-connection )
logger.error("Connection reset by peer: IOException");
}
if(count != -1){
byteBuffer.flip();
byte[] bs = new byte[count];
byteBuffer.get(bs);
ByteBuffer returnBuffer = ByteBuffer.allocate(count);
returnBuffer.clear();
returnBuffer.put(bs);
returnBuffer.flip();
PrintUtil.printBf(returnBuffer.array());
ParseBufferData parseData=new ParseBufferData(returnBuffer);
parseData.parseBuffer();
}
if(count < 0){
reconnect();
}
}
/**
* send pakcet of request
* @param channel
*/
public void sendRequest(SocketChannel channel){
byte[] array = packageQueue.takeMsgs();
if(array!=null){
ByteBuffer byteBuffer = ByteBuffer.wrap(array);
try {
channel.write(byteBuffer);
} catch (IOException e) {
reconnect();//通道没打开或者断开执行重连工作(Channel did not open the work of the implementation of re-connection )
logger.warn("socket not connected or has been closed: IOException");
}
}
}
public void reconnect(){
stopClient();
logger.warn("socket not connected or has been closed");
ThreadPoolUtil.getExecutor().execute(new CameraSocket());
}
public void stopClient(){
this.stop = true;
if(socketChannel.isConnected() && !socketChannel.isOpen()){
try {
socketChannel.close();
logger.info("server_socket has connected");
} catch (IOException e) {
logger.warn("Channel closed to failed: IOException");
}
}
}
}
发送和接收数据存放在缓存中
public class PackageQueue {
private static List<byte[]> queue = new ArrayList<byte[]>();
public PackageQueue(){
}
public void pushMsgs(byte[] array){
synchronized(queue){
queue.add(array);
}
}
public byte[] takeMsgs() {
synchronized (queue) {
byte[] sd=null;
if(queue != null){
if(queue.size() > 0){
sd = queue.get(0);
queue.remove(0);
}
}
return sd;
}
}
public static List<byte[]> getQueue() {
return queue;
}
public static void setQueue(List<byte[]> queue) {
PackageQueue.queue = queue;
}
}
以上就是客户端连接、发送、接收的代码。希望对大家有所帮助