【Android Socket专题】: TCP通信服务器端app的demo的实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shankezh/article/details/51555455

UDP Client客户端  http://blog.csdn.net/shankezh/article/details/50731287

UDP Server服务器 http://blog.csdn.net/shankezh/article/details/51452811

TCP Client客户端  http://blog.csdn.net/shankezh/article/details/70763579

TCP Server服务器 http://blog.csdn.net/shankezh/article/details/51555455

----这篇实现以下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包地址:

TCP(客户端和服务器demo)

猜你喜欢

转载自blog.csdn.net/shankezh/article/details/51555455