1. 程式人生 > >Android網路程式設計的Socket通訊總結

Android網路程式設計的Socket通訊總結

建立伺服器端的步驟
1,指定埠例項化一個ServerSocket

2,呼叫ServerSocket的accept方法等待連線期間阻塞

3,獲取位於底層的Socket流進行讀寫操作

4,將資料封裝成流

5,對Socket進行讀寫

6,關閉流

建立客戶端的步驟:

1,通過IP地址和埠例項化Socket,請求連線伺服器

2,獲取位於底層的Socket流進行讀寫操作

3,將資料封裝成流(BufferedReader/PrintWriter,DataOutputStream/DataInputStream)的例項

4,對Socket進行讀寫

5,關閉流

使用ServerSocket建立伺服器端:

public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		  //建立一個ServerSo檢視特,用於監聽客戶端Socket的連線請求
           ServerSocket ss=new  ServerSocket(3000);
           while(true){
        	   //每當接收到客戶端Socket的請求,伺服器端也對應產生一個Socket,沒接收到請求就等待。。
        	   Socket s=ss.accept();
        	   OutputStream os=s.getOutputStream();//伺服器端產生的Socket獲取輸出流
        	   os.write("您好,您收到了來自伺服器的祝福!\n".getBytes());
        	   os.close();
        	   s.close();
           }
	}
客戶端使用Socket通訊:
測試環境是PC端伺服器,手機當客戶端,PC和手機要連線同一個區域網,PC和手機在同一網段

package com.example.simpleclient;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;


public class MainActivity extends Activity {
     TextView text;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text=(TextView) findViewById(R.id.text);
        new Thread(){

			@Override
			public void run() {
				// TODO Auto-generated method stub
									
					try {
						//建立連線到遠端伺服器的的Socket,Ip是伺服器端PC的IP,測試環境是PC端伺服器,手機當客戶端,PC和手機要連線同一個區域網,PC和手機在同一網段
						Socket socket = new Socket("192.168.88",3000);
						//將Socket對應的輸入流包裝秤BufferedReader
						BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
						String line=br.readLine();
						text.setText("來自服務的資料:"+line);
						br.close();
						socket.close();
					} catch (UnknownHostException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}									
			}
        	
        }.start();
    }
   
}
 <!-- 訪問網路許可權 -->
    <uses-permission android:name="android.permission.INTERNET"/>

客戶端和伺服器端的輸入輸出流的問題容易搞混淆:如下圖


在客戶端:

socket.getInputStream();從socket讀來自伺服器的資料

socket.getOutputStream();向socket中寫資料,傳給伺服器,伺服器在它的socket的輸入流讀這個資料

在伺服器端:

socket.getInputStream();從socket讀來自客戶端的資料

socket.getOutputStream();向socket中寫資料,傳給客戶端,客戶端在它的socket的輸入流讀這個資料

就是說客戶端和伺服器端的輸入輸出流是對應的,輸入流連線到輸出流

輸入輸出流的包裝:

第一種方法:資料二進位制流

DataInputStream in=new DataInputStream(socket.getInputStream());//接收客戶端資訊
DataOutputStream out=new DataOutputStream(socket.getOutputStream());  //向客戶端傳送訊息
第二種方法:
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

