1. 程式人生 > >【Android Socket專題】: TCP通訊伺服器端app的demo的實現

【Android Socket專題】: TCP通訊伺服器端app的demo的實現

----這篇實現以下TCP 伺服器,另外由於實在不是想在重複工作了,所以關於tcp多連線伺服器程式碼直接提供思路放在這邊了,單一使用請自行刪除和移位相關程式碼即可,原理是一樣的,實現方法也有很多種,個人這裡只實現其中一種。

首先規劃一下頁面:


附上對應XML程式碼:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="5"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:layout_weight="1">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:orientation="horizontal">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="本機ip:"/>
            <TextView
                android:layout_width="0dp"
                android:layout_weight="2"
                android:layout_height="match_parent"
                android:gravity="center"
                android:id="@+id/txt_Server_Ip"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="port"/>

            <EditText
                android:id="@+id/edit_Server_Port"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:hint="1234"
                android:inputType="number" />
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="horizontal"
            android:layout_weight="1">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="ID(英文或數字):"/>
            <EditText
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:id="@+id/edit_Server_ID"/>
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="隨機生成"
                android:id="@+id/btn_tcpServerRandomID"/>
        </LinearLayout>

    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="開啟"
            android:id="@+id/btn_tcpServerConn"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="關閉"
            android:id="@+id/btn_tcpServerClose"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="清除接收區"
            android:id="@+id/btn_tcpCleanServerRecv"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="清除傳送區"
            android:id="@+id/btn_tcpCleanServerSend"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:layout_weight="2">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:gravity="bottom"
            android:text="接收區:" />
        <TextView
            android:layout_width="match_parent"
            android:layout_weight="5"
            android:layout_height="0dp"
            android:id="@+id/txt_ServerRcv"
            android:background="@android:color/holo_blue_light"
            />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:layout_weight="2">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="傳送區:" />

        <TextView
            android:id="@+id/txt_ServerSend"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="7"
            android:background="@android:color/holo_purple" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="horizontal"
            android:layout_weight="2">
            <EditText
                android:layout_width="0dp"
                android:layout_weight="5"
                android:id="@+id/edit_Send"
                android:layout_height="match_parent" />
            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:layout_gravity="right"
                android:id="@+id/btn_tcpServerSend"
                android:text="傳送"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

介面規劃完成,其中關於隨機ID這塊我沒有寫程式碼,有空再補了;

實現TCP 伺服器端的關鍵類在於以下幾個類:

1、ServerSocket;

2、Socket;

3、InputSteam;

4、OutputSteam;

5、PrintWriter;

不懂得希望自己搜尋查一下,我這就不費話了;

TCP關於伺服器建立的流程設計如下:

1、初始化ServerSocket的埠,阻塞監聽;

2、監聽到連線後,開闢執行緒,建立對應通訊socket流執行緒,每一個監聽到一個物件對應一個新的執行緒;

3、socket執行緒內部阻塞監聽接收資料,收到後傳送給主介面顯示;

4、結束迴圈,對應關閉socket物件;

5、結束監聽,關閉ServerSocket物件(同時要注意關閉還在執行的Socket執行緒及物件);

關於此部分有一些提醒需要說明:

1、serverSocket最好設定超時,因為最終結束時候,還會被阻塞一次,當然也可以利用api強制結束監聽異常;

2、Socket最好設定超時,理由同上;

3、關閉ServerSocket時,記得關閉所有的Socket物件,它們還在執行中;

TCP關於伺服器傳送訊息流程:

1、呼叫相對應的Socket執行緒中的Send方法即可,利用PrintWriter實現;

具體實現見程式碼:

package jason.tcpdemo.coms;

import android.content.Intent;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

import jason.tcpdemo.funcs.FuncTcpServer;

/**
 * Created by Jason Zhu on 2017-04-24.
 * Email: 
[email protected]
*/ public class TcpServer implements Runnable{ private String TAG = "TcpServer"; private int port = 1234; private boolean isListen = true; //執行緒監聽標誌位 public ArrayList<ServerSocketThread> SST = new ArrayList<ServerSocketThread>(); public TcpServer(int port){ this.port = port; } //更改監聽標誌位 public void setIsListen(boolean b){ isListen = b; } public void closeSelf(){ isListen = false; for (ServerSocketThread s : SST){ s.isRun = false; } SST.clear(); } private Socket getSocket(ServerSocket serverSocket){ try { return serverSocket.accept(); } catch (IOException e) { e.printStackTrace(); Log.i(TAG, "run: 監聽超時"); return null; } } @Override public void run() { try { ServerSocket serverSocket = new ServerSocket(port); serverSocket.setSoTimeout(5000); while (isListen){ Log.i(TAG, "run: 開始監聽..."); Socket socket = getSocket(serverSocket); if (socket != null){ new ServerSocketThread(socket); } } serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } public class ServerSocketThread extends Thread{ Socket socket = null; private PrintWriter pw; private InputStream is = null; private OutputStream os = null; private String ip = null; private boolean isRun = true; ServerSocketThread(Socket socket){ this.socket = socket; ip = socket.getInetAddress().toString(); Log.i(TAG, "ServerSocketThread:檢測到新的客戶端聯入,ip:" + ip); try { socket.setSoTimeout(5000); os = socket.getOutputStream(); is = socket.getInputStream(); pw = new PrintWriter(os,true); start(); } catch (IOException e) { e.printStackTrace(); } } public void send(String msg){ pw.println(msg); pw.flush(); //強制送出資料 } @Override public void run() { byte buff[] = new byte[4096]; String rcvMsg; int rcvLen; SST.add(this); while (isRun && !socket.isClosed() && !socket.isInputShutdown()){ try { if ((rcvLen = is.read(buff)) != -1 ){ rcvMsg = new String(buff,0,rcvLen); Log.i(TAG, "run:收到訊息: " + rcvMsg); Intent intent =new Intent(); intent.setAction("tcpServerReceiver"); intent.putExtra("tcpServerReceiver",rcvMsg); FuncTcpServer.context.sendBroadcast(intent);//將訊息傳送給主介面 if (rcvMsg.equals("QuitServer")){ isRun = false; } } } catch (IOException e) { e.printStackTrace(); } } try { socket.close(); SST.clear(); Log.i(TAG, "run: 斷開連線"); } catch (IOException e) { e.printStackTrace(); } } } }

核心程式碼完成。

主介面程式碼(幾個類和UDP介紹介面程式碼時是一樣的):

package jason.tcpdemo.funcs;


import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;


import java.lang.ref.WeakReference;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


import jason.tcpdemo.R;
import jason.tcpdemo.coms.TcpServer;


/**
 * Created by Jason Zhu on 2017-04-24.
 * Email: [email protected]
 */


public class FuncTcpServer extends Activity {
    private Button btnStartServer,btnCloseServer, btnCleanServerSend, btnCleanServerRcv,btnServerSend,btnServerRandom;
    private TextView txtRcv,txtSend,txtServerIp;
    private EditText editServerSend,editServerRandom, editServerPort;
    private static TcpServer tcpServer = null;
    private MyBtnClicker myBtnClicker = new MyBtnClicker();
    private final MyHandler myHandler = new MyHandler(this);
    private MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
    @SuppressLint("StaticFieldLeak")
    public static Context context;
    ExecutorService exec = Executors.newCachedThreadPool();






    private class MyHandler extends android.os.Handler{
        private final WeakReference<FuncTcpServer> mActivity;
        MyHandler(FuncTcpServer activity){
            mActivity = new WeakReference<FuncTcpServer>(activity);
        }


        @Override
        public void handleMessage(Message msg) {
            FuncTcpServer activity = mActivity.get();
            if (activity!= null){
                switch (msg.what){
                    case 1:
                        txtRcv.append(msg.obj.toString());
                        break;
                    case 2:
                        txtSend.append(msg.obj.toString());
                        break;
                }
            }
        }
    }


    private class MyBroadcastReceiver extends BroadcastReceiver{


        @Override
        public void onReceive(Context context, Intent intent) {
            String mAction = intent.getAction();
            switch (mAction){
                case "tcpServerReceiver":
                    String msg = intent.getStringExtra("tcpServerReceiver");
                    Message message = Message.obtain();
                    message.what = 1;
                    message.obj = msg;
                    myHandler.sendMessage(message);
                    break;
            }
        }
    }


    private void bindReceiver(){
        IntentFilter intentFilter = new IntentFilter("tcpServerReceiver");
        registerReceiver(myBroadcastReceiver,intentFilter);
    }


    private class MyBtnClicker implements View.OnClickListener{


        @Override
        public void onClick(View view) {
            switch (view.getId()){
                case R.id.btn_tcpServerConn:
                    Log.i("A", "onClick: 開始");
                    btnStartServer.setEnabled(false);
                    btnCloseServer.setEnabled(true);
                    btnServerSend.setEnabled(true);
                    tcpServer = new TcpServer(getHost(editServerPort.getText().toString()));
                    exec.execute(tcpServer);
                    break;
                case R.id.btn_tcpServerClose:
                    tcpServer.closeSelf();
                    btnStartServer.setEnabled(true);
                    btnCloseServer.setEnabled(false);
                    btnServerSend.setEnabled(false);
                    break;
                case R.id.btn_tcpCleanServerRecv:
                    txtRcv.setText("");
                    break;
                case R.id.btn_tcpCleanServerSend:
                    txtSend.setText("");
                    break;
                case R.id.btn_tcpServerRandomID:
                    break;
                case R.id.btn_tcpServerSend:
                    Message message = Message.obtain();
                    message.what = 2;
                    message.obj = editServerSend.getText().toString();
                    myHandler.sendMessage(message);
                    exec.execute(new Runnable() {
                        @Override
                        public void run() {
                            tcpServer.SST.get(0).send(editServerSend.getText().toString());
                        }
                    });
                    break;
            }
        }
    }


    private int getHost(String msg){
        if (msg.equals("")){
            msg = "1234";
        }
        return Integer.parseInt(msg);
    }




    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tcp_server);
        context = this;
        bindID();
        bindListener();
        bindReceiver();
        ini();
    }


    private void ini(){
        btnCloseServer.setEnabled(false);
        btnServerSend.setEnabled(false);
        txtServerIp.setText(getHostIP());
    }


    private void bindListener() {
        btnStartServer.setOnClickListener(myBtnClicker);
        btnCloseServer.setOnClickListener(myBtnClicker);
        btnCleanServerRcv.setOnClickListener(myBtnClicker);
        btnCleanServerSend.setOnClickListener(myBtnClicker);
        btnServerRandom.setOnClickListener(myBtnClicker);
        btnServerSend.setOnClickListener(myBtnClicker);
    }


    private void bindID() {
        btnStartServer = (Button) findViewById(R.id.btn_tcpServerConn);
        btnCloseServer = (Button) findViewById(R.id.btn_tcpServerClose);
        btnCleanServerRcv = (Button) findViewById(R.id.btn_tcpCleanServerRecv);
        btnCleanServerSend = (Button) findViewById(R.id.btn_tcpCleanServerSend);
        btnServerRandom = (Button) findViewById(R.id.btn_tcpServerRandomID);
        btnServerSend = (Button) findViewById(R.id.btn_tcpServerSend);
        txtRcv = (TextView) findViewById(R.id.txt_ServerRcv);
        txtSend = (TextView) findViewById(R.id.txt_ServerSend);
        txtServerIp = (TextView) findViewById(R.id.txt_Server_Ip);
        editServerRandom = (EditText) findViewById(R.id.edit_Server_ID);
        editServerSend = (EditText) findViewById(R.id.edit_tcpClientSend);
        editServerPort = (EditText)findViewById(R.id.edit_Server_Port);
    }


    /**
     * 獲取ip地址
     * @return
     */
    public String getHostIP() {


        String hostIp = null;
        try {
            Enumeration nis = NetworkInterface.getNetworkInterfaces();
            InetAddress ia = null;
            while (nis.hasMoreElements()) {
                NetworkInterface ni = (NetworkInterface) nis.nextElement();
                Enumeration<InetAddress> ias = ni.getInetAddresses();
                while (ias.hasMoreElements()) {
                    ia = ias.nextElement();
                    if (ia instanceof Inet6Address) {
                        continue;// skip ipv6
                    }
                    String ip = ia.getHostAddress();
                    if (!"127.0.0.1".equals(ip)) {
                        hostIp = ia.getHostAddress();
                        break;
                    }
                }
            }
        } catch (SocketException e) {
            Log.i("FuncTcpServer", "SocketException");
            e.printStackTrace();
        }
        return hostIp;


    }








}

