Created
September 19, 2020 06:18
-
-
Save c-rainstorm/30169433e0745486b25a30c2987b756b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package me.rainstorm.playground.webserver; | |
import lombok.SneakyThrows; | |
import java.io.*; | |
import java.net.ServerSocket; | |
import java.net.Socket; | |
import java.time.LocalDateTime; | |
/** | |
* @author baochen1.zhang | |
* @date 2020.09.19 | |
*/ | |
public class BIOServer implements Runnable { | |
ServerSocket ss; | |
public BIOServer(int port) throws IOException { | |
ss = new ServerSocket(port); | |
} | |
@Override | |
public void run() { | |
try { | |
// run 方法调用,没有新链接进入时阻塞在 ss.accept() | |
System.out.println("bioServer started,waiting connection"); | |
while (!Thread.interrupted()) { | |
new Thread(new Handler(ss.accept())).start(); | |
} | |
} catch (IOException ex) { /* ... */ } | |
} | |
class Handler implements Runnable { | |
final Socket socket; | |
final BufferedReader bufferedReader; | |
final BufferedWriter bufferedWriter; | |
Handler(Socket s) throws IOException { | |
socket = s; | |
bufferedReader = new BufferedReader(new InputStreamReader(s.getInputStream())); | |
bufferedWriter = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); | |
System.out.println("new socket created"); | |
} | |
@SneakyThrows | |
@Override | |
public void run() { | |
try { | |
System.out.printf("[%s]%s on %s start run %n", LocalDateTime.now(), this, Thread.currentThread().getName()); | |
String input = bufferedReader.readLine(); | |
System.out.printf("[%s]%s on %s read done%n ", LocalDateTime.now(), this, Thread.currentThread().getName()); | |
String output = process(input); | |
System.out.printf("[%s]%s on %s process done%n ", LocalDateTime.now(), this, Thread.currentThread().getName()); | |
bufferedWriter.write(output); | |
bufferedWriter.flush(); | |
System.out.printf("[%s]%s on %s write done%n ", LocalDateTime.now(), this, Thread.currentThread().getName()); | |
} catch (IOException ex) { /* ... */ } finally { | |
System.out.printf("[%s]%s on %s socket closing%n ", LocalDateTime.now(), this, Thread.currentThread().getName()); | |
socket.close(); | |
System.out.printf("[%s]%s on %s socket closed%n ", LocalDateTime.now(), this, Thread.currentThread().getName()); | |
} | |
} | |
private String process(String cmd) { | |
return cmd.split(" ")[1] + "\n"; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package me.rainstorm.playground.webserver; | |
import lombok.SneakyThrows; | |
import java.io.IOException; | |
import java.net.InetSocketAddress; | |
import java.nio.ByteBuffer; | |
import java.nio.channels.ServerSocketChannel; | |
import java.nio.channels.SocketChannel; | |
import java.time.LocalDateTime; | |
/** | |
* @author baochen1.zhang | |
* @date 2020.09.19 | |
*/ | |
public class NonBlockingServer implements Runnable { | |
final ServerSocketChannel serverSocket; | |
/** | |
* Server Socket non-Block | |
*/ | |
public NonBlockingServer(int port) throws IOException { | |
serverSocket = ServerSocketChannel.open(); | |
serverSocket.socket() | |
// bind() | |
.bind(new InetSocketAddress(port)); | |
serverSocket.configureBlocking(false); | |
} | |
@Override | |
public void run() { | |
try { | |
// run 方法调用,没有新链接进入时不阻塞,而是返回 null | |
System.out.println("NonBlockingServer started,waiting connection"); | |
while (!Thread.interrupted()) { | |
SocketChannel newConnection; | |
while ((newConnection = serverSocket.accept()) == null) { | |
System.out.printf("[%s][ %s] 当前没有连接接入,主动挂起3秒%n", LocalDateTime.now(), Thread.currentThread().getName()); | |
Thread.sleep(3000); | |
} | |
new Thread(new Handler(newConnection)).start(); | |
} | |
} catch (IOException | InterruptedException ex) { /* ... */ } | |
} | |
class Handler implements Runnable { | |
final SocketChannel socket; | |
ByteBuffer inputBuffer = ByteBuffer.allocate(1000); | |
public Handler(SocketChannel newConnection) throws IOException { | |
System.out.println("new socket created"); | |
socket = newConnection; | |
socket.configureBlocking(false); | |
} | |
@SneakyThrows | |
@Override | |
public void run() { | |
try { | |
System.out.printf("[%s]%s on %s start run %n", LocalDateTime.now(), this, Thread.currentThread().getName()); | |
String input = read(); | |
System.out.printf("[%s]%s on %s read done%n ", LocalDateTime.now(), this, Thread.currentThread().getName()); | |
String output = process(input); | |
System.out.printf("[%s]%s on %s process done%n", LocalDateTime.now(), this, Thread.currentThread().getName()); | |
write(output); | |
System.out.printf("[%s]%s on %s write done%n ", LocalDateTime.now(), this, Thread.currentThread().getName()); | |
} catch (IOException ex) { /* ... */ } finally { | |
System.out.printf("[%s]%s on %s socket closing%n ", LocalDateTime.now(), this, Thread.currentThread().getName()); | |
socket.close(); | |
System.out.printf("[%s]%s on %s socket closed%n ", LocalDateTime.now(), this, Thread.currentThread().getName()); | |
} | |
} | |
private void write(String output) throws IOException { | |
byte[] bytes = output.getBytes(); | |
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); | |
socket.write(byteBuffer); | |
socket.write(ByteBuffer.wrap("\n".getBytes())); | |
} | |
private String read() throws IOException, InterruptedException { | |
// ASCII 在UTF8里都是一字节 | |
int length = readLength(); | |
return readBody(length); | |
} | |
private String readBody(int total) throws IOException, InterruptedException { | |
byte[] content = new byte[total]; | |
int pos = 0; | |
while (pos < total) { | |
while (!inputBuffer.hasRemaining()) { | |
// 切到写模式 | |
inputBuffer.flip(); | |
while (socket.read(inputBuffer) <= 0) { | |
System.out.printf("[%s][%s][readBody]未读到足够数据,休眠一秒%n", LocalDateTime.now(), Thread.currentThread().getName()); | |
Thread.sleep(1000); | |
} | |
// 切回读模式 | |
inputBuffer.flip(); | |
} | |
byte cur = inputBuffer.get(); | |
while (Character.isWhitespace(cur) && inputBuffer.hasRemaining()) { | |
cur = inputBuffer.get(); | |
} | |
if (Character.isWhitespace(cur)) { | |
continue; | |
} | |
System.out.println("find char: " + (char) cur); | |
content[pos++] = cur; | |
} | |
return new String(content); | |
} | |
private int readLength() throws IOException, InterruptedException { | |
while (socket.read(inputBuffer) <= 0) { | |
System.out.printf("[%s][%s][readLength]未读到足够数据,休眠一秒%n", LocalDateTime.now(), Thread.currentThread().getName()); | |
Thread.sleep(1000); | |
} | |
// 切到读模式 | |
inputBuffer.flip(); | |
byte len = inputBuffer.get(); | |
return len - '0'; | |
} | |
private String process(String cmd) { | |
return cmd; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package me.rainstorm.playground.test.webserver; | |
import me.rainstorm.playground.webserver.BIOServer; | |
import me.rainstorm.playground.webserver.NonBlockingServer; | |
import org.junit.jupiter.api.Test; | |
import java.io.IOException; | |
/** | |
* @author baochen1.zhang | |
* @date 2020.09.19 | |
*/ | |
public class WebServerTest { | |
/** | |
* 基于 Telnet 来测试,Telnet 文本格式 [len][ ][len个字符][换行] | |
* <p> | |
* 2 HH // telnet 请求 | |
* HH // 服务端响应 | |
*/ | |
@Test | |
public void bioServer() throws IOException { | |
BIOServer server = new BIOServer(1997); | |
server.run(); | |
} | |
/** | |
* 基于 Telnet 来测试,Telnet 文本格式 [len] [len个字符,可以不连续,忽略中间空白符] | |
* <p> | |
* 2 H H // telnet 请求 | |
* HH // 服务端响应 | |
*/ | |
@Test | |
public void nioServer() throws IOException { | |
NonBlockingServer nonBlockingServer = new NonBlockingServer(1997); | |
nonBlockingServer.run(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment