Java套接字Socket

什么是套接字Socket

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

面向連接的入門例子

功能:客戶端發(fā)送請(qǐng)求,服務(wù)器接收請(qǐng)求
服務(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ù)器端啟動(dòng)了ServerSocket,然后綁定了端口9999。
② 調(diào)用accept方法,阻塞等待客戶端連接。
③ 當(dāng)接收到客戶端的請(qǐng)求之后,通過字節(jié)流獲取客戶端發(fā)來的信息。
多個(gè)客戶端:

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

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

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

場(chǎng)景
基于上面的例子,思考:服務(wù)器在很多情況下是需要接收來自很多個(gè)客戶端的請(qǐng)求的。
解決辦法
根據(jù)多線程時(shí)間分片方式來解決問題:多個(gè)客戶端的請(qǐng)求,那么服務(wù)器采用每次接收到一個(gè)請(qǐng)求就創(chuàng)建一個(gè)線程來執(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使用一個(gè)接收者線程來專門負(fù)責(zé)監(jiān)聽客戶端的請(qǐng)求,接收到客戶端的請(qǐng)求,就為其創(chuàng)建一個(gè)新的線程去執(zhí)行業(yè)務(wù)處理,最后通過字節(jié)流進(jìn)行返回響應(yīng)。這種模型就是BIO(阻塞IO)。
方案的缺點(diǎn):
這種工作的模型有一個(gè)問題,就是當(dāng)請(qǐng)求數(shù)很多時(shí),就會(huì)創(chuàng)建和請(qǐng)求數(shù)一樣多匹配線程。最終當(dāng)并發(fā)量上來,那么系統(tǒng)的性能將會(huì)下降。

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

場(chǎng)景:
基于上面的例子,使用多線程方式來處理請(qǐng)求,每個(gè)請(qǐng)求都創(chuàng)建一個(gè)匹配的線程,浪費(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)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容