再次強調,許可權:
<uses-permission android:name="android.permission.INTERNET"/>  

結束,看效果圖:


和寫的TCP Client通訊效果如下:


開發環境:Android Studio

demo包地址:

相關推薦

Android Socket專題 TCP通訊伺服器app的demo的實現

----這篇實現以下TCP 伺服器,另外由於實在不是想在重複工作了,所以關於tcp多連線伺服器程式碼直接提供思路放在這邊了,單一使用請自行刪除和移位相關程式碼即可,原理是一樣的,實現方法也有很多種,個人這裡只實現其中一種。 首先規劃一下頁面: 附上

Android Socket專題 TCP通訊客戶app的demo的實現

------- Android TCP 客戶端實現,規劃介面如下: 對應XML程式碼如下: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" a

Android Socket專題UDP通訊客戶app的demo的實現

Android Socket 專題: 關於UDP通訊其實可以不用多做累述,多數像我一樣的朋友在此基礎上也只是為了應用,需要了解下該瞭解的就可以了,具體的想要對這個協議研究深入的,可以自己努力!我這兒只做Android客戶端的應用實現,注意是客戶端,不是伺服器,

Wireshark抓包TCP的狀態型別

承接上篇:https://blog.csdn.net/hxcaifly/article/details/85339299 1. 前言 在分析wireshark抓包時,封包詳情資訊裡展現出了TCP的狀態型別。除了我們比較熟悉的SYN, ACK的三次握手時見到的狀態之外。突

