非阻塞Socket通信
最近在做一个基于TCP协议的数据同步Android移动端,研究发现在通信的过程中,Socket通信分为阻塞式和非阻塞式。
通俗来讲,阻塞式Socket通信就像一个单行道(SocketChannel),举个例子,有一个煤场,货车装完煤出厂只有一个单行道。煤场默认的出厂方式是按照进入厂区的序号(1,2,3......),厂区里面同时有几个装载机给货车装煤。假如此时,后面序号的几辆车都装满煤,等待出厂,而此时,1号货车没有装满,那么这时,交通处于“阻塞状态”,只有等1号车装满才能畅通。而非阻塞的方式是这样的,此时厂区安排了一个车辆引导员,他负责选择(Selector)已经装满煤的货车先开走,这样出厂单行道是畅通的,“非阻塞方式”,大大提高了运行效率。
下面给出一个代码示例:
注:界面就不列出了,就一个TextView,代码是未封装前的调试阶段,已通。
SelectionKey key;;
//定义处理编码和解码的字符集
private Charset charset = Charset.forName("UTF-8");
public String pmsg = "";
public String msg = "";
Socket socket = null;
TextView textview_show;
static CharsetEncoder encoder = Charset.forName("GB2312").newEncoder();
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
textview_show.setText((String)msg.obj);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textview_show = (TextView) findViewById(R.id.textview_show_message);
AndroidServer tr = new AndroidServer();
Thread thread = new Thread(tr);
thread.start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public class AndroidServer implements Runnable{
public void run() {
try {
String htmp="";
Selector selector = Selector.open(); //静态方法 实例化selector
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false); //设置为非阻塞方式,如果为true 那么就为传统的阻塞方式
SharedPreferences preferences = getSharedPreferences("setup", MODE_PRIVATE);
Integer getport;
String ctmp=preferences.getString("jrdk", "");
if (ctmp.equals("")||ctmp==null){
getport=7100;
}else{
getport=Integer.valueOf(ctmp);
}
serverChannel.socket().bind(new InetSocketAddress(7100)); //绑定IP 及 端口
serverChannel.register(selector, SelectionKey.OP_ACCEPT); //注册 OP_ACCEPT事件
while(true)
{
try
{
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iter = keys.iterator();
SocketChannel sc = null ;
while(iter.hasNext())
{
key = iter.next();
// 新的连接
if(key.isAcceptable()){
// 调用accept方法接受连接,产生服务器端对应的SocketChannel
sc = serverChannel.accept();
// 设置采用非阻塞模式
sc.configureBlocking(false);
// 将该SocketChannel也注册到selector
sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
// 将sk对应的Channel设置成准备接受其他请求
key.interestOps(SelectionKey.OP_ACCEPT);
}
// 可读
if(key.isReadable()) {
// 获取该SelectionKey对应的Channel,该Channel中有可读的数据
sc = (SocketChannel) key.channel();
// 定义准备执行读取数据的ByteBuffer
ByteBuffer buff = ByteBuffer.allocate(1024);
String content = "";
try {
while (sc.read(buff) > 0) {
buff.flip();
content += charset.decode(buff);
if (content!=null)
{
System.out.println("From Server: " + content);
//显示
String temp = textview_show.getText().toString();
mHandler.sendMessage(mHandler.obtainMessage(0, content + "\n" + temp));
// textview_show.setText(content + "\n" + textview_show.getText());
ctmp =preferences.getString("zfdk", "");
try{
htmp = content;
}catch(Exception e) {
System.out.println("S: Error");
e.printStackTrace();
}
}
}
// 将sk对应的Channel设置成准备下一次读取
key.interestOps(SelectionKey.OP_READ);
}
catch (IOException ex) {
// 从Selector中删除指定的SelectionKey
key.cancel();
if (key.channel() != null) {
key.channel().close();
}
}
}
// 可写
if (key.isWritable()) {
// String name = (String) key.attachment();
ByteBuffer block = encoder.encode(CharBuffer.wrap("from client!"));
System.out.println("发送成功! ");
sc.write(block);
// sc.write(ByteBuffer.wrap(new String("from client!").getBytes()));
}
}
iter.remove(); //处理完事件的要从keys中删去
}
catch (Exception e)
{
e.printStackTrace();
}
}
}catch(Exception e) {
System.out.println("S: Error");
e.printStackTrace();
}
}
}
public static String str2HexStr(String str)
{
char[] chars = "0123456789ABCDEF".toCharArray();
StringBuilder sb = new StringBuilder("");
byte[] bs = str.getBytes();
int bit;
for (int i = 0; i < bs.length; i++)
{
bit = (bs[i] & 0x0f0) >> 4;
sb.append(chars[bit]);
bit = bs[i] & 0x0f;
sb.append(chars[bit]);
sb.append(' ');
}
return sb.toString().trim();
}
public static ByteBuffer getByteBuffer(String str)
{
return ByteBuffer.wrap(str.getBytes());
}
}