Socket,你需要知道的事儿

15,813 阅读6分钟

what is socket

socket作为一种抽象层,应用程序通过它来发送和接收数据,使用socket可以将应用程序与处于同一网络中的其他应用程序进行通信交互。简而言之,socket提供了应用程序内部与外界通信的端口以及为通信双方提供了数据传输的通道。

对比

android与服务器通信主要有两种方式:Http通信与Socket通信。

差异:

Http通信

  • 请求—响应方式”
  • 请求时建立连接通道,当客户端向服务器发送请求后,服务器端才能向客户端返回数据

Socket通信

  • 双方建立起连接后就可以直接进行数据的传输,在连接时可实现信息的主动推送,而不需要每次由客户端想服务器发送请求
  • 数据丢失率低,使用简单,易于移植

模型

Socket基本通信模式如下:

从上面模型可以看出Socket可以是基于Tcp协议的Socket通信或基于Udp协议的Socket通信,今天笔者就讲基于Tcp的Socket通信。

Tcp通信模型如下:

了解模型后,优先需要理解Tcp 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数据传输。

实践

原理懂了,则需实践,毛主席说过“实践是检验真理的唯一标准”。 代码走起来~~~

客户端:

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

关于作者