1. 程式人生 > >java socket通訊I/O阻塞>多執行緒實現非阻塞通訊

java socket通訊I/O阻塞>多執行緒實現非阻塞通訊

簡單的java socket通訊,多個客戶端同時連線,功能可在此基礎上進行擴充套件。效果如圖:

客戶機發送,服務端接收!

server:

package com.lb.LB_Socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class myServer {
    private
ServerSocket server; private Socket socket; private BufferedReader brin; private PrintWriter pw; private BufferedReader in ; private Boolean bool=true; public static void main(String args[]){ myServer ms = new myServer(); try { ms.onServer(); } catch
(IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void onServer() throws IOException{ try{ try{ server = new ServerSocket(4440); System.out.println("伺服器已啟動!"); }catch
(Exception e){ System.out.println("伺服器未啟動:"+e); } while(true){ try{ socket = server.accept(); System.out.println("客戶端接入--"); bool=true; }catch(Exception e){ System.out.println("出錯了 :"+e); } while(bool){ brin = new BufferedReader(new InputStreamReader(socket.getInputStream())); pw= new PrintWriter(socket.getOutputStream()); in= new BufferedReader(new InputStreamReader(System.in)); String content = brin.readLine(); System.out.println("客戶端:"+content); if(content.equals("exit")){ pw.println("歡迎下次再來!"); pw.flush(); System.out.println("客戶端退出!"); bool=false; } /* String replay = in.readLine(); pw.println(replay); pw.flush(); */ } } }catch(Exception e){ System.out.println("Error:"+e); }finally{ if(pw!=null){ pw.close(); } if(brin!=null){ brin.close(); } if(in!=null){ in.close(); } if(socket!=null){ socket.close(); } if(server!=null){ server.close(); } } } }

伺服器端(ServerSocket):使用while()死迴圈一直等待客戶端的連線,在使用while(bool)迴圈接收客戶端傳送來的資料,如果客戶端退出則等待下一個客戶機的連線,

客戶端:


package com.lb.LB_Socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketException;

public class myClient implements Runnable{
    private Socket socket;
    private BufferedReader brin;
    private PrintWriter pw;
    private BufferedReader in ;
    private Boolean bool = true;
    public static void main(String args[]) throws IOException{
        new Thread(new myClient()).start();

    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        try{
            socket = new Socket("192.168.30.36",4440);
            System.out.println("客戶端啟動成功!");
            try{
                while(bool){
                    in = new BufferedReader(new InputStreamReader(System.in));
                    pw = new PrintWriter(socket.getOutputStream());
                    brin = new BufferedReader(new InputStreamReader(socket.getInputStream()));


                        String readline;
                        readline = in.readLine();
                        //System.out.println(readline);

                        pw.println(readline);
                        pw.flush();

                        if(readline.equals("exit")){
                            in.close();
                            pw.close();
                            brin.close();
                            socket.close();

                            bool = false;
                        }



                        //System.out.println("伺服器:"+brin.readLine());  //伺服器回覆

                }

            }catch(SocketException e){
                if(socket==null){
                    System.out.println("斷開連線!");
                }
            }catch(Exception e){
                e.printStackTrace();
                System.out.println("未開啟伺服器");
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

客戶端(Socket):傳送資料,使用while()迴圈當客戶機線上時連續傳送訊息,傳送exit時,銷燬物件,退出連線。

想實現無阻塞通訊,雙端隨時傳送訊息,但基本的socket通訊就是你發一句我發一句,我發了你不發,我這邊就處於等待接收狀態,也就是阻塞。
  1. 就是利用執行緒,接收和傳送是兩個執行緒,互不干擾,隨發隨收;待實踐。
  2. 非阻塞的通訊機制。伺服器程式接收客戶連線、客戶程式建立與伺服器的連線,以及伺服器程式和客戶程式收發資料的操作都可以按非阻塞的方式進行。伺服器程式只需要建立一個執行緒,就能完成同時與多個客戶通訊的任務。非阻塞的通訊機制主要由java.nio包(新I/O包)中的類實現,主要的類包括ServerSocketChannel、SocketChannel、Selector、SelectionKey和ByteBuffer等。

初步實現利用執行緒解決非阻塞通訊

看下效果:這裡寫圖片描述

基本的想法就是把傳送訊息和接收列印訊息作為單獨的執行緒啟動。直接貼程式碼了

sendMsg:

package com.lb.LB_Socket;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class sendMsg implements Runnable {
    private DataOutputStream dos = null;
    //private DataInputStream dis = null;
    private Socket socket = null;
    private BufferedReader br =null ;
    private String name;
    private String content;
    boolean bool = true;

    public sendMsg(Socket socket,String name){
        //super();

        this.socket = socket;
        this.name = name;
    }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(bool){
                try {
                    dos = new DataOutputStream(socket.getOutputStream());
                    br = new BufferedReader(new InputStreamReader(System.in));

                    content = br.readLine();
                    dos.writeUTF(name+"說:"+content);
                    if(content.equals("exit")){
                        dos.close();
                        br.close();
                        socket.close();
                        bool = false;
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    bool=false;
                }

            }
        }
}

printMsg:

package com.lb.LB_Socket;

import java.io.DataInputStream;

import java.io.IOException;

import java.net.Socket;


public class printMsg implements Runnable {
    //private DataOutputStream dos = null;
    private DataInputStream dis = null;
    private Socket socket = null;
    private String content;
    boolean bool = true;

    //private String name;

    public printMsg(Socket socket){
        //super();

        this.socket = socket;
        //this.name = name;
    }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(bool){
                try {
                    dis = new DataInputStream(socket.getInputStream());
                    content = dis.readUTF();
                    System.out.println(content);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    bool=false;
                    e.printStackTrace();
                    //System.exit(1);
                }catch(Exception e){
                    bool = false;
                    e.printStackTrace();
                }
            }
        }       
}

server:

package com.lb.LB_Socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class myServer implements Runnable{
    private boolean bool=true;

    Socket socket = null;
    ServerSocket server= null;

    public static void main(String args[]){
        new Thread(new myServer()).start();
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        try{
            try{
                server = new ServerSocket(4440);
                System.out.println("伺服器已啟動!");
            }catch(Exception e){
                System.out.println("伺服器未啟動:"+e);
            }
            while(true){
                try{
                    socket = server.accept();
                    System.out.println("客戶端接入--");
                    bool=true;
                }catch(Exception e){
                    System.out.println("出錯了 :"+e);
                }
                sendMsg send = new sendMsg(socket,"伺服器");
                printMsg print  = new printMsg(socket);
                Thread s= new Thread(send);
                Thread p = new Thread(print);

                if(send.bool == false || print.bool==false){
                    s.interrupt();
                    p.interrupt();
                }else{
                    s.start();
                    p.start();
                }

            }   
        }catch(Exception e){
            System.out.println("Error:"+e);
        }finally{
            try{
                if(socket!=null){
                    socket.close();
                }
                if(server!=null){
                    server.close();
                }
            }catch(Exception e){
                e.printStackTrace();
            }

        }

    }
}

client:

package com.lb.LB_Socket;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class myClient{

    public static void main(String args[]){
        try {
            Socket socket = new Socket("192.168.30.36",4440);
            System.out.println("客戶端啟動成功!");

            sendMsg send = new sendMsg(socket,"客戶");
            printMsg print  = new printMsg(socket);
            Thread s= new Thread(send);
            Thread p = new Thread(print);

            if(send.bool == false || print.bool==false){
                p.interrupt();
                s.interrupt();
            }else{
                s.start();
                p.start();
            }
            //System.out.println(socket.isConnected());
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   
    }
}

問題是執行緒的控制,當成功連線以後,互發訊息,當某一方斷掉後,執行緒就會列印錯誤。
1. 斷掉客戶端,服務端起的傳送、接收流執行緒無法判定關閉,導致下次客戶端連線後,會出現短暫的接收不到訊息及錯誤資訊列印。之後恢復正常。
2. 斷掉伺服器,客戶端的傳送、接收流執行緒無法判定關閉退出,列印連線錯誤資訊。

如何控制socket的連線,監聽socket連線的狀態以供事件處理、關閉流。

還需多多學習,東西真的太多了。