編輯:初級開發
之前采用聊天敲門的方式來介紹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
private SocketChannel readingsocket = null;
public YaoChatServer()
{
System.out.println("Inside startserver ");
}
public YaoChatServer(int port)
{
System.out.println("Inside startserver ");
thisport = port;
}
public void initializeOperations() throws IOException,UnknownHostException
{
System.out.println("Inside initialization ");
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
}
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();
}
}
}
}
移動開發大潮興起於蘋果公司的iPhone,移動浏覽器也隨之得到迅猛發展,特別是對Html5、CSS3以及CSS動畫的支持。之後Google公司推出android更是開啟
很多Android開發者可能因為沒有充分測試自己的軟件造成很容易出現FC(Force Close)的問題,這裡我們可以通過使用Android固件中自帶的monkey工具
上課講義摘錄之13:android的IBinder介面及其安全性機制Activity物件與Service物件在不同的進程(Process)裡執行,各有不同的UID(Un
webvIEw很簡單,和普通widget沒什麼區別,就是要在androidManifest.XML中加一句<uses-permission android:nam