CoreJava學習11——網絡編程

Java網絡編程html

一、Socket編程java

Socket(套接字):封裝着如端口號,ip地址,計算機名等信息的類。經過Socket咱們能夠和遠程計算機通訊。spring

網絡通訊模型編程

C/S Client/Server緩存

客戶端通航運行在用戶的計算機上,客戶端是一個特有的程序,實現特有的功能,鏈接服務器進行通訊,誰發起鏈接誰是用戶。安全

服務器端一般是等待客戶端鏈接,提供功能服務並與之通訊。服務器

B/S網絡

固定了客戶端和通訊協議和C/S結構。多線程


通訊協議:計算機通訊的實質就是相互收發字節。那麼按照必定的格式收發字節就是通訊協議。併發


/**
* 建立客戶端Socket
* Socket客戶端類
* 構造的時候就會根據給定的服務端ip地址和服務端的端口號嘗試鏈接
*/
try {
    System.out.println("開始鏈接");
    Socket socket = new Socket("172.16.3.33", 8088);
    System.out.println("與服務端鏈接成功!");
    /**
    * 經過socket能夠獲取一組與服務器通訊的輸入輸出流
    * 咱們對其包裝就能夠方便進行讀寫信息了。
    * 經過socket拿到的是兩個低級流(字節流)
    */
    InputStream in = socket.getInputStream();
    OutputStream out = socket.getOutputStream();
    /**
    * 向服務器發送字符,將字符輸出流轉換成緩衝字符輸出流
    */
    PrintWriter pw = new PrintWriter(out);
    pw.println("你好服務器!");
    pw.flush();
    /**
    * 收取服務器發送的字符串,包裝爲緩衝字符輸入流
    */
    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    //讀取服務器發回的信息
                                                                                                                                                                                                                                                                                                       
    String info = reader.readLine();
    System.out.println("服務端:"+info);
} catch (IOException e) {
    //e.printStackTrace();
}
/**
* 服務端
* 服務器端打開socket等待客戶端的鏈接
* 服務器端的Socket名稱是ServerSocket
* 建立ServerSocket須要指定服務端口號
*/
public static void main(String[] args) {
    try {
        System.out.println("dddd");
        ServerSocket server = new ServerSocket(8050);
        /**
        * 監聽端口
        * 等待客戶端鏈接
        * 等待客戶端鏈接的方法accept()
        * 該方法是一個阻塞方法,知道有客戶端鏈接上該方法纔會返回,返回的就是當前客戶端的套接字。
        * 從中咱們能夠知道客戶端的ip等信息。
        *
        * accept()方法能夠重複調用
        */
        System.out.println("啓動完畢,等待鏈接");
        Socket client = server.accept();
        System.out.println("有一個客戶端和我鏈接了");
        /**
        * 經過socket獲取輸入流讀取客戶端的信息
        */
        InputStream in = client.getInputStream();
        OutputStream out = client.getOutputStream();
                                                                                                                                                                                                                                                                                                           
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        System.out.println("客戶端:"+reader.readLine());
                                                                                                                                                                                                                                                                                                           
        //向客戶端發信息
        PrintWriter pw = new PrintWriter(out);
        pw.println("你好客戶端");
        pw.flush();   
    } catch (IOException e) {
        //e.printStackTrace();
    }
}


二、多線程Socket


Server端多線程:

服務器端無限循環接受客戶端的訪問,每鏈接都能茶聖一對新的Socket的實例。

爲每一個客戶端鏈接建立一個獨立線程,處理客戶請求。


public static void main(String[] args) {
    try {
        ServerSocket server = new ServerSocket(8088);
        System.out.println("啓動完畢,等待鏈接");
                                                                                                                                                                                                                                                                                                           
        while (true) {
        Socket client = server.accept();
        if(client==null)continue;
        System.out.println("與客戶端鏈接成功"+client.getInetAddress().getHostAddress());
        Handler handler = new Handler(client);//交給線程去處理
        Thread t = new Thread(handler);
        t.start();
        }   
    } catch (IOException e) {
    }
}
/**
* 與客戶端通訊線程
* 負責與某個特定的socket的客戶端進行通訊
* 每一個線程實例負責一個客戶端的通訊
*/
private static class Handler implements Runnable {
    /**
    * 當前線程要通訊的客戶端socket
    */
    private Socket client;
    public Handler(Socket client) {
        this.client = client;
    }
    public void run(){
        try {
            /** 經過socket獲取客戶端信息 */
            InputStream in = this.client.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            /** 死循環讀取客戶端信息 */
                                                                                                                                                                                                                                                                                                               
            while (true) {
                if (reader.readLine() == null)
                return;
                System.out.println("客戶端"+ this.client.getInetAddress().getHostAddress() + ":" + reader.readLine());
            }
        } catch (Exception e) {
        }
    }
}