socketClient,java

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class socketClient extends Activity {
	private Button button;
	private TextView text;
	private EditText edit;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		button = (Button) findViewById(R.id.button);
		edit = (EditText) findViewById(R.id.edit);
		text = (TextView) findViewById(R.id.text);

		button.setOnClickListener(new View.OnClickListener() {
			private Socket socket = null;

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub

				String sendMsg = edit.getText().toString() + "\r\n";

				try {
					socket = new Socket("192.168.0.37", 8888); // 建立Socket,其中ip地址為我的PC機器的地址,手機通過wifi上網和伺服器在一個網段

					// PrintWriter out = new PrintWriter(new BufferedWriter(new
					// OutputStreamWriter(socket.getOutputStream())),true);
					// out.println(sendMsg);
					//					
					// BufferedReader in = new BufferedReader(new
					// InputStreamReader(socket.getInputStream()));
					// String readMsg = in.readLine();
					// if(readMsg !=null){
					// text.setText(readMsg);
					// }else{
					// text.setText("錯誤");
					// }
					//					
					// out.close();
					// in.close();
					// socket.close();

					DataOutputStream out = new DataOutputStream(socket
							.getOutputStream()); // 向伺服器傳送訊息
					out.writeUTF(sendMsg);
					out.flush();

					DataInputStream in = new DataInputStream(socket
							.getInputStream()); // 接收來自伺服器的訊息
					String readMsg = in.readUTF();
					if (readMsg != null) {
						text.setText(readMsg);
					}
					out.close();
					in.close();
					socket.close();

				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}
}

Server.java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
	public Server(){
		new ServerThread().start();
	}
	class ServerThread extends Thread{
		public void run() {
			try {
				ServerSocket ss=new ServerSocket(8888); ////建立一個ServerSocket物件,並讓這個ServerSocket在8888埠監聽
				while(true){
					Socket socket=ss.accept(); //呼叫ServerSocket的accept()方法,接受客戶端所傳送的請求,如果客戶端沒有傳送資料,那麼該執行緒就停滯不繼續
//					try {
//						BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));  //接收客戶端資訊
//						String readline = in.readLine();
//						System.out.println("readline:"+readline);
//						
//						PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
//						out.println("link server success");
//						
//						in.close();   //關閉流
//						out.close();//關閉流
//					    socket.close();//關閉開啟的socket
//						
//					} catch (Exception e) {
//						// TODO: handle exception
//					}finally{
//					//	socket.close();//
//					}
					try {
						DataInputStream in=new DataInputStream(socket.getInputStream());//接收客戶端資訊
						String readline=in.readUTF();
						System.out.println(readline);
							
						DataOutputStream out=new DataOutputStream(socket.getOutputStream());  //向客戶端傳送訊息
						out.writeUTF("link server success");
						out.flush();
						
						in.close();   //關閉流
						out.close();//關閉流
						socket.close();//關閉開啟的socket
						
					} catch (Exception e) {
						System.out.println(e.getMessage());
					}
				}
			} catch (IOException e) {
				System.out.println(e.getMessage());
			}
		}
	}
	public static void main(String[] args) throws IOException {
		new Server();   //開啟伺服器
	}
	

}

加入多執行緒:聊天室

客戶端和伺服器端保持長時間的通訊,伺服器需要不斷的讀取客戶端資料,並向客戶端寫入資料,客戶端也需要不斷的讀取伺服器的資料

伺服器應該為每個Socket單獨啟動一條執行緒,每條執行緒負責與一個客戶端進行通訊

伺服器端:

package com.hust.multithred;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class MyServer {

	/**
	 * @param args
	 * @throws IOException 
	 */
	//伺服器端儲存所有Socket的ArrayList
	public static  ArrayList<Socket> socketlist=new ArrayList<Socket>();
	
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
      
       ServerSocket ss=new ServerSocket(3000);//ServerSocket監聽3000埠
       while(true){
    	   Socket socket_in_server=ss.accept();//迴圈等待客戶端的Socket
    	   socketlist.add(socket_in_server);   //每接收到一個客戶端的Socket,將伺服器端產生的與之對應的Socket加入陣列
    	   //為每一個Socket單獨啟動一條執行緒,每個執行緒負責與一個客戶端進行通訊
    	   SocketThread socketthread=new SocketThread(socket_in_server);
    	   new Thread(socketthread).start();
       }
       
	}
	

}
package com.hust.multithred;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;

public class SocketThread implements Runnable{//執行緒任務類,實現Runnable介面
    Socket s=null;
    BufferedReader br=null;
    public SocketThread(Socket s) throws IOException{
    	this.s=s;
    	br=new BufferedReader(new InputStreamReader(s.getInputStream()));//Socket輸入流包裝成字元流,來自客戶端的資料在此輸入流上,伺服器端可以讀
    }
	public void run() {
		// TODO Auto-generated method stub
		try {
		String content=null;
		//迴圈不斷衝Socket中讀取客戶端傳送過來的資料
		while((content=readFormClient())!=null){
			//每讀到資料之後,將讀到的內容向每個Socket傳送一次
			for(Socket s:MyServer.socketlist){					
					OutputStream os=s.getOutputStream();
					os.write((content+"\n").getBytes("utf-8"));	//寫到每個socket 的輸出流上				
			}
		}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	//從輸入流上讀取來自客戶端的資料方法
	public String readFormClient(){
		String content=null;
		try {
			content = br.readLine();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			MyServer.socketlist.remove(s);
		}
		return content;
	}
	
}

客戶端:

MainActivity.java

package com.hust.multithreadclient;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
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;
     TextView show;
     Button send;
     Handler handler;
     ClientThread clientthread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        input=(EditText) findViewById(R.id.input);
        show=(TextView) findViewById(R.id.show);
        send=(Button) findViewById(R.id.send);
        //此處handler接收來自子執行緒的訊息,負責處理訊息,更新UI
        handler=new Handler(){

			@Override
			//如果訊息來自子執行緒
			public void handleMessage(Message msg) {
				// TODO Auto-generated method stub
				if(msg.what==0x123){
					show.append("\n"+msg.obj.toString());
				}
			}
        	
        };
        //客戶端啟動ClientThread執行緒建立玩過連線,讀取來自伺服器的資料
        new Thread(new ClientThread(handler)).start();
        
        
        send.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				// 當用戶按下發送按鈕後,將使用者輸入的資料封裝成Message,發給子執行緒的Handler,此處handler負責傳送訊息
				Message msg=new Message();
				msg.what=0x111;
				msg.obj=input.getText().toString();
				clientthread.rvhandler.sendMessage(msg);//發給子執行緒的Handler
				input.setText("");
			}        	
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

ClientThread.java
package com.hust.multithreadclient;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

public class ClientThread implements Runnable {
	 Socket s;
	 Handler handler;	//定義向UI執行緒傳送訊息的Handler物件
     Handler rvhandler; //定義接收UI執行緒訊息的Handler物件
     
     BufferedReader br=null;
     OutputStream os=null;
     public ClientThread(Handler handler){
    	 this.handler=handler;
     }
	@Override
	public void run() {
		// TODO Auto-generated method stub
           try {
			s=new Socket("192.168.1.88",3000);
			br=new BufferedReader(new InputStreamReader(s.getInputStream()));
			os=s.getOutputStream();
			//啟動一條子執行緒來讀取伺服器端相應的資料
			new Thread(){

				@Override
				public void run() {
					// TODO Auto-generated method stub
					String content=null;
					try{
						while((content=br.readLine())!=null){
							Message msg=new Message();
							msg.what=0x123;
							msg.obj=content;
							handler.sendMessage(msg);//此子執行緒中僅僅是讀來自伺服器的資料,併發給UI執行緒的Handler處理
						}
						
					}catch (IOException e) {
					 	// TODO Auto-generated catch block
						  e.printStackTrace();
					 }
				}
				
			}.start();
			/*
			 * 當前客戶端執行緒中Handler,Android執行緒之間的通訊是Handler來實現
			 * 此Hnadler接收UI執行緒發過來的資料,即使用者的輸入,並寫到輸出流
			 * 因為此Handler是接收處理訊息,所以需要使用Looper
			 * */
			Looper.prepare();
			rvhandler=new Handler(){

				@Override
				public void handleMessage(Message msg) {
					//如果接收到UI執行緒中使用者輸入的資料
				    if(msg.what==0x111){
				    	try{
				    		//將使用者在文字框內的輸入內容寫入網路
				    		os.write((msg.obj.toString()+"\r\n").getBytes("utf-8"));
				    	}catch(IOException e){
				    		e.printStackTrace();				    		
				    	}
				    }
				}
				
			};
			Looper.loop();//啟動looper
			
		   } catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			   e.printStackTrace();
		  } catch (IOException e) {
		 	// TODO Auto-generated catch block
			  e.printStackTrace();
		   }
	}

}