Java NIO实例-ServerSocketChannel实现Socket传输

参考网上的资料:

Java NIO 系列教程:http://www.iteye.com/magazines/132-Java-NIO

SocketChannel续2---各种注意点:http://www.360doc.com/content/12/0902/22/495229_233834423.shtml

Java NIO类库Selector机制解析(下):http://blog.csdn.net/haoel/article/details/2224069

NIO Selector示意图

Java NIO实例-ServerSocketChannel实现Socket传输_第1张图片


服务端用java实现,客户端用android实现

简单功能(目的为学习基本原理):实现了手机端发送消息到服务端,服务端成功接收

服务端

package cn;

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

/**
 * @author gn
 * @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 SocketServerDemo {
	//Socket协议服务端
	private int port = 9875;
	private ServerSocketChannel serverSocketChannel;
	private Charset charset = Charset.forName("UTF-8");
	private Selector selector = null;

	public SocketServerDemo() throws IOException {
		selector = Selector.open();
		serverSocketChannel = ServerSocketChannel.open();
		serverSocketChannel.socket().setReuseAddress(true);
		serverSocketChannel.socket().bind(new InetSocketAddress(port));
		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 {
		serverSocketChannel.configureBlocking(false);
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		/** 外循环,已经发生了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.isAcceptable()) {
						ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
						SocketChannel sc = ssc.accept();
						System.out
								.println("客户端机子的地址是 "
										+ sc.socket().getRemoteSocketAddress()
										+ "  客户端机机子的端口号是 "
										+ sc.socket().getLocalPort());
						sc.configureBlocking(false);
						ByteBuffer buffer = ByteBuffer.allocate(1024);
						sc.register(selector, SelectionKey.OP_READ , buffer);//buffer通过附件方式,传递
					}
					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();
					}
				}
			}
			/* 内循环完 */
		}
		/* 外循环完 */
	}

	int x = 1;

	/* 接收 */
	 public void reveice(SelectionKey key) throws IOException {
		if (key == null)
			return;
		//***用SelectionKey.attachment()获取客户端消息***//
		//:通过附件方式,接收数据
//		 ByteBuffer buff = (ByteBuffer) key.attachment();
		// SocketChannel sc = (SocketChannel) key.channel();
//		 buff.limit(buff.capacity());
		// buff.position(0);
		// sc.read(buff);
		// buff.flip();
		// String reviceData = decode(buff);
		// System.out.println("接收:" + reviceData);

		//***用channel.read()获取客户端消息***//
		//:接收时需要考虑字节长度		
		SocketChannel sc = (SocketChannel) 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.trim());

		// sc.write(ByteBuffer.wrap(reviceData.getBytes()));
//		try {
//			sc.write(ByteBuffer.wrap(new String(
//					"测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试" + (++x)).getBytes()));// 将消息回送给客户端
//		} catch (IOException e1) {
//			e1.printStackTrace();
//		}
	}
	
	int y = 0;
	public void send(SelectionKey key) {
//		if (key == null)
//			return;
//		ByteBuffer buff = (ByteBuffer) key.attachment();
//		SocketChannel sc = (SocketChannel) 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 SocketServerDemo().service();
	}
}

客户端

package com.android.mealplus.test;

/**
 * Created by gn on 0029 2015-07-29.
 */

import android.telephony.TelephonyManager;
import android.util.Log;

import com.android.mealplus.MealPlusApplication;
import com.android.mealplus.ui.common.DataTypeByte;
import com.android.mealplus.ui.common.NoInData;
import com.android.mealplus.utils.UtilsTools;
import com.android.mealplus.utils.ValidateTools;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;

public class TCPClient {
    // 信道选择器
    private Selector selector;

    // 与服务器通信的信道
    SocketChannel socketChannel;

    // 要连接的服务器Ip地址
    private String hostIp;

    // 要连接的远程服务器在监听的端口
    private int hostListenningPort;
    String imei;

