Socket編程簡單示例(聊天服務器)
傳統(tǒng)Socket基于BIO實現(xiàn)一個簡單的聊天服務器
服務端代碼如下
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("服務端啟動成功...");
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服務端~".getBytes(StandardCharsets.UTF_8));
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));
}
}先啟動服務器端,再啟動客戶端。即可
傳統(tǒng)BIO是阻塞的,舉個燒水的例子來理解

Socket編寫一個簡單的Http服務器
http服務器的代碼
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服務器啟動成功");
while (true) {
Socket client = serverSocket.accept();
// 獲取客戶端發(fā)送過來的數(shù)據
InputStream is = client.getInputStream();
byte[] buf = new byte[1024];
int len = is.read(buf);
System.out.println("客戶端發(fā)送過來的數(shù)據:" + new String(buf, 0, len, StandardCharsets.UTF_8));
// 給客戶端響應HTTP協(xié)議的數(shù)據
OutputStream os = client.getOutputStream();
os.write(response.getBytes(StandardCharsets.UTF_8));
// 注意:要關閉客戶端資源
client.close();
}
}
}只要響應數(shù)據滿足HTTP協(xié)議,就可以通過瀏覽器訪問到頁面,下面我們使用瀏覽器訪問下

基于NIO的非阻塞簡單服務器實現(xiàn)
傳統(tǒng)BIO會阻塞,使用NIO通道編程可以設置服務器為非阻塞,當未獲取到連接時,可以處理其他的邏輯。相當于線程模型換了。下面是服務端代碼,客戶端代碼不變,采用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); // 設置非阻塞
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ù)據:" + 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); // 設置非阻塞
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ù)據會阻塞服務端。如果客戶端發(fā)送數(shù)據過大,假設要10秒,那服務端調用read方法讀取數(shù)據就要等待客戶端至少10秒。
基于NIO的Selector的簡單服務器實現(xiàn)
selector的服務端如下,這是要給單線程的服務端。相比上一小節(jié)沒有使用selector,它的優(yōu)點就是連接事件和讀事件都不會阻塞了。即使客戶端發(fā)送數(shù)據很慢,服務端也不會阻塞。
缺點是單線程執(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("服務器啟動成功...");
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. 關閉資源
client.close();
}
iterator.remove();
}
}
}
}它的線程模型還是用燒水的例子來舉例

總結
到此這篇關于Socket編程簡單示例的文章就介紹到這了,更多相關Socket編程示例內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
IDEA中Java出現(xiàn)無效的源發(fā)行版錯誤的解決辦法
這篇文章主要給大家介紹了關于IDEA中Java出現(xiàn)無效的源發(fā)行版錯誤的解決辦法,IDEA中Java出現(xiàn)?效的源發(fā)?版解決辦法出現(xiàn)該問題的原因是項?Project當中的jdk與電腦當中的jdk版本不?致造成的,需要的朋友可以參考下2023-10-10
解決springboot的JPA在Mysql8新增記錄失敗的問題
這篇文章主要介紹了解決springboot的JPA在Mysql8新增記錄失敗的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06