Android---專案中複製到剪貼簿功能的實現

專案中需要複製銀行卡賬號到剪貼簿,於是需要用到此功能。 button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Clipbo

Android 進階CSDN新聞類客戶-練手專案

無圖無真相: 宣告: 本專案資料全部來自CSDN官網,純練手之作,個人未從中獲取任何利益,資料的獲取與共享可能會侵犯到CSDN的權益,若被告知需停止共享與使用,本人會立即刪除整個專案。

Android 進階12程序通訊Socket (順便回顧 TCP UDP)

不要害怕困難,這是你進步的機會! 讀完本文你將瞭解: 前面幾篇文章我們介紹了 AIDL 、Binder、Messenger 以及 ContentProvider 實現程序通訊的方式,這篇文章將介紹“使用 Socket 進行跨程序通訊”。 在介紹

Android應用開發技術基礎構建命令行下的Android應用開發

star andro all class文件 abs 2.3 pil data 資源 作者:郭孝星 微博:郭孝星的新浪微博 郵箱:[email protected]/* */ 博客:http://blog.csdn.net/allenw

背包專題C - The trouble of Xiaoqian hdu3591混合背包多重背包+完全背包

back 無法 name spa int receive with out man In the country of ALPC , Xiaoqian is a very famous mathematician. She is immersed in calculate,

