Java利用Socket進行通信

上學期學校開設了創新項目的課程,我選擇了《基於手機定位的Android考勤系統》,在整個開發過程中,總的來說,真的是學到了很多,尤其是客戶端和服務器端通信這一塊。對Socket通信,多線程等有了一定的認識,所以在此記錄一下,一起學習,我的認識還是很淺的,如有錯誤,歡迎指出。

服務器端

(我這裏是把自己的電腦當做服務器,也可以申請雲服務器)

主要步驟:
  • 1、在服務器端,用一個端口來實例化一個ServerSocket對象。當服務器端開始運行時,就可以用這個端口時刻監聽從客戶端發來的連接請求。
  • 2、調用ServerSocket的accept方法,接收從端口上發送來的連接請求,返回客戶端的socket對象,用來進行讀寫IO的操作。
  • 3、利用客戶端socket的isConnected()方法,來獲取客戶端連接的狀態,連接成功可以做相應的操作,比如支持多用戶併發訪問的時候,可以將客戶端的socket添加到線程池中。(isConnected()方法獲取的並不是實時的客戶端的連接狀態,可以通過心跳包機制來獲取實時的連接狀態)。
  • 4、通訊完成後,關閉打開的流和Socket對象。
服務器代碼(Server.java)
public class Server {
    private ExecutorService executorService;// 線程池
    private ServerSocket serverSocket = null;
    private Socket socket = null;
    private boolean isStarted = true;//判斷服務是否啓動

    public Server() {
        try {
            // 創建線程池,池中具有(cpu個數*50)條線程
            executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
                    .availableProcessors() * 50);
            //實例化ServerSocket對象 注意客戶端端口號需要和服務器端端口號保持一致 
            serverSocket = new ServerSocket(8090);
            } catch (IOException e) {
            e.printStackTrace();
            //quit();
        }
    }

    public void start() {

        try {
            while (isStarted) {
                System.out.println("等待連接");
                socket = serverSocket.accept();
                String ip = socket.getInetAddress().toString();
                System.out.println("客戶端已連接");
                // 爲支持多用戶併發訪問,採用線程池管理每一個用戶的連接請求
                if (socket.isConnected())
                {   
                    //new Thread(new HeartBeatMonitor(socket)).start();
                    executorService.execute(new SocketTask(socket));// 添加到線程池
                }
            }
            if (socket != null)
                socket.close();
            if (serverSocket != null)
                serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();

        }
    }
    /**
     * 退出
     */
    public void quit() {
        try {
            this.isStarted = false;
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Server().start();//啓動服務
       //   new Server().quit();
    }
    //單個Socket線程
    final class SocketTask implements Runnable {
        private Socket socket = null;
        private ServerThread  in;
        private OutputThread out;
        private OutputThreadMap outputThreadMap;


        public SocketTask(Socket socket) {
            this.socket = socket;
            outputThreadMap = OutputThreadMap.getInstance();
            //socketThreadMap = SocketThreadMap.getInstance();
        }

        @Override
        public void run() {
            out = new OutputThread(socket, outputThreadMap);// 先實例化寫消息線程,(把對應用戶的寫線程存入map緩存器中)
            in = new ServerThread(socket, out, outputThreadMap);// 再實例化讀消息線程
            out.setStart(true);
            in.setStart(true);
            in.start();
            out.start();
        }
    }
}

服務器讀消息線程代碼

