Java套接字Socket

什么是套接字Socket

Socket本身就是網(wǎng)絡(luò)上的兩個程序可以互相發(fā)送請求和接收請求,應(yīng)用程序可以利用套接字在網(wǎng)絡(luò)上進(jìn)行數(shù)據(jù)傳輸。
Socket本身作為應(yīng)用程序和網(wǎng)絡(luò)層TCP/UDP之間的一個抽象層,在TCP中主要采用流套接字,采用的方式就是點(diǎn)對點(diǎn)通過字節(jié)流傳輸;UDP主要采用數(shù)據(jù)報套接字。

面向連接的入門例子

功能:客戶端發(fā)送請求,服務(wù)器接收請求
服務(wù)端:

public class MyServer {

    class HandleTask {
        
        private Socket socket;
        
        public HandleTask() {}

        public HandleTask(Socket socket) {
            this.socket = socket;
        }
        
        public void handle() {
            StringBuilder sb = new StringBuilder("Hello: ");
            InputStream is = null;
            BufferedReader br = null;
            
            try {
                is = socket.getInputStream();
                br = new BufferedReader(new InputStreamReader(is));
                sb.append(br.readLine());
                System.out.println(sb.toString());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != br) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
                try {
                    if (null != is) {
                        is.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            
            try {
                Thread.sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println(sb.toString() + "-- 結(jié)束執(zhí)行 --");
        }
    }
    
    
    public static void main(String[] args) throws IOException{
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(9999);
            while (true) {
                socket = serverSocket.accept();
                new MyServer().new HandleTask(socket).handle();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (null != socket) {
                socket.close();
            }
            
            if (null != serverSocket) {
                serverSocket.close();
            }
        }
    }
}

思路:
① 服務(wù)器端啟動了ServerSocket,然后綁定了端口9999。
② 調(diào)用accept方法,阻塞等待客戶端連接。
③ 當(dāng)接收到客戶端的請求之后,通過字節(jié)流獲取客戶端發(fā)來的信息。
多個客戶端:

public class MyClient {

    public static void main(String[] args) {
        Socket socket = null;
        BufferedWriter bw = null;
        Scanner scanner = new Scanner(System.in);
        
        try {
            socket = new Socket("localhost", 9999);
            bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write(scanner.nextLine());
            bw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            scanner.close();
            
            try {
                if (null != bw) {
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            try {
                if (null != socket) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

public class MyClient2 {

    public static void main(String[] args) {
        Socket socket = null;
        BufferedWriter bw = null;
        Scanner scanner = new Scanner(System.in);
        
        try {
            socket = new Socket("localhost", 9999);
            bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write(scanner.nextLine());
            bw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            scanner.close();
            
            try {
                if (null != bw) {
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            try {
                if (null != socket) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

結(jié)果:


1.jpg

解釋:
上面這個例子的缺陷就是如果是多個請求去等待服務(wù)器端去處理,那么服務(wù)器端會按照收到請求的順序執(zhí)行,這樣如果第一個請求執(zhí)行時間很長,那么第二個請求就很長時間等不到執(zhí)行。

多線程執(zhí)行多個客戶端

場景
基于上面的例子,思考:服務(wù)器在很多情況下是需要接收來自很多個客戶端的請求的。
解決辦法
根據(jù)多線程時間分片方式來解決問題:多個客戶端的請求,那么服務(wù)器采用每次接收到一個請求就創(chuàng)建一個線程來執(zhí)行。
采用多線程改進(jìn)的服務(wù)端

public class MyThreadServer {
    
    class HandleTask extends Thread{

        private Socket socket;
        public HandleTask() {}

        public HandleTask(Socket socket) {
            this.socket = socket;
        }
        
        @Override
        public void run() {
            StringBuilder sb = new StringBuilder("Hello: ");
            InputStream is = null;
            BufferedReader br = null;
            
            try {
                is = socket.getInputStream();
                br = new BufferedReader(new InputStreamReader(is));
                sb.append(br.readLine());
                System.out.println(sb.toString());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != br) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
                try {
                    if (null != is) {
                        is.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            
            try {
                Thread.sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println(sb.toString() + "-- 結(jié)束執(zhí)行 --");
        }
    }
    
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(9999);
            while (true) {
                socket = serverSocket.accept();
                new MyThreadServer().new HandleTask(socket).start();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (null != socket) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            try {
                if (null != serverSocket) {
                    serverSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
2.jpg

3.jpg

方案使用的模型:
上面的程序?qū)iT使用一個接收者線程來專門負(fù)責(zé)監(jiān)聽客戶端的請求,接收到客戶端的請求,就為其創(chuàng)建一個新的線程去執(zhí)行業(yè)務(wù)處理,最后通過字節(jié)流進(jìn)行返回響應(yīng)。這種模型就是BIO(阻塞IO)。
方案的缺點(diǎn):
這種工作的模型有一個問題,就是當(dāng)請求數(shù)很多時,就會創(chuàng)建和請求數(shù)一樣多匹配線程。最終當(dāng)并發(fā)量上來,那么系統(tǒng)的性能將會下降。

線程池執(zhí)行多個客戶端

場景:
基于上面的例子,使用多線程方式來處理請求,每個請求都創(chuàng)建一個匹配的線程,浪費(fèi)線程資源。采用線程池管理線程,可以充分利用線程。
采用線程池改進(jìn)的服務(wù)端:

public class MyThreadPoolServer {

    private static ExecutorService es = Executors.newCachedThreadPool();
    
    class HandleTask extends Thread {

        private Socket socket;
        public HandleTask() {}

        public HandleTask(Socket socket) {
            this.socket = socket;
        }
        
        @Override
        public void run() {
            StringBuilder sb = new StringBuilder("Hello: ");
            InputStream is = null;
            BufferedReader br = null;
            
            try {
                is = socket.getInputStream();
                br = new BufferedReader(new InputStreamReader(is));
                sb.append(br.readLine());
                System.out.println(sb.toString());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != br) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
                try {
                    if (null != is) {
                        is.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            
            try {
                Thread.sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println(sb.toString() + "-- 結(jié)束執(zhí)行 --");
        }
    }
    
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(9999);
            while (true) {
                socket = serverSocket.accept();
                es.execute(new MyThreadServer().new HandleTask(socket));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (null != socket) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            try {
                if (null != serverSocket) {
                    serverSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
4.jpg

方案使用的模型:


5.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,915評論 18 139
  • Java知識點(diǎn)1、==和equals的區(qū)別基本類型比較==比較內(nèi)容 equals比較地址值引用類型比較==比較地址...
    壓抑的內(nèi)心閱讀 606評論 0 0
  • 1.啟動Spark集群 2.執(zhí)行jar包 3.啟動了Driver進(jìn)程(通過執(zhí)行代碼啟動了Driver) 然后生成了...
    0_9f3a閱讀 1,113評論 0 0
  • 不知道從什么時候開始,我們聊天的字變得越來越少,代替文字的就是一些圖標(biāo)或是符號。比如,心情不好,用哭臉表示;需要...
    邊際小黑閱讀 158評論 0 1
  • 同事的女兒今年十八歲,小姑娘準(zhǔn)備以獻(xiàn)血的形式,慶祝自己長大成人。00后的孩子們,想法越來越不同凡響。 ...
    輕輕道來閱讀 170評論 0 1