標籤:android 即時通訊 udp tcp 通訊
這幾天學習了下在android中實現即時通訊的方法,一開始,自然是從基本的網路通訊協定中開始嘗試了,這樣能夠最大化的私人訂製自己的應用,還能學習到更多的知識,好處多多,接下來就簡單介紹下兩種協議的不同點吧
TCP協議:提供IP環境下的資料可靠傳輸,它提供的服務包括資料流傳送、可靠性、有效流控、全雙工系統操作和多工。通過連線導向、端到端和可靠的資料包發送。就如給懸崖上的兩人通訊時,他必須先把橋建好,確認橋是沒問題的情況下,才把信件交過去,以後大家每次通訊時,都確認下橋沒什麼問題,再通過這座橋來回通訊了。
UDP協議:不為IP提供可靠性、流控或差錯恢複功能,在正式通訊前不必與對方先建立串連,不管對方狀態就直接發送。這個就是飛鴿傳書了~
雖然UDP可靠性不如TCP協議,但是通訊效率高於TCP。在網速極差的情況下優先考慮UDP協議,網速好的話TCP還是很方便使用的。
在Java中使用TCP可以通過java.net.Socket;這個類
<span style="font-family:Microsoft YaHei;font-size:18px;">建立串連</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">//執行個體化一個Socket對象socket = new Socket();//與對應的ip、連接埠進行串連,先要把橋建好socket.connect(new InetSocketAddress(ip, port), 3000);</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">發送資訊</span>
</pre><pre name="code" class="java"><span style="font-family:Microsoft YaHei;font-size:18px;">InputStream ois = socket.getInputStream();DataInputStream dis = new DataInputStream(new BufferedInputStream(ois));//讀取伺服器發過來的資訊,如果沒資訊將會阻塞線程msg = dis.readUTF();</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">發送資訊</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">//獲得輸出資料流DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));//發送資料dos.writeUTF(msg);</span>
接下來上源碼,為三個Thread的子類,分別對應上面三個
<span style="font-family:Microsoft YaHei;font-size:18px;">public class SocketThread extends Thread{private Socket socket;private Client client;private String ip;private int port;private boolean isStart=false;private MessageListener mMessageListener;/** * * 使用TCP協議,串連訪問 * @param ip 目標機器的IP * @param port 連接埠 * @param mMessageListener 收到伺服器端資料時將回調該介面內的 * public void Message(String msg)方法 */public SocketThread(String ip, int port,MessageListener mMessageListener) {this.ip = ip;this.port = port;this.mMessageListener = mMessageListener;}public void run() {try {//執行個體化一個Socket對象socket = new Socket();//與對應的ip、連接埠進行串連,先要把橋建好socket.connect(new InetSocketAddress(ip, port), 3000);if (socket.isConnected()) {System.out.println("Connected..");client = new Client(socket,mMessageListener);//開啟對應的輸入/輸出流監聽client.start();isStart=true;}} catch (IOException e) {e.printStackTrace();isStart=false;}}// 直接通過client得到讀線程public ClientInputThread getClientInputThread() {return client.getIn();}// 直接通過client得到寫線程public ClientOutputThread getClientOutputThread() {return client.getOut();}//返回Socket狀態public boolean isStart(){return isStart;}// 直接通過client停止讀寫訊息public void setIsStart(boolean isStart) {this.isStart = isStart;client.getIn().setStart(isStart);client.getOut().setStart(isStart);}//發送訊息public void sendMsg(String msg){client.getOut().sendMsg(msg);}public class Client {private ClientInputThread in;private ClientOutputThread out;public Client(Socket socket,MessageListener mMessageListener) {//用這個監聽輸入資料流線程來接收資訊in = new ClientInputThread(socket);in.setMessageListener(mMessageListener);//以後就用這個監聽輸出資料流的線程來發送資訊了out = new ClientOutputThread(socket);}public void start() {in.setStart(true);out.setStart(true);in.start();out.start();}// 得到讀訊息線程public ClientInputThread getIn() {return in;}// 得到寫訊息線程public ClientOutputThread getOut() {return out;}}}</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">public class ClientInputThread extends Thread {private Socket socket;private String msg;private boolean isStart = true;private InputStream ois;private DataInputStream dis;private MessageListener messageListener;// 訊息監聽介面對象public ClientInputThread(Socket socket) {this.socket = socket;try {ois = socket.getInputStream();dis = new DataInputStream(new BufferedInputStream(ois));} catch (IOException e) {e.printStackTrace();}}/** * 提供給外部的訊息監聽方法 * * @param messageListener * 訊息監聽介面對象 */public void setMessageListener(MessageListener messageListener) {this.messageListener = messageListener;}public void setStart(boolean isStart) {this.isStart = isStart;}@Overridepublic void run() {try {while (isStart) {//讀取資訊,如果沒資訊將會阻塞線程msg = dis.readUTF();// 每收到一條訊息,就調用介面的方法Message(String msg)Log.v("收到訊息", msg);messageListener.Message(msg);}ois.close();if (socket != null)socket.close();} catch (IOException e) {e.printStackTrace();}}BufferedReader reader=null;public String getInputStreamString() {/* * To convert the InputStream to String we use the * BufferedReader.readLine() method. We iterate until the BufferedReader * return null which means there's no more data to read. Each line will * appended to a StringBuilder and returned as String. */if (ois != null) {reader = new BufferedReader(new InputStreamReader(ois));}StringBuilder sb = new StringBuilder();String line = null;try {while ((line = reader.readLine()) != null) {sb.append(line + "\n");}} catch (IOException e) {e.printStackTrace();} return sb.toString();}}</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">public class ClientOutputThread extends Thread {private Socket socket;private DataOutputStream dos;private boolean isStart = true;private String msg;public ClientOutputThread(Socket socket) {this.socket = socket;try {dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));} catch (IOException e) {e.printStackTrace();}}public void setStart(boolean isStart) {this.isStart = isStart;}// 這裡處理跟伺服器是一樣的public void sendMsg(String msg) {this.msg = msg;synchronized (this) {notifyAll();}}@Overridepublic void run() {try {while (isStart) {if (msg != null) {dos.writeUTF(msg);dos.flush();msg=null;synchronized (this) {wait();// 發送完訊息後,線程進入等待狀態}}}dos.close();// 迴圈結束後,關閉輸出資料流和socketif (socket != null)socket.close();} catch (InterruptedException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">//定義接收到訊息時的,處理訊息的介面public interface MessageListener {public void Message(String msg);}</span>
主介面,感覺很醜,將就吧
<span style="font-family:Microsoft YaHei;font-size:18px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.chatclient.MainActivity" > <ScrollView android:id="@+id/svMessage" android:layout_width="fill_parent" android:layout_height="100dp" > <TextView android:id="@+id/tvMessage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="訊息內容:\n" /> </ScrollView> <EditText android:id="@+id/etMessage" android:layout_width="100dp" android:layout_height="wrap_content" android:layout_below="@+id/svMessage" /> <TextView android:id="@+id/tvSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/svMessage" android:layout_marginLeft="10dp" android:layout_toRightOf="@+id/etMessage" android:text="發送訊息" android:textSize="25sp" />"</RelativeLayout></span>
MainActivity代碼
<span style="font-family:Microsoft YaHei;font-size:18px;">public class MainActivity extends Activity {EditText etMessage;TextView tvSend, tvMessage;SocketThread client;MyHandler myHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);setup();}public void setup() {etMessage = (EditText) findViewById(R.id.etMessage);tvSend = (TextView) findViewById(R.id.tvSend);tvMessage = (TextView) findViewById(R.id.tvMessage);tvSend.setOnClickListener(onClick);myHandler = new MyHandler();//初始化client = new SocketThread("10.21.56.226", 8888,new MessageListener() {//收到訊息後調用此方法@Overridepublic void Message(String msg) {// TODO Auto-generated method stub// tvMessage.append(msg);Bundle bundle = new Bundle();bundle.putString("input", msg);Message isMessage = new Message();isMessage.setData(bundle);//使用handler轉寄myHandler.sendMessage(isMessage);}});//正式啟動線程client.start();}OnClickListener onClick = new OnClickListener() {public void onClick(android.view.View v) {String message = etMessage.getText().toString();Log.v("發送訊息", message);if (client.isStart()) {client.sendMsg(message);}};};private class MyHandler extends Handler {@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubLog.v("處理收到的訊息", " ");tvMessage.append(msg.getData().getString("input"));}}}</span>
伺服器端的代碼,主要是使用ServerSocket監聽一個連接埠,來與用戶端連結和收發資訊
<span style="font-family:Microsoft YaHei;font-size:18px;">public class ChatServer { boolean started = false; ServerSocket ss = null; List<Client> clients = new ArrayList<Client>(); public static void main(String[] args) {new ChatServer().start(); } public void start() {try { //ServerSocket監聽8888連接埠 ss = new ServerSocket(8888); started = true;} catch (BindException e) { System.out.println("start...."); System.out.println("有問題"); e.printStackTrace(); System.exit(0);} catch (IOException e) { e.printStackTrace();}try { while (started) {Socket s = ss.accept();Client c = new Client(s);System.out.println("a client connected!");new Thread(c).start();clients.add(c);// dis.close(); }} catch (IOException e) { e.printStackTrace();} finally { try {ss.close(); } catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace(); }} } class Client implements Runnable {private Socket s;private DataInputStream dis = null;private DataOutputStream dos = null;private boolean bConnected = false;public Client(Socket s) { this.s = s; try {dis = new DataInputStream(s.getInputStream());dos = new DataOutputStream(s.getOutputStream());bConnected = true; } catch (IOException e) {e.printStackTrace(); }}public void send(String str) { try {dos.writeUTF(str); } catch (IOException e) {clients.remove(this);System.out.println("關閉一個串連");// e.printStackTrace(); }}public void run() { try {while (bConnected) { String str = dis.readUTF(); System.out.println(str); for (int i = 0; i < clients.size(); i++) {Client c = clients.get(i);c.send(str);// System.out.println(" a string send !"); } /* * for(Iterator<Client> it = clients.iterator(); * it.hasNext(); ) { Client c = it.next(); c.send(str); } */ /* * Iterator<Client> it = clients.iterator(); * while(it.hasNext()) { Client c = it.next(); c.send(str); * } */} } catch (EOFException e) {System.out.println("Client closed!"); } catch (IOException e) {e.printStackTrace(); } finally {try { System.out.println("close All !"); if (dis != null)dis.close(); if (dos != null)dos.close(); if (s != null) {s.close();// s = null; }} catch (IOException e1) { e1.printStackTrace();} }} }}</span>接下來先運行伺服器代碼,再運行手機端就可以了,多台手機可以互相發送資訊了。
晚點再寫UDP的
android實現基於TCP和UDP協議的即時通訊,含android端和伺服器端