private Socket socket;
    private Gson gson;
    private OutputThread out;// 傳遞進來的寫消息線程,因爲我們要給用戶回覆消息啊
    private OutputThreadMap map;// 寫消息線程緩存器
    private SocketThreadMap socketThreadMap;// 寫消息線程緩存器
    private DataInputStream inputStream;// 對象輸入流
    private InputStreamReader iReader;
    private boolean isStart = true;// 是否循環讀消息
    public long lastReceiveHeart;//上次接收心跳包時間

    public ServerThread(Socket socket, OutputThread out, OutputThreadMap map) {
        // TODO Auto-generated constructor stub
        this.socket = socket;
        this.out = out;
        this.map = map;
        try {
            inputStream = new DataInputStream(socket.getInputStream());// 實例化對象輸入流
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void setStart(boolean isStart) {// 提供接口給外部關閉讀消息線程
        this.isStart = isStart;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {
            while (isStart) {
                iReader = new InputStreamReader(inputStream, "UTF-8");
                char[] buffer = new char[1024];
                int count = 0;
                String phone = null;
                StringBuffer sBuilder = new StringBuffer();
                while ((count = iReader.read(buffer, 0, buffer.length)) > -1) {
                    sBuilder.append(buffer, 0, count);
                    if (count < 1024 && count != 0) {
                        break;
                    }
                }

                gson = new GsonBuilder().setPrettyPrinting() // 格式化輸出(序列化)
                        .setDateFormat("yyyy-MM-dd HH:mm:ss") // 日期格式化輸出
                        .create();
                JsonReader jsonReader = new JsonReader(new StringReader(sBuilder.toString()));// 其中jsonContext爲String類型的Json數據
                jsonReader.setLenient(true);
                TranObject readObject = gson.fromJson(jsonReader, TranObject.class);
                if (readObject != null )
                {
                    lastReceiveHeart = System.currentTimeMillis();
                    phone = readObject.getFromUser();//手機號作爲用戶的標識
                }
                //如果距離接收心跳包的時間超過5分鐘 說明用戶掉線
                if(System.currentTimeMillis() - lastReceiveHeart > 300000) {
                    try {
                        if (phone != null) {//更新用戶狀態
                            new UserDao().updateStatus(0, phone);
                        }

                        socket.close();
                    } catch (IOException e) {
                        // TODO: handle exception
                        e.printStackTrace();
                    }
                }
                TranObject serverResult = execute(readObject);
                pushMessage(readObject);// 執行推送的消息
                if (serverResult != null) {
                    out.setMessage(serverResult);
                }
            }
            if (iReader != null) {
                iReader.close();
            }
            if (inputStream != null) {
                inputStream.close();
            }
            if (socket != null) {
                socket.close();
            }
        } catch (IOException e) {
            // TODO: handle exception
        }

    }
    // 處理客戶端發送過來的消息
    private TranObject execute(TranObject readObject) {

            //...省略代碼...
    }
    /**
     * 處理需要互相推送的消息
     * 
     * @param readObject
     */
    private void pushMessage(TranObject readObject) {
          // ...省略代碼...
    }

服務器寫消息線程

public class OutputThread extends Thread{
    @SuppressWarnings("unused")
    private OutputThreadMap map;
    private SocketThreadMap socketThreadMap;
    //private ObjectOutputStream oos;
    private OutputStreamWriter oStreamWriter;
    private DataOutputStream dataOutputStream;
    private TranObject object;
    private boolean isStart = true;// 循環標誌位
    private Socket socket;

    public OutputThread(Socket socket, OutputThreadMap map) {
        this.socket = socket;
        this.map = map;
        try {           
            dataOutputStream = new DataOutputStream(socket.getOutputStream());// 在構造器裏面實例化對象輸出流
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void setStart(boolean isStart) {
        this.isStart = isStart;
    }

    // 調用寫消息線程,設置了消息之後,喚醒run方法,可以節約資源
    public void setMessage(TranObject object) {
        this.object = object;
        synchronized (this) {
            notify();
        }
    }

    @Override
    public void run() {
        try {

            while (isStart) {
                // 沒有消息寫出的時候,線程等待
                synchronized (this) {
                    wait();
                }
                if (object != null) {
                     Gson gson = new GsonBuilder()
                                 .setPrettyPrinting()  //格式化輸出(序列化)
                                 .setDateFormat("yyyy-MM-dd HH:mm:ss") //日期格式化輸出
                                 .create();
                       oStreamWriter = new OutputStreamWriter(dataOutputStream, "UTF-8");
                       String outputString = gson.toJson(object);
                       //dataOutputStream.writeInt(outputString.length());
                       //dataOutputStream.write(outputString.getBytes());
                       //dataOutputStream.flush();
                       StringBuffer sBuilder = new StringBuffer();
                        sBuilder.append(outputString);
                       oStreamWriter.write(sBuilder.toString());
                       oStreamWriter.flush();
                       if(object != null && object.getType()!=TranObjectType.HEART_TEST)
                       {
                           System.out.println(outputString);
                       }

                }
            }
            if(oStreamWriter != null)
            {
                oStreamWriter.close();
            }
            if (dataOutputStream != null)// 循環結束後,關閉流,釋放資源
                dataOutputStream.close();
            if (socket != null)
                socket.close();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章