    public TCPClient(String HostIp, int HostListenningPort, String imei) throws IOException {
        this.hostIp = HostIp;
        this.hostListenningPort = HostListenningPort;
        this.imei = imei;
        initialize();
    }


    /**
     * 初始化
     *
     * @throws IOException
     */
    private void initialize() throws IOException {
        try {
            // 获得一个Socket通道
            SocketChannel channel = SocketChannel.open();
            // 设置通道为非阻塞
            channel.configureBlocking(false);
            // 获得一个通道管理器
            this.selector = Selector.open();

            // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
            //用channel.finishConnect();才能完成连接
            channel.connect(new InetSocketAddress(hostIp, hostListenningPort));
            //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
            channel.register(selector, SelectionKey.OP_CONNECT);

        } catch (Exception e) {

            Log.d("a", e.toString());
        }

        // 启动读取线程
        new TCPClientReadThread(selector, imei);
    }

//    /**
//     * 发送字符串到服务器
//     *
//     * @param message
//     * @throws IOException
//     */
//    public void sendMsg(String message) throws IOException {
//        ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes("UTF-8"));
//
//        socketChannel.write(writeBuffer);
//    }


}

package com.android.mealplus.test;

/**
 * Created by gn on 0029 2015-07-29.
 */


import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class TCPClientReadThread implements Runnable {
    // 监听器,如果缓冲区有数据,通知程序接收
    private Selector selector;
    String imei;

    public TCPClientReadThread(Selector selector, String imei) {
        this.selector = selector;
        this.imei = imei;
        new Thread(this).start();
    }

    @Override
    public void run() {
        try {
            // 轮询访问selector
            while (true) {
                selector.select();//如果队列有新的Channel加入,那么Selector.select()会被唤醒
                // 获得selector中选中的项的迭代器
                Iterator ite = this.selector.selectedKeys().iterator();
                while (ite.hasNext()) {
                    SelectionKey key = (SelectionKey) ite.next();
                    // 删除已选的key,以防重复处理
                    ite.remove();
                    // 连接事件发生
                    if (key.isConnectable()) {
                        SocketChannel channel = (SocketChannel) key.channel();
                        // 如果正在连接,则完成连接
                        if (channel.isConnectionPending()) {
                            channel.finishConnect();

                        }
                        // 设置成非阻塞
                        channel.configureBlocking(false);

                        //在这里可以给服务端发送信息哦
                        channel.write(ByteBuffer.wrap(new String("我们测试中国ab").getBytes()));
                        //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
                        channel.register(this.selector, SelectionKey.OP_READ );

                    } else if (key.isReadable()) {
                        read(key);
                    }

                }

            }

        } catch (IOException e) {
            e.printStackTrace();
            Log.d("aa", e.toString());
        }

    }

    /**
     * 处理读取服务端发来的信息 的事件
     *
     * @param key
     * @throws IOException
     */
    public void read(SelectionKey key) throws IOException {

        // 服务器可读取消息:得到事件发生的Socket通道
        SocketChannel channel = (SocketChannel) key.channel();
        // 创建读取的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(10);
        channel.read(buffer);
        byte[] data = buffer.array();
        String msg = new String(data).trim();
        Log.d("aa", "服务端收到信息:" + msg);

//        //发送消息到服务端
//        ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
//        channel.write(outBuffer);// 将消息回送给客户端
    }

    public static final String btyetoString(byte[] bArray) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() == 1) {
                sb.append(0);

            }
            sb.append(sTemp.toUpperCase());
            sb.append(" ");
        }
        return sb.toString();
    }
}

在Activity下调用

 new Thread() {
            @Override
            public void run() {
                try {
                    client = new TCPClient("192.168.1.118", port, ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)).getDeviceId());

                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                }
            }
        }.start();


你可能感兴趣的:(Java NIO实例-ServerSocketChannel实现Socket传输)