Socket編程簡單示例(聊天服務(wù)器)
傳統(tǒng)Socket基于BIO實現(xiàn)一個簡單的聊天服務(wù)器
服務(wù)端代碼如下
public class MyServerSocket { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(); // 綁定5000端口 serverSocket.bind(new InetSocketAddress("127.0.0.1", 5000)); System.out.println("服務(wù)端啟動成功..."); while (true) { // 如果獲取不到socket就會一致阻塞在此 Socket socket = serverSocket.accept(); InputStream is = socket.getInputStream(); byte[] buf = new byte[1024]; int len = is.read(buf); System.out.println("客戶端說:" + new String(buf, 0, len, StandardCharsets.UTF_8)); OutputStream os = socket.getOutputStream(); os.write("你好客戶端,我收到你的消息了".getBytes(StandardCharsets.UTF_8)); } } }
客戶端代碼如下
public class ClientSocket { public static void main(String[] args) throws IOException { Socket socket = new Socket(); socket.connect(new InetSocketAddress("127.0.0.1",5000)); OutputStream os = socket.getOutputStream(); os.write("hello服務(wù)端~".getBytes(StandardCharsets.UTF_8)); InputStream is = socket.getInputStream(); byte[] buf = new byte[1024]; int len = is.read(buf); System.out.println("服務(wù)器說:" + new String(buf, 0, len, StandardCharsets.UTF_8)); } }
先啟動服務(wù)器端,再啟動客戶端。即可
傳統(tǒng)BIO是阻塞的,舉個燒水的例子來理解
Socket編寫一個簡單的Http服務(wù)器
http服務(wù)器的代碼
public class HttpServer { private static String response = """ HTTP/1.1 200 OK content-type: text/html <h1>hello,client</h1> """; public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(); serverSocket.bind(new InetSocketAddress("127.0.0.1", 5001)); System.out.println("HTTP服務(wù)器啟動成功"); while (true) { Socket client = serverSocket.accept(); // 獲取客戶端發(fā)送過來的數(shù)據(jù) InputStream is = client.getInputStream(); byte[] buf = new byte[1024]; int len = is.read(buf); System.out.println("客戶端發(fā)送過來的數(shù)據(jù):" + new String(buf, 0, len, StandardCharsets.UTF_8)); // 給客戶端響應(yīng)HTTP協(xié)議的數(shù)據(jù) OutputStream os = client.getOutputStream(); os.write(response.getBytes(StandardCharsets.UTF_8)); // 注意:要關(guān)閉客戶端資源 client.close(); } } }
只要響應(yīng)數(shù)據(jù)滿足HTTP協(xié)議,就可以通過瀏覽器訪問到頁面,下面我們使用瀏覽器訪問下
基于NIO的非阻塞簡單服務(wù)器實現(xiàn)
傳統(tǒng)BIO會阻塞,使用NIO通道編程可以設(shè)置服務(wù)器為非阻塞,當(dāng)未獲取到連接時,可以處理其他的邏輯。相當(dāng)于線程模型換了。下面是服務(wù)端代碼,客戶端代碼不變,采用BIO的即可
public class NioServerSocket { public static void main(String[] args) throws IOException, InterruptedException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 5002)); serverSocketChannel.configureBlocking(false); // 設(shè)置非阻塞 while (true) { SocketChannel clientChannel = serverSocketChannel.accept(); if (clientChannel == null) { System.out.println("客戶端無連接,休息一下"); Thread.sleep(1000); continue; } Socket socket = clientChannel.socket(); InputStream is = socket.getInputStream(); byte[] buf = new byte[1024]; int len = is.read(buf); System.out.println("客戶端發(fā)送過來的數(shù)據(jù):" + new String(buf, 0, len, StandardCharsets.UTF_8)); socket.close(); clientChannel.close(); } } }
第二種實現(xiàn)方式如下
public class NioServerSocket2 { public static void main(String[] args) throws IOException, InterruptedException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 5002)); serverSocketChannel.configureBlocking(false); // 設(shè)置非阻塞 while (true) { SocketChannel clientChannel = serverSocketChannel.accept(); if (clientChannel == null) { System.out.println("客戶端無連接,休息一下"); Thread.sleep(1000); continue; } ByteBuffer buffer = ByteBuffer.allocate(1024); int len = clientChannel.read(buffer); System.out.println("客戶端說" + new String(buffer.array(), 0, len)); clientChannel.close(); } } }
但是該實現(xiàn)也有一個問題:雖然客戶端的連接過程不會阻塞了,但是客戶端發(fā)送數(shù)據(jù)會阻塞服務(wù)端。如果客戶端發(fā)送數(shù)據(jù)過大,假設(shè)要10秒,那服務(wù)端調(diào)用read方法讀取數(shù)據(jù)就要等待客戶端至少10秒。
基于NIO的Selector的簡單服務(wù)器實現(xiàn)
selector的服務(wù)端如下,這是要給單線程的服務(wù)端。相比上一小節(jié)沒有使用selector,它的優(yōu)點就是連接事件和讀事件都不會阻塞了。即使客戶端發(fā)送數(shù)據(jù)很慢,服務(wù)端也不會阻塞。
缺點是單線程執(zhí)行,如果一個線程搶到讀就緒事件并且處理的很慢,就會影響整體性能。
public class NioSelectorServerSocket { public static void main(String[] args) throws Exception { // 1. 獲取通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 5003)); serverSocketChannel.configureBlocking(false); // 2. 獲取選擇器 Selector selector = Selector.open(); // 3. 把通道注冊到選擇器上,只注冊連接繼續(xù)事件 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("服務(wù)器啟動成功..."); while (true) { // 4. 不斷輪詢選擇器中是否由連接事件 int select = selector.select(2000); if (select == 0) { System.out.println("暫時沒有客戶端連接哦"); continue; } // 5. 如果有連接繼續(xù)事件,獲取客戶端通道 Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); if (key.isAcceptable()) { SocketChannel client = serverSocketChannel.accept(); //SocketChannel client = ((ServerSocketChannel) key.channel()).accept(); // 兩種寫法都一樣 client.configureBlocking(false); // 6. 為每個連接都注冊寫事件監(jiān)聽 client.register(selector, SelectionKey.OP_READ); System.out.println("已注冊可讀事件"); } if (key.isReadable()) { SocketChannel client = (SocketChannel) key.channel(); // 7. 監(jiān)聽到可讀事件,處理可讀事件 ByteBuffer buffer = ByteBuffer.allocate(1024); int len = client.read(buffer); System.out.println("客戶端說:" + (len > 0 ? new String(buffer.array(), 0, len, StandardCharsets.UTF_8) : "")); // 8. 關(guān)閉資源 client.close(); } iterator.remove(); } } } }
它的線程模型還是用燒水的例子來舉例
總結(jié)
到此這篇關(guān)于Socket編程簡單示例的文章就介紹到這了,更多相關(guān)Socket編程示例內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JDK生成WebService客戶端代碼以及調(diào)用方式
WebService 是一種跨編程語言和跨操作系統(tǒng)平臺的遠(yuǎn)程調(diào)用技術(shù),下面這篇文章主要給大家介紹了關(guān)于JDK生成WebService客戶端代碼以及調(diào)用方式的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-08-08IDEA中Java出現(xiàn)無效的源發(fā)行版錯誤的解決辦法
這篇文章主要給大家介紹了關(guān)于IDEA中Java出現(xiàn)無效的源發(fā)行版錯誤的解決辦法,IDEA中Java出現(xiàn)?效的源發(fā)?版解決辦法出現(xiàn)該問題的原因是項?Project當(dāng)中的jdk與電腦當(dāng)中的jdk版本不?致造成的,需要的朋友可以參考下2023-10-10解決springboot的JPA在Mysql8新增記錄失敗的問題
這篇文章主要介紹了解決springboot的JPA在Mysql8新增記錄失敗的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06