Socket,你須要知道的事兒

what is socket

socket做爲一種抽象層,應用程序經過它來發送和接收數據,使用socket能夠將應用程序與處於同一網絡中的其餘應用程序進行通訊交互。簡而言之,socket提供了應用程序內部與外界通訊的端口以及爲通訊雙方提供了數據傳輸的通道。javascript

對比

android與服務器通訊主要有兩種方式:Http通訊與Socket通訊。java

差別:android

Http通訊git

  • 請求—響應方式」
  • 請求時創建鏈接通道,當客戶端向服務器發送請求後,服務器端才能向客戶端返回數據

Socket通訊github

  • 雙方創建起鏈接後就能夠直接進行數據的傳輸,在鏈接時可實現信息的主動推送,而不須要每次由客戶端想服務器發送請求
  • 數據丟失率低,使用簡單,易於移植

模型

Socket基本通訊模式以下:服務器

從上面模型能夠看出Socket能夠是基於Tcp協議的Socket通訊或基於Udp協議的Socket通訊,今天筆者就講基於Tcp的Socket通訊。網絡

Tcp通訊模型以下:app

瞭解模型後,優先須要理解Tcp Socket原理,重點來啦~socket

原理

服務器端首先聲明一個ServerSocket對象而且指定端口號,而後調用Serversocket的accept()方法接收客戶端的數據。accept()方法在沒有數據進行接收的處於堵塞狀態。(Socketsocket=serversocket.accept()),一旦接收到數據,經過inputstream讀取接收的數據。 客戶端建立一個Socket對象,指定服務器端的ip地址和端口號(Socket socket=new Socket("172.17.30.12",9999);),經過inputstream讀取數據,獲取服務器發出的數據(OutputStream outputstream = socket.getOutputStream()),最後將要發送的數據寫入到outputstream便可進行TCP協議的socket數據傳輸。tcp

實踐

原理懂了,則需實踐,毛主席說過「實踐是檢驗真理的惟一標準」。 代碼走起來~~~

客戶端:

package cn.jianke.socket.tcp;

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

/** * @className: TcpSocketClient * @classDescription: tcp套接字客戶端 * @author: leibing * @createTime: 2016/10/06 */
public class TcpSocketClient {
    // 服務端地址
    private String serverIp = "172.17.30.12";
    // 服務端端口號
    private int serverPort = 9999;
    // 套接字
    private Socket mSocket = null;
    // 緩衝區讀取
    private BufferedReader in = null;
    // 字符打印流
    private PrintWriter out = null;
    // tcp套接字監聽
    private TcpSocketListener mTcpSocketListener;
    // 內容
    private String content = "";

    /** * 構造函數 * @author leibing * @createTime 2016/10/06 * @lastModify 2016/10/06 * @param mTcpSocketListener tcp套接字監聽 * @return */
    public TcpSocketClient(TcpSocketListener mTcpSocketListener){
        this.mTcpSocketListener = mTcpSocketListener;
    }

    /** * 構造函數 * @author leibing * @createTime 2016/10/06 * @lastModify 2016/10/06 * @param serverIp = 服務端地址 * @param serverPort 服務端口號 * @param mTcpSocketListener tcp套接字監聽 * @return */
    public TcpSocketClient(String serverIp, int serverPort , TcpSocketListener mTcpSocketListener){
        this.serverIp  = serverIp;
        this.serverPort = serverPort;
        this.mTcpSocketListener = mTcpSocketListener;
    }

    /** * 啓動tcp套接字鏈接 * @author leibing * @createTime 2016/10/06 * @lastModify 2016/10/06 * @param * @return */
    public void startTcpSocketConnect(){
        // 開啓一個線程啓動tcp socket
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    mSocket = new Socket(serverIp, serverPort);
                    in = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
                    out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                            mSocket.getOutputStream())), true);
                    while (true) {
                        if (mSocket.isConnected()) {
                            if (!mSocket.isInputShutdown()) {
                                if ((content = in.readLine()) != null) {
                                    content += "\n";
                                    if (mTcpSocketListener != null)
                                        mTcpSocketListener.callBackContent(content);
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /** * 經過tcp套接字發送消息 * @author leibing * @createTime 2016/10/06 * @lastModify 2016/10/06 * @param * @return */
    public void sendMessageByTcpSocket(String msg){
        if (mSocket != null && mSocket.isConnected()){
            if (!mSocket.isOutputShutdown() && out != null){
                out.println(msg);
                if (mTcpSocketListener != null)
                    mTcpSocketListener.clearInputContent();
            }
        }
    }

    /** * @interfaceName: * @interfaceDescription: tcp套接字監聽 * @author: leibing * @createTime: 2016/10/06 */
    public interface TcpSocketListener{
        // 回調內容
        void callBackContent(String content);
        // 清除輸入框內容
        void clearInputContent();
    }
}複製代碼

封裝一個TcpSocket客戶端類,寫構造函數,設置服務端ip地址、端口號並設置監聽(用於回調從Socket服務端接收的數據),寫啓動tcp套接字鏈接方法,其中BufferedReader in用於接收服務端數據,PrintWriter out用於向服務端發送數據。

package cn.jianke.socket.module;

import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import cn.jianke.socket.R;
import cn.jianke.socket.tcp.TcpSocketClient;

/** * @className:MainActivity * @classDescription: tcp套接字客戶端頁面 * @author: leibing * @createTime: 2016/10/06 */
public class MainActivity extends AppCompatActivity {
    // 服務端地址
    private final static String serverIp = "172.17.30.12";
    // 服務端口號
    private final static int serverPort = 9999;
    // 控件
    private TextView showTv;
    private EditText contentEdt;
    private Button sendBtn;
    // tcp套接字客戶端
    private TcpSocketClient mTcpSocketClient;
    // 自定義Handler,用於更新Ui
    private Handler mHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // findView
        showTv = (TextView) findViewById(R.id.tv_show);
        contentEdt = (EditText) findViewById(R.id.edt_content);
        // 初始化tcp套接字客戶端
        mTcpSocketClient = new TcpSocketClient(serverIp, serverPort,
                new TcpSocketClient.TcpSocketListener() {
            @Override
            public void callBackContent(final String content) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (showTv != null)
                            showTv.setText(showTv.getText().toString() + content);
                    }
                });
            }

                    @Override
                    public void clearInputContent() {
                        if (contentEdt != null)
                            contentEdt.setText("");
                    }
                });
        // 啓動tcp套接字鏈接
        mTcpSocketClient.startTcpSocketConnect();
        // onClick
        findViewById(R.id.btn_send).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String msg = contentEdt.getText().toString().trim();
                mTcpSocketClient.sendMessageByTcpSocket(msg);
            }
        });
    }

    @Override
    protected void onDestroy() {
        // 斷開tcp連接
        if (mTcpSocketClient != null)
            mTcpSocketClient.sendMessageByTcpSocket("exit");
        super.onDestroy();
    }
}複製代碼

在頁面初始TcpSocketClient類(設置服務端ip地址、端口以及設置監聽),並啓動啓動tcp套接字鏈接。

服務端:

package cn.jianke.socket.tcp;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/** * @className: TcpSocketServer * @classDescription: tcp套接字服務端 * @author: leibing * @createTime: 2016/10/06 */
public class TcpSocketServer {
    // 端口號
    private final static int serverPort = 9999;
    // tcp套接字列表
    private List<Socket> mList = new ArrayList<Socket>();
    // 套接字服務
    private ServerSocket server = null;
    // 線程池
    private ExecutorService mExecutorService = null;

    /** * 主函數入口 * @author leibing * @createTime 2016/10/06 * @lastModify 2016/10/06 * @param args * @return */
    public static void main(String[] args) {
        // 啓動tcp套接字服務
        new TcpSocketServer();
    }

    /** * 啓動tcp套接字服務 * @author leibing * @createTime 2016/10/06 * @lastModify 2016/10/06 * @param * @return */
    public TcpSocketServer() {
        try {
            server = new ServerSocket(serverPort);
            System.out.print("server start ...");
            Socket client = null;
            //create a thread pool
            mExecutorService = Executors.newCachedThreadPool();
            while(true) {
                client = server.accept();
                mList.add(client);
                //start a new thread to handle the connection
                mExecutorService.execute(new TcpSocketService(client));
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    /** * @className: TcpSocketService * @classDescription: tcp套接字服務 * @author: leibing * @createTime: 2016/10/06 */
    class TcpSocketService implements Runnable {
            // 套接字
            private Socket socket;
            // 緩衝區讀取
            private BufferedReader in = null;
            // 消息
            private String msg = "";

            /** * 構造函數 * @author leibing * @createTime 2016/10/06 * @lastModify 2016/10/06 * @param socket 套接字 * @return */
            public TcpSocketService(Socket socket) {
                this.socket = socket;
                try {
                    in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    msg = "tips: user" +this.socket.getInetAddress() + " come";  
                    this.sendmsg();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    while(true) {
                        if((msg = in.readLine())!= null) {
                            if(msg.equals("exit")) {
                                mList.remove(socket);
                                in.close();
                                msg = "tips: user" +this.socket.getInetAddress() + " exit";
                                socket.close();
                                this.sendmsg();
                                break;
                            } else {
                                msg = socket.getInetAddress() + ":" + msg;
                                this.sendmsg();
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        /** * 發送消息 * @author leibing * @createTime 2016/10/06 * @lastModify 2016/10/06 * @param * @return */
        public void sendmsg() {
               System.out.println(msg);
               int num =mList.size();
               for (int index = 0; index < num; index ++) {
                   Socket mSocket = mList.get(index);
                   PrintWriter pout = null;
                   try {
                       pout = new PrintWriter(new BufferedWriter(
                               new OutputStreamWriter(mSocket.getOutputStream())),true);
                       pout.println(msg);
                   }catch (IOException e) {
                       e.printStackTrace();
                   }
               }
           }
        }    
}複製代碼

初始化ServerSocket指定端口,將tcp套接字服務放入線程池與客戶端進行交互,BufferedReader in用於接收客戶端發來的數據,PrintWriter pout 用於向客戶端發送數據。

效果圖

服務端:

server start ...tips: user/172.17.30.15 come
/172.17.30.15:夠麼
/172.17.30.15:不夠
/172.17.30.15:哈根
/172.17.30.15:裏咯哦圖
/172.17.30.15:諾魔圖
/172.17.30.15:這是一個socket
/172.17.30.15:hgdh複製代碼

客戶端:

Demo地址:Socket

關於做者

相關文章
相關標籤/搜索