Skip to content

Instantly share code, notes, and snippets.

@c-rainstorm
Created September 19, 2020 06:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save c-rainstorm/30169433e0745486b25a30c2987b756b to your computer and use it in GitHub Desktop.
Save c-rainstorm/30169433e0745486b25a30c2987b756b to your computer and use it in GitHub Desktop.
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";
}
}
}
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;
}
}
}
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