根據前兩周寫的關於Socket編程的網絡通信的代碼,現在對有關知識和注意事項進行總結如下:
1.首先說下Android NIO中有關Socket編程的類:
1)ServerSocketChannel類:服務器套接字通道相當於傳統IO下的ServerSocket,通過ServerSocketChannel的socket()可以獲得傳統的ServerSocket,反過來使用ServerSocket的getChannel()可以獲得ServerSocketChannel對象;實例化ServerSocketChannel可以直接通過ServerSocketChannel的靜態方法open()就可以了。
2)SocketChannel類:套接字通道相當於傳統IO下的Socket,通過SocketChannel的socket()可以獲得傳統的Socket,反過來使用Socket的getChannel()可以獲得SocketChannel對象;
3)Selector選擇器:在NIO中注冊各種事件的方法主要使用Selector來實現的,我們可以使用Selector類的靜態方法open()來實例化。
4)SelectionKey類:是個選擇鍵,在NIO中選擇器和選擇鍵是很重要的,SelectionKey描述了NIO中比較重要的事件,如OP_ACCEPT、OP_READ、OP_WRITE。
2.然后說下非阻塞和阻塞模式的區別以及非阻塞模式的實現:
1)非阻塞:所謂非阻塞就是說,服務器監聽客戶端連接的時候,如果沒有客戶連接,程序還接續執行,不會停在這里等待客戶連接;或者客戶連接上了,下一步就是等待客戶發數據,如果不發,程序不會停留在這里,而是繼續執行。反過來停留在這里,不繼續執行就是阻塞。
2)非阻塞模式的實現:對於客戶端來說,得到SocketChannel對象后,通過調用方法configureBlocking(false)來設置這個Socket為非阻塞狀態。然后在配合Selector和SelectionKey的使用來與服務器進行交互。代碼如下:
SocketChannel socket = new SocketChannel(ip,port);
socket.configureBlocking(false);
mSelector = Selector.open();
socket.register(mSelector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
while (true) {
mSelector.select(0);
Set<SelectionKey> readKeys = mSelector.selectedKeys();
Iterator<SelectionKey> iterator = readKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isReadable()) {.....
} else if (key.isWritable()) {.....
}
}
}
3. 消息處理機制—Message Handler Looper
Message消息 Handler處理發送和接收消息 Looper管理消息,就是一個手柄
在什么情況下使用消息呢?比如一個子線程,在執行過程中要更改主線程UI的一個控件值,這個時候就需要發送一個消息給主線程,而使用消息處理機制也可以不干擾主線程執行其他操作,在主線程中只要Handler感應到有信息過來它會自己接收,然后處理。代碼如下:
在主線程:聲明一個Handler
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
在子線程:
public void sendMessage(String message, int what) {
Message msg = mHandler.obtainMessage(what, 1, 1, message);
mHandler.sendMessage(msg);
}
注意子線程中的Handler必須是主線程聲明的Handler,這樣子線程發送的消息才能被主線程Handler認識接收。
4.遇到的問題
1)線程問題:Socket編程中,實現服務器與多個客戶端的通信要盡量用多線程機制來實現,當服務器監聽到一個客戶端的連接就開辟一個線程專門負責與客戶端的通信,如果客戶端因為某個原因斷開與服務器的連接就要及時關閉線程;當然如果是服務器斷開了,那么就要關閉所有與客戶通信的線程。Android開發中,要注意一點,比較耗時的操作最好開辟一個線程單獨執行,不然主線程因為執行這個耗時的操作而耽誤執行其他操作,這樣導致程序效率很差。
2)網絡問題:Android開發中,往往遇到服務器與客戶端正在通信時候突然網絡斷了,這個時候就要關閉socket,但是在關閉socket的時候,雙方要知道網絡斷了才能關閉,如果是人為關閉socket,另一方肯定能檢測到,但是是網絡突然斷開,就不能檢測到了,解決這樣的方法就是,雙方連接上后,每隔一段時間發一個保護數據包,這個數據包雙方只管發送,接收不進行處理,如果在一段時間內,一方既沒有發送正常的數據包也沒有發送保護數據包,那就關閉socket,這樣就解決了網絡斷開的問題導致一方沒有及時關閉socket的問題。
3)socket關閉問題:在進行socket編程中,遇到異常或者其他需要斷開連接的情況時,socket要及時關閉,而在關閉socket之前,要保證線程關閉;舉個列子說下,客戶端人為斷開與服務器的連接,此時服務器就會檢測到與這個客戶的通道已經關閉而出現異常,此時服務器就要關閉線程和socket。
還有一個特別重要的問題,在Android開發中,如果服務器與客戶端通信的時候,如果沒有定義一個協議,只是簡單的發送一個字符串,那么如果某一方發送一個空值,這個時候另一方就會默認的認為你沒有發送數據。解決這個問題的辦法就是,要么定義一個規范的協議發送數據,也就是每發送一個數據都有一個頭,要么定義一個專門的數據包(如byte類型的 1 )代表一個空值。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
獲取手機IP地址
第一種:
public int getIpAddress() {
WifiManager wifiManager = (WifiManager) this.context
.getSystemService(Context.WIFI_SERVICE);
// 判斷wifi是否開啟
if (!wifiManager.isWifiEnabled()) {
wifiManager.setWifiEnabled(true);
}
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
int ipAddress = wifiInfo.getIpAddress();
return ipAddress;
}
private String intToIp(int i) {
return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF)
+ "." + (i >> 24 & 0xFF);
}
第二種:
public String getInetIpAddress() {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface
.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> ipAddr = intf.getInetAddresses(); ipAddr
.hasMoreElements();) {
InetAddress inetAddress = ipAddr.nextElement();
return inetAddress.getHostAddress();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
http://www.cnblogs.com/crearo-ssy/archive/2012/08/16/2640612.html