Java TCP/IP Socket程式設計----NIO----TCP通道

NIO介紹 基本Java套接字對於小規模系統可以很好執行,涉及同時有上千個客戶端,就會出現問題,其中一客戶一執行緒的方式線上程的建立,維護和切換需要系統開銷較大,而使用執行緒池的方式雖然節省了一定的系統開銷,但是對於連線生存期比較長的協議,執行緒池的大小限制了系統可以同時服務的客戶端總數。隨著執

android學習筆記activity間的通訊案例之高德地圖實現天氣查詢

【概述】app實現天氣查詢是再正常不過的功能了,又因為往往不止一個activity去獲取資料,那就想到封裝一個類,需要時去呼叫獲取即可。 【注】因為看文件還有點懵,故將自己抓腦寫的程式碼記錄下,以便查詢 【思路】activity傳送請求--獲取地址--根據地址獲取天氣

Python學習第20篇互斥鎖以及程序之間的三種通訊方式(IPC)以及生產者個消費者模型 python併發程式設計之多程序1-----------互斥鎖與程序間的通訊

python併發程式設計之多程序1-----------互斥鎖與程序間的通訊 一、互斥鎖 程序之間資料隔離,但是共享一套檔案系統,因而可以通過檔案來實現程序直接的通訊,

網路程式設計基礎林老師版簡單的 套接字通訊(一)

一、服務端程式碼  import socket #1、買手機 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # print(phone) #2、繫結手機卡 phone.bind(('127.0.0.1',8081)) #0-

Linux網路程式設計TCP伺服器多程序和多執行緒(http訪問)版本

為了讓伺服器同時接受多個客戶端訪問,所以需要多程序或者多執行緒 多程序版本: #include<unistd.h> #include<stdio.h> #include<stdlib.h> #include<sys/types.h

Android 進階Android自定義系列邊緣凹凸的卡劵效果

所謂前人栽樹,後人乘涼,在此感謝博主的貢獻。 原文:邊緣凹凸的卡劵效果 先上效果圖: 我實現的效果和原博主實現的效果是不一樣的,我是左右邊緣凹凸,而博主是上下邊緣凹凸。其實理解了原理,哪個邊緣實現這個效果都是可以的。 實現原理: 直接

架構拾集 Android 移動應用架構設計

在這一個多月裡,我工作在一個採用外掛化的原生 Android 應用專案上。隨著新技術的引入,及編

Android 併發程式設計執行緒間通訊的三種基本方式,android執行緒

1. 使用管道流Pipes “管道”是java.io包的一部分。它是Java的特性,而不是Android特有的。一條“管道”為兩個執行緒建立一個單向的通道。生產者負責寫資料,消費者負責讀取資料。 下面是一個使用管道流進行通訊的例子。 public class Pip

Android 進階淘寶頭條向上滾動廣告條ViewFlipper

所謂前人栽樹,後人乘涼,在此感謝博主的貢獻。 參考博文: 仿淘寶首頁的淘寶頭條View垂直滾動 歡迎關注我的微信公眾號 不只是原創技術文章,更多的是對生活的思考總結 我在博主的基礎上做了如下工作: 修復了滾動條第二條點選事件無法觸發這個

Android開發-5界面裝修,五大布局你選誰

比例 技術分享 article 嵌套 content java lin layout mark 前言:假設要開一家店,門店裝修是非常重要的事情。有錢都請專門的建築設計公司來設計裝修,沒錢的僅僅能自己瞎折騰。好不好看全憑自己的感覺。像Android開發。在移動端大家看到的

進擊的Python第九章paramiko模塊、線程與進程、各種線程鎖、queue隊列、生產者消費者模型

password locking form maxsize 廁所 sorted [0 hostname nbsp 一、paramiko模塊 他是什麽東西?   paramiko模塊是用python語言寫的一個模塊,遵循SSH2協議,支持以加密和認證的方式,進行遠程服務器的連