三、線程池

上面這種頻繁的建立線程和銷燬線程是很是消耗資源和性能的。

能夠建立一些空的線程,將它們保存起來,當有任務須要併發執行時,咱們取出一個空的線程來運行這個任務,當任務運行完畢後,在將線程收回,等待下次分配任務。

這樣自始自終咱們都只使用了開始建立的那些線程,並能夠重複利用來節省性能開銷。

JDK提供了線程池的管理器ExecutorService

經過Executors類的靜態方法建立幾個不一樣實現的線程池。


Executors.newCachedThreadPool();建立一個緩存線程池,當有任務的時候會檢查線程池中是否有空線程,如有就使用它,若沒有就建立新的線程。若長久沒有使用的線程會自動回收。

Executors.newFixedThreadPool(int threads);建立一個可重用的,具備固定線程數的線程池。

Executors.newScheduledExecutor();建立只有一條線程的線程池,它能夠在指定延遲後執行線程任務。


Executors.newSingleThreadExecutor();建立一個只有單線程的線程池,至關於Exceutors.newFixedThreadPool方法時傳入參數1。裏邊維護着一個任務隊列。


/**
* 建立一個線程池,具備50個
*/
ExecutorService threadPool =Executors.newFixedThreadPool(50);
try {
    ServerSocket server = new ServerSocket(8088);
    System.out.println("啓動完畢,等待鏈接");
    /** 將轉發消息線程啓動 */
    SendMessageHandler sHandler = new SendMessageHandler();
    Thread sendTh = new Thread(sHandler);
    sendTh.start();
                                                                                                                                                                                                                                                                                                       
    while (true) {
        Socket client = server.accept();
        System.out.println("與客戶端鏈接成功"+client.getInetAddress().getHostAddress());
        Handler handler = new Handler(client);//交給線程去處理
        /**
        * 將須要併發的任務(Runnable)交給線程池
        * 讓其分配空線程去運行該任務
        * 若線程都在工做,那麼登載,直到有空線程爲止。
        */
        threadPool.execute(handler);
        //Thread t = new Thread(handler);
        //t.start();
    }
} catch (IOException e) {
}


四、雙端隊列

內部由兩個隊列實現,他們交替進行存取工做。這樣至少能夠保證2個線程同時進行存取操做。可是對於兩個線程同時進行存或者取,仍是要求同步的。

可是比單隊列實現線程安全仍是要快的。


BlockingDeque雙端隊列

實現:

1)ArrayBlockingDeque該類實現的構造方法要求咱們傳入一個整數,表明當前隊列的長度。因此這個是一個固定大小的雙端隊列,存取原則爲FIFO先入先出的原則。

當元素調用offer存入了隊列時,若隊列已滿,那麼能夠設置延時等待,當超過了延時等待會返回存入失敗。

2)LinkedBlockingDeque變長雙端隊列。長度不定,隨着元素數量而增長,最大能夠達到Integer.MAX_VALUE,重載構造方法能夠傳入一個整數,使之變爲一個定長的隊列。

3)PriorityBlockingDeque 這個和LinkedBlockingDeque類似,只不過是進去了天然排序後獲取。

4)SynchronousQueue特殊的雙端隊列 存取步驟有要求,必須存一次取一次,交替進行。

例:

服務端

/**
* 建立一個靜態集合保護全部客戶端的數輸入流
* 注意:由於這個集合被多個線程使用,所喲一集合要是安全的。
*/
static Vector<PrintWriter> allOut = new Vector<PrintWriter>();
/** 消息隊列 */
static BlockingDeque<String> msgQueue = new LinkedBlockingDeque<String>();
public static void main(String[] args) {
    /**
    * 建立一個線程池,具備50個
    */
    ExecutorService threadPool =Executors.newFixedThreadPool(50);
    try {
        ServerSocket server = new ServerSocket(8088);
        System.out.println("啓動完畢,等待鏈接");
        /** 將轉發消息線程啓動 */
        SendMessageHandler sHandler = new SendMessageHandler();
        Thread sendTh = new Thread(sHandler);
        sendTh.start();
                                    
        while (true) {
            Socket client = server.accept();
            System.out.println("與客戶端鏈接成功"+client.getInetAddress().getHostAddress());
            Handler handler = new Handler(client);//交給線程去處理
            /**
            * 將須要併發的任務(Runnable)交給線程池
            * 讓其分配空線程去運行該任務
            * 若線程都在工做,那麼登載,直到有空線程爲止。
            */
            threadPool.execute(handler);
            //Thread t = new Thread(handler);
            //t.start();
        }
    } catch (IOException e) {
    }
}
/**
* 與客戶端通訊線程
* 負責與某個特定的socket的客戶端進行通訊
* 每一個線程實例負責一個客戶端的通訊
*/
private static class Handler implements Runnable {
    /**
    * 當前線程要通訊的客戶端socket
    */
    private Socket client;
                                
    public Handler(Socket client) {
        this.client = client;
    }
    public void run(){
        PrintWriter pw = null;
        try {
            /** 經過socket獲取客戶端信息 */
            InputStream in = this.client.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            /**
            * 將當前客戶端的輸出流保存到共享集合
            */
            pw = new PrintWriter(client.getOutputStream());
            allOut.add(pw);
            /** 死循環讀取客戶端信息 */
            while (true) {
                String str = reader.readLine();
                /**
                * 將信息放入到消息隊列
                */
                if("q".equals(str)){
                client.close();
                }
                msgQueue.offer(str);
                /**
                * arg1:存放的元素
                * arg2:延時時間
                * arg3:延時的時間單位
                *
                * 下面方法是向隊列中存放元素,設置5秒超時,若超時了尚未放入隊列這返回false
                */
                boolean tf = msgQueue.offer(str, 5, TimeUnit.SECONDS);
            }       
        } catch (Exception e) {
            e.printStackTrace();
            /**
            * 拋出異常,咱們應該將這個客戶端的輸出流從共享集合中刪除
            * 告訴其餘線程不要再發信息了。
            */
            allOut.remove(pw);
        }
   }
}
/**
* 轉發消息
* 循環消息隊列,將每個消息經過全部客戶端的輸入流發送給客戶端
* 作到廣播的效果。
*/
public static class SendMessageHandler implements Runnable{
    public void run() {
        while (true) {
            /** 循環將消息發送給每個客戶端 每50ms做一次*/
            String str = null;
            while ((str=msgQueue.poll())!=null) {//and msgQueue.poll()!=null
                /** 獲取全部客戶端的輸出流,將字符串輸出 */
                for (PrintWriter pw : allOut) {
                    pw.println(str);
                    pw.flush();
                }
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


客戶端:

public static void main(String[] args) {
    /**
    * 建立客戶端Socket
    * Socket客戶端類
    * 構造的時候就會根據給定的服務端ip地址和服務端的端口號嘗試鏈接
    */
    try {
        System.out.println("開始鏈接");
        Socket socket = new Socket("172.16.3.14", 8088);
        System.out.println("與服務端鏈接成功!");
              
        //啓動消息線程
        Thread readerMsgTh = new Thread(new ReadMessageHandler(socket.getInputStream()));
        readerMsgTh.start();
              
        OutputStream out = socket.getOutputStream();
        PrintWriter pw = new PrintWriter(out);
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true) {
            pw.println(reader.readLine());
            pw.flush();
        }
    } catch (IOException e) {
    //e.printStackTrace();
    }
}
    /**
    * 該線程用於從服務器讀取信息。
    */
    public static class ReadMessageHandler implements Runnable{
        private InputStream in;//從服務端讀取信息
        public ReadMessageHandler(InputStream in) {
            this.in = in;
        }
        public void run() {
            try {
                //將字節輸入流轉換爲緩衝字符輸入流
                BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                while (true) {
                   System.out.println(reader.readLine());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }



http://www.cnblogs.com/springcsc/archive/2009/12/03/1616413.html這裏有一篇文章講的比較細,把地址記錄下來,供查詢,感謝做者。

相關文章
相關標籤/搜索