1. 程式人生 > >Android客戶端與本地服務器Socket通信

Android客戶端與本地服務器Socket通信

msg 初始化 ips print rmi listener 個數 myeclips 開啟

Android客戶端與本地服務器Socket通信

Socket服務器運行結果圖??
技術分享圖片

一.客戶端和服務器端的選擇:

  • 客戶端是我們手機端,關於服務器端,只要安裝了JDK,自然就擁有通訊的功能,我們只需要在Eclipse或者MyEclipse中寫好文章中服務器端的代碼,運行起來即可,用accept()方法啟動服務器端,等待客戶端的連接,在未連接的情況下,服務器端處於堵塞的狀態。

二.客戶端註意事項

  • andriod客戶端添加網絡訪問權限
    <uses-permission android:name="android.permission.INTERNET" />

  • 對Socket的操作放在非UI線程內進行

  • 要使用正確的IP地址和端口號端口號的範圍是0~65535,1024一下的端口被系統分給了一些服務,在cmd窗口執行netstat -ano命令可以看到所有端口的使用情況

  • 真機進行調試(1、連接上手機,手機開啟adb。步驟:設置> 應用程序> 開發>選擇USB調試;usb選項有些不可見,具體百度),指定Server的IP地址,此地址為局域網地址,如果是使用WIFI上網,則為PC機的WIFI IP。上圖中連接的第二個就是示例
    三.Socket通信
  • 利用ip地址+端口號唯一標示網絡中的一個進程,能夠唯一標示網絡中的進程後,它們就可以利用socket進行通信。

  • socket是在應用層和傳輸層之間的一個抽象層,它把TCP/IP層復雜的操作抽象為幾個簡單的接口供應用層調用

    ,實現進程在網絡中通信。

  • socket是"打開—讀/寫—關閉"模式的實現(只能讀取對方放在流中的數據),以使用TCP協議通訊的socket為例,其交互流程大概是這樣子的

  • Socket有兩種主要的操作方式:面向連接的和無連接的,即TCP和UDP。
      面向連接的Socket操作就像一部電話,Socket必須在發送數據之前與目的地的Socket取得連接,一旦連接建立了,Socket就可以使用一個流接口進行打開、讀寫以及關閉操作。並且,所有發送的數據在另一端都會以相同的順序被接收。

  • 無連接的Socket操作就像一個郵件投遞,每一個數據報都是一個獨立的單元,它包含了這次投遞的所有信息(目的地址和要發送的內容)。在這個模式下的Socket不需要連接目的地Socket,它只是簡單的投出數據報。
    四.TCP連接與HTTP連接與Socket連接的區別
  • TCP連接與HTTP連接的區別

HTTP是基於TCP的,客戶端往服務端發送一個HTTP請求時第一步就是要建立與服務端的TCP連接。

  • TCP連接與Socket連接的區別:

socket層只是在TCP/UDP傳輸層上做的一個抽象接口層,基於TCP協議的socket連接同樣需要通過三次握手建立連接,是可靠的;基於UDP協議的socket連接不需要建立連接的過程,不過對方能不能收到都會發送過去,是不可靠的,大多數的即時通訊IM都是後者。

  • HTTP連接與Socket連接的區別

HTTP是短連接,Socket(基於TCP協議的)是長連接。盡管HTTP1.1開始支持持久連接,但仍無法保證始終連接。而Socket連接一旦建立TCP三次握手,除非一方主動斷開,否則連接狀態一直保持。

HTTP連接服務端無法主動發消息。決定二者分別適合應用在什麽場景下。HTTP采用“請求-響應”機制,必須滿足客戶端發送消息在前,服務端回復在後。Socket連接雙方類似peer2peer的關系,一方隨時可以向另一方喊話。

  • 什麽時候該用HTTP,什麽時候該用socket

用HTTP的情況:雙方不需要時刻保持連接在線,比如客戶端資源的獲取、文件上傳等。

用Socket的情況:大部分即時通訊應用(QQ、微信)、聊天室、蘋果APNs等。

五.socket代碼

客戶端代碼

public class MainActivity extends AppCompatActivity {
//IP地址和端口號
public static String IP_ADDRESS = "192.168.1.106";
public static int PORT = 2346;
//三個控件
EditText text = null;
Button connect = null;
TextView info = null;
//handler
Handler handler = null;
Socket soc = null;
DataOutputStream dos = null;
DataInputStream dis = null;
String messageRecv = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    text = (EditText) findViewById(R.id.editText);
    connect = (Button) findViewById(R.id.buttonConnection);

    info = (TextView) findViewById(R.id.info);
    connect.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new ConnectionThread(text.getText().toString()).start();
        }
    });
    handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Bundle b = msg.getData();  //獲取消息中的Bundle對象
            String str = b.getString("data");  //獲取鍵為data的字符串的值
            info.append(str);
        }
    }; }


            //新建一個子線程,實現socket通信
    class ConnectionThread extends Thread {
    String message = null;

    public ConnectionThread(String msg) {
        message = msg;
    }

    @Override
    public void run() {
        if (soc == null) {
            try {
                //Log.d("socket","new socket");
                soc = new Socket(IP_ADDRESS, PORT);
                //獲取socket的輸入輸出流
                dis = new DataInputStream(soc.getInputStream());
                dos = new DataOutputStream(soc.getOutputStream());
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        try {
            dos.writeUTF(message);
            dos.flush();
            messageRecv = dis.readUTF();//如果沒有收到數據,會阻塞
            Message msg = new Message();
            Bundle b = new Bundle();
            b.putString("data", messageRecv);
            msg.setData(b);
            handler.sendMessage(msg);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
}

`
服務器端代碼

public class Server {

ServerSocket serverSocket = null;
public final int port = 2346;

public Server(){

        //輸出服務器的IP地址
        try {
            InetAddress addr = InetAddress.getLocalHost();
            System.out.println("local host:"+addr);
            serverSocket = new ServerSocket(port);
            System.out.println("0k");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
}

public void startService(){

    try {
        Socket socket = null;
        System.out.println("waiting...");
        //等待連接,每建立一個連接,就新建一個線程
        while(true){                
            socket = serverSocket.accept();//等待一個客戶端的連接,在連接之前,此方法是阻塞的
            System.out.println("connect to"+socket.getInetAddress()+":"+socket.getLocalPort());
            new ConnectThread(socket).start();
        }

    } catch (IOException e) {
        // TODO Auto-generated catch block
        System.out.println("IOException");
        e.printStackTrace();
    }
}

//向客戶端發送信息
class ConnectThread extends Thread{
    Socket socket = null;

    public ConnectThread(Socket socket){    
        super();
        this.socket = socket;
    }

    @Override
    public void run(){
        try {
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
            while(true){
                String msgRecv = dis.readUTF();
                System.out.println("msg from client:"+msgRecv);
                dos.writeUTF("received:"+msgRecv);
                dos.flush();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}
public static void main(String[] args) {
    // TODO Auto-generated method stub
    new Server().startService();
}

}

代碼的邏輯

客戶端 :

  1. 初始化控件,並綁定監聽器,編寫按鈕的事件處理代碼。

  2. 在事件處理代碼中開啟子線程,子線程中通過Sockt訪問服務器。
  3. 利用異步消息處理機制,message對象將子線程的數據傳回handle的處理方法更新UI。

服務端

  1. serversocket對象監聽等待,利用循環當有客戶訪問就開啟子線程處理傳消息回客戶端。(模擬器和手機同時通過局域網訪問)
    最後的廢話
  1. 搞計算機必須得有理論指導實踐,否則只能像個沒頭蒼蠅到處亂撞,我的計算機網絡知識真的是一言難盡。

  2. 各大語言的官網是個好東西。豐富的資料和教程簡直讓人沈醉其中不能自拔。雖然比不上網絡小說通俗易懂,但是引人入勝一點也不差。
  3. 不會的東西太多,用谷歌插件等工具列表,先解決主要的,平時有想法也可以記錄。

  4. 用博客整理自己的知識,形成體系。看得再多不如編一遍。


PS. 集成了很多地方的知識點,官網,博主,就不一一記錄原地址了。

2018-05-13 22:15:53 星期日

Android客戶端與本地服務器Socket通信