android socket通訊demo (本篇服務於android訊息推送)
本文系作者原創,轉載請附原文地址,謝謝。
文章末尾提供本文中的原始碼下載連結,需資源積分1分,人艱不拆,下載後評論資源可獲系統返回積分=無損!前言:
關於什麼是socket通訊,本篇文件中不進行解釋,不甚清楚的可以去百科查詢,日後得空我也會整理相關的內容。
本文是對上一篇關於訊息推送的文章的補充,此處給出快速連結:http://blog.csdn.net/a774057695/article/details/47024887
本篇書寫的目的:
1. 讓android客戶端和伺服器端實現socket通訊,且demo需實現雙向
2. 為實現伺服器向客戶端推送訊息奠定基礎
特別說明:
1. 伺服器端利用pc機執行apach伺服器模擬
2. Pc整合xampp環境
3. 伺服器端eclipse編譯,java實現
4. 客戶端使用真機除錯(使用模擬器需要對原始碼進行修改,因為沒有嘗試,所以沒有給出示例程式碼,使用模擬器調試出錯的請留意該點)
5. 手機和pc加入同一域,至少連入同一臺路由器,使用wifi共享的方法本文不提供技術支援、不保證其可行性。
功能概述:
l 伺服器端:
主函式實現:阻塞的程式碼段等待socket連線、啟動執行緒為socket連線提供應答;
互動執行緒(暫且這樣稱呼):實現收到客戶端訊息後進行反饋
單向執行緒(暫且這樣稱呼):實現接受控制檯輸入並向客戶端傳送
連線斷開刪去該條socket
l 客戶端:
實現向伺服器端建立socket連線
啟動執行緒監聽伺服器傳送的訊息、並反饋UI顯示
將本地訊息發往伺服器
程式碼結構:
伺服器端:(主要)
|-- MyServer
|--src
|--[package]
|--MyServer.java
|--ServerThread.java
|--Thread2.java
客戶端:(主要)
|--MultiThreadClient
|--src
|--[package]
|--ClientThread.java
|--MainActivity.java
|--res
|--layout
|--activity_main.xml
|--AndroidManifest.xml
程式碼示例及關鍵點:
l 伺服器端:
MyServer.java
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class MyServer {
//定義儲存所有Socket的ArrayList
public static ArrayList<Socket> socketList = new ArrayList<Socket>();
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// param 埠號
ServerSocket ss = new ServerSocket(30000);
while(true){
//此程式碼會阻塞,將一直等待別人連線
Socket s = ss.accept();
socketList.add(s);
/*每當客戶端連線後啟動一條ServerThread執行緒為該客戶端服務*/
new Thread(new ServerThread(s)).start();
new Thread(new Thread2(s)).start();
}
}
}
此處我們需要留意的是埠號,第一不要去使用系統關鍵服務的埠號,第二我們要注意客戶端中的埠號需要匹配,在後面我們還會提及。
ServerThread.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class ServerThread implements Runnable {
//定義當前執行緒所處理的Socket
Socket s = null;
//該執行緒所處理的Socket所對應的輸入流
BufferedReader br = null;
public ServerThread(Socket s) throws IOException {
this.s = s;
//初始化該Socket對應的輸入流
br = new BufferedReader(new InputStreamReader(s.getInputStream(),
"utf-8"));
}
@Override
public void run() {
try
{
String content = null;
//採用迴圈不斷從Socket中讀取客戶端傳送過來的資料
while((content = readFromClient())!=null){
//遍歷socketList中的每個Socket
//將讀到的內容向每個Socket傳送一次
for(Socket s : MyServer.socketList){
OutputStream os = s.getOutputStream();
//簡單處理模擬應答
os.write((content + "——返回自伺服器\n").getBytes("utf-8"));
}
}
}catch (IOException e) {
e.printStackTrace();
}
}
/**
* 讀取客戶端資料
* @return
*/
public String readFromClient()
{
try{
return br.readLine();
}catch(IOException e){ //如果捕捉到異常,表明該Socket對應的客戶端已經被關閉
//刪除該Socket
MyServer.socketList.remove(s);
}
return null;
}
}
這裡也沒有太多值得注意的,這條執行緒是實現對客戶端應答,當客戶端向伺服器發出訊息,伺服器收到後進行反饋,這裡我們只是對原來的內容進行了簡單的處理,可以按照伺服器-客戶端之間的約定,實現更有意義的事情。
當連線斷開時,刪除該socket
Thread2.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class Thread2 implements Runnable {
//定義當前執行緒所處理的Socket
Socket s = null;
//該執行緒所處理的Socket所對應的輸入流
BufferedReader br = null;
public Thread2(Socket s) throws IOException {
this.s = s;
//初始化該Socket對應的輸入流
br = new BufferedReader(new InputStreamReader(s.getInputStream(),
"utf-8"));
}
/**
* 讀取控制檯輸入
* */
private void readConsole() {
//陣列緩衝
byte[] b = new byte[1024];
//有效資料個數
int n = 0;
try{
while(true){
//提示資訊
System.out.println("請輸入:");
//讀取資料
n = System.in.read(b);
//轉換為字串
String str = new String(b,0,n - 2);
//傳送到客戶端
for(Socket s : MyServer.socketList){
OutputStream os = s.getOutputStream();
os.write(("伺服器傳送"+str + "\n").getBytes("utf-8"));
}
//判斷是否是quit
if(str.equalsIgnoreCase("quit")){
break; //結束迴圈
}
//回顯內容
System.out.println("輸入內容為:" + str);
}
}catch(Exception e){}
}
@Override
public void run() {
readConsole();
}
}
這條執行緒實現讀取控制檯輸入內容並向客戶端傳送,這裡我們沒有關心連線斷開,這與demo的目的不衝突,但是投入生產時還是應當注意。
l 客戶端:
AndroidManifest.xml
建立一個應用,首先做得事情不是在預設開啟的佈局檔案中進行佈局,而是規劃一下需要哪些許可權,並在配置檔案中新增!
本處我們需要申請Internet許可權:
<uses-permission android:name="android.permission.INTERNET"/> |
手打的朋友請注意:是uses,不是user!
activity_main.xml
預覽:(真的太大,求調大小功能) |
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom"
android:orientation="vertical" >
<!--個並不美觀的佈局 -->
<EditText
android:id="@+id/show"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/linearLayout1"
android:layout_alignParentLeft="true"
android:cursorVisible="false"
android:gravity="top" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/linearLayout1"
android:orientation="horizontal"
android:weightSum="5"
android:layout_alignParentBottom="true" >
<EditText
android:id="@+id/input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
>
<requestFocus />
</EditText>
<Button
android:id="@+id/send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="send" />
</LinearLayout>
</RelativeLayout>
MainActivity.java
package com.example.multithreadclient;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity {
EditText input, show;
TextView mode;
Button send,smode,cmode;
OutputStream os;
Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
input = (EditText) findViewById(R.id.input);
send = (Button) findViewById(R.id.send);
show = (EditText) findViewById(R.id.show);
handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 0x123) {
show.append("\n" + msg.obj.toString());
}
};
};
new AsyncTask<String, String, String>(){
@Override
protected String doInBackground(String... params) {
// TODO Auto-generated method stub
Socket s;
try {
/**@ param param1 param2
* param1: 伺服器ip地址,本例中,即pc機的ip地址 cmd輸入ipconfig -all可查詢
* param2:埠號 注意和伺服器原始碼中的匹配本
*例中30000
* */
s = new Socket(param1,param2);
//客戶端啟動ClientThread執行緒,不斷讀取來自伺服器的資料
new Thread(new ClientThread(s, handler)).start();
os = s.getOutputStream();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}.execute("");
send.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
try {
//將使用者在文字框中輸入的內容寫入網路
os.write((input.getText().toString() + "\r\n")
.getBytes("utf-8"));
input.setText("");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
os.write("".getBytes("utf-8"));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
值得特別注意的就是兩個引數!在註釋中已經說得很明白了,第一個是pc機的ip,第二個是伺服器約定的埠號。使用模擬器進行除錯需要修改ip,我印象中是10.0.2.2,而127.0.0.1會被認為是android模擬器本身。
ClientThread.java
package com.example.multithreadclient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import android.os.Handler;
import android.os.Message;
public class ClientThread implements Runnable {
private Socket s; //該執行緒負責處理的Socket
private Handler handler;
//該執行緒所處理的Socket對應的輸入流
BufferedReader br = null;
public ClientThread(Socket s, Handler handler) throws IOException {
this.s = s;
this.handler = handler;
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
String content = null;
//不斷讀取Socket輸入流中的內容
while ((content = br.readLine()) != null) {
//每當讀到來自伺服器的資料後,傳送訊息通知程式介面顯示資料
Message msg = new Message();
msg.what = 0x123;
msg.obj = content;
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
此處沒有特別關注的地方
執行測試:
除錯android應用,將其安裝到手機,只是想先裝上去而已
執行xampp panel 啟動apach服務
Eclipse中執行伺服器端程式
重新啟動手機端的客戶端程式——只是需要伺服器先啟動,客戶端再連線而已。
你可以在eclipse的console中輸入想要傳送的訊息,回車會被去掉,輸入quit回車可以停止之,然並卵
你也可以在手機端傳送訊息等待回傳訊息
截圖就不附了