一步一步android(16):关于socket编程(2)【以非阻塞I/O服务器及Service为例】

来源:互联网 发布:android listview优化 编辑:程序博客网 时间:2024/05/16 19:32

http://hi.baidu.com/yaoyuanhuajx/item/dc613bf21b111c2d743c4cd7

之前采用聊天敲门的方式来介绍Socket通信,有两个不足的地方,

1.服务器会造成IO的阻塞
即服务器一旦执行server.accept();
将一直处于阻塞状态,直到有客户端请求连接。

2.服务器端没有建立用户列表,无法将某一客户端发送的消息广播给所有正在连接的客户端。
就好象是一个人自说自话,自己发送给客户端,自己接收服务器返回的消息。

基于以上两点,我改进了我的程序。

服务器端的改进:

1.通过采用socketchannel的非阻塞方式进行通信
2.建立Userlist客户端的哈希表,存储 已连接客户端的 ip地址和 服务器为其分发的socketchannel

客户端的改进:
1.采用Service 与服务器端进行连接,发送数据,实时监听服务器返回的数据。

流程图:



需要改进的地方
服务器端:
1.当一个客户端断开连接以后,另一个客户端在收到消息之前也断开连接,而此时服务器正在向客户端发送消息,
因此,服务器的Thread.sleep时间不能太长,但也不能太短,因为考虑到服务器的负荷问题。
2.服务器容错处理机制需要改进。
客户端:
1.将Notificationbar改为其他更为直观方式刷新显示。
2.容错处理机制的处理。

下面是效果图:

服务器端:


dos客户端:

Android客户端:



效果图的意思是,Android的客户端通过绑定Service与服务器端进行了连接,并发送消息。服务器向所有正在连接的客户端广播消息。

之后,Dos终端也进行连接,并发送消息,服务器接到消息后向所有正在连接的客户端广播消息(其中包括在线的android手机)


源代码如下:

Server端:

package com.android.Yao;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
import java.nio.charset.*;
import java.lang.*;


public class YaoChatServer
{
    public Selector sel = null;
    public ServerSocketChannel server = null;
    public SocketChannel socket = null;
    public int thisport = 4900;
    private String result = null;
    private Hashtable<String,SocketChannel> userlists ;
   
    private SocketChannel readingsocket = null;
   
    public YaoChatServer()
    {
    System.out.println("Inside startserver\n");
    }
   
public YaoChatServer(int port)
    {
   System.out.println("Inside startserver\n");
   thisport = port;
    }

    public void initializeOperations() throws IOException,UnknownHostException
    {
   System.out.println("Inside initialization\n");
   sel = Selector.open();
   server = ServerSocketChannel.open();
   server.configureBlocking(false);
   InetAddress ia = InetAddress.getLocalHost();
   InetSocketAddress isa = new InetSocketAddress(ia,thisport);
   server.socket().bind(isa);
   userlists = new Hashtable<String,SocketChannel>();  
    }

public void startServer() throws IOException
    {
        initializeOperations();
        server.register(sel, SelectionKey.OP_ACCEPT);

        while (sel.select() > 0 )
   {
            Set readyKeys = sel.selectedKeys();
       Iterator it = readyKeys.iterator();

      
       while(it.hasNext())
       {
        SelectionKey key = (SelectionKey)it.next();
     it.remove();
    
          if (key.isAcceptable())
          {
              ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
         socket = (SocketChannel) ssc.accept();
         socket.configureBlocking(false);
         String socketname = socket.socket().getRemoteSocketAddress().toString();
              socket.register(sel, SelectionKey.OP_WRITE);
             
         userlists.put(socketname,socket);
         System.out.println(socketname +" is connected!");        
          }
         
     if (key.isWritable()) {
      readingsocket =(SocketChannel)key.channel();
      String ret=readMessage(readingsocket);

      if (ret.equalsIgnoreCase("@@@@@ is going to say goodbye!"))
      {
       key.cancel();
       readingsocket.close();
       userlists.remove(readingsocket.socket().getRemoteSocketAddress().toString());
       System.out.println("send server msg:"+ret.replace("@@@@@", readingsocket.socket().getRemoteSocketAddress().toString()));
       sendMessage(ret.replace("@@@@@", readingsocket.socket().getRemoteSocketAddress().toString()));
      }
      else if (ret.length() > 0 ) {      
       System.out.println("send server msg:"+ret);
       sendMessage(ret);
      }
     }
       }
   }
    }


    public void sendMessage(String msg) throws IOException
{
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    buffer = ByteBuffer.wrap(msg.getBytes());
//   ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes("UTF-8"));
   Collection channels = userlists.values();  
   SocketChannel sc;
   for(Object o:channels){  
      sc = (SocketChannel)o;  
      sc.write(buffer);  
      buffer.flip();
     
      try {
     Thread.sleep(500);
    } catch (InterruptedException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
}
    public String readMessage(SocketChannel sc)
    {
   int nBytes = 0;  
        ByteBuffer buf = ByteBuffer.allocate(1024);
       
        try {
    nBytes = sc.read(buf);       
    buf.flip();
    Charset charset = Charset.forName("UTF-8");
    CharsetDecoder decoder = charset.newDecoder();
    CharBuffer charBuffer = decoder.decode(buf);
    result = charBuffer.toString();
   } catch (IOException e) {   
    result = "@@@@@ is going to say goodbye!";
   }
   return result;
    }

    public static void main(String args[])
    {
    YaoChatServer nb = new YaoChatServer();
   try
   {
    nb.startServer();
   }
   catch (IOException e)
   {
    e.printStackTrace();
    System.exit(-1);
   }  
}   
}

Android客户端的Service类:

package com.android.Yao;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Collection;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class ReceiveMessage extends Service{


    private SocketChannel client = null;
    private InetSocketAddress isa = null;
    private String message="";

     public void onCreate() {
         super.onCreate();
        
         ConnectToServer();
         StartServerListener();

       }
   
     public void onDestroy() {
         super.onDestroy();
         DisConnectToServer();
       }

     public void onStart(Intent intent, int startId) {
         super.onStart(intent, startId);        
     }
   
    
     /*
      * IBinder方法 , LocalBinder 类,mBinder接口这三项用于
      * Activity进行Service的绑定,点击发送消息按钮之后触发绑定
      * 并通过Intent将Activity中的EditText的值
      * 传送到Service中向服务器发送
      *
      * */
   public IBinder onBind(Intent intent) {
   
    message = intent.getStringExtra("chatmessage");
    if(message.length()>0)
    {
     SendMessageToServer(message);
    }
    return mBinder;
   }
  

     public class LocalBinder extends Binder {
         ReceiveMessage getService() {
             return ReceiveMessage.this;
         }
     }    
     private final IBinder mBinder = new LocalBinder();

    
   //用于链接服务器端
     public void ConnectToServer()
     {
    try {
    
     client = SocketChannel.open();
     isa = new InetSocketAddress("192.168.0.107",4900);
     client.connect(isa);
     client.configureBlocking(false);
    
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    
    }
     }
    
     //断开与服务器端的链接
     public void DisConnectToServer()
     {
     try {
     client.close();
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
     }

   
     //启动服务器端的监听线程,从Server端接收消息
     public void StartServerListener()
     {    
     ServerListener a=new ServerListener();
     a.start();
     }
    
    
    
     //向Server端发送消息
     public void SendMessageToServer(String msg)
     {
    
     try
     {
        ByteBuffer bytebuf = ByteBuffer.allocate(1024);
        bytebuf = ByteBuffer.wrap(msg.getBytes("UTF-8"));       
     client.write(bytebuf);
     bytebuf.flip();
    }
     catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
     }
   
   
    
     private void shownotification(String tab)
     {
      NotificationManager barmanager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
      Notification msg=new Notification(android.R.drawable.stat_notify_chat,"A Message Coming!",System.currentTimeMillis());   
      PendingIntent contentIntent=PendingIntent.getActivity(this, 0, new Intent(this,YaoChatRoomAndroid.class), PendingIntent.FLAG_ONE_SHOT);
      msg.setLatestEventInfo(this,"Message" , "Message:"+tab, contentIntent);
      barmanager.notify(0, msg);
     }
    
    
     private class ServerListener extends Thread
     {
     public void run () {
       
      try {
         //无线循环,监听服务器,如果有不为空的信息送达,则更新Activity的UI
         while(true)
         {       
       ByteBuffer buf = ByteBuffer.allocate(1024);  
       client.read(buf);
       buf.flip();
        Charset charset = Charset.forName("UTF-8");
       CharsetDecoder decoder = charset.newDecoder();
       CharBuffer charBuffer;    
       charBuffer = decoder.decode(buf);
       String result = charBuffer.toString();    
       if (result.length()>0)
        shownotification(result);    
         }
     }
      catch (CharacterCodingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
      catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }       
     }
     }

    

}


0 0