Java Socket Server的演进 (一)(一)

2014-11-24 01:42:33 · 作者: · 浏览: 0
最近在看一些网络服务器的设计, 本文就从起源的角度介绍一下现代网络服务器处理并发连接的思路, 例子就用java提供的API。
1.单线程同步阻塞式服务器及操作 系统API
此种是最简单的socket服务器了,完全不考虑多连接的问题,主线程一次只处理一个连接,其他的连接由操作系统保持,用的是java socket包的ServerSocket,其构造函数支持的backlog就是TCP连接的等待队列
复制代码
* The maximum queue length for incoming connection indications (a
* request to connect) is set to the backlog parameter. If
* a connection indication arrives when the queue is full, the
* connection is refused.
public ServerSocket(int port, int backlog) throws IOException {
this(port, backlog, null);
}
复制代码
server的样例代码,纯测试用途,不考虑优雅问题了:
复制代码
public class SocketServer{
Logger log = getLogger("SocketServer");
ServerSocket server = null;
public SocketServer() throws IOException {
server = new ServerSocket(8080,50);
System.out.println("Server start... listen on:8080" + server.getInetAddress().toString());
}
public void service() throws InterruptedException {
while(true) {
try {
log.info("wait connection...");
Socket socket = server.accept();
log.info(socket.toString());
InputStream is = socket.getInputStream();
Scanner scan = new Scanner(is);
byte[] buffer = new byte[1024];
while (scan.hasNextLine()){
System.out.println("start read.");
String str = scan.nextLine();
System.out.println(str);
}
socket.getOutputStream().write(new String("HTTP-Version Status-Code Reason-Phrase CRLF\r\nHTTP/1.1 200 OK\r\n").getBytes());
Thread.sleep(1000);
log.info("awake.");
socket.close();
} catch (IOException e) {
log.severe(e.getMessage()); //To change body of catch statement use File | Settings | File Templates.
}
}
}
public static void main(String[] args){
try{
SocketServer ss = new SocketServer();
ss.service();
} catch (Exception e){
e.printStackTrace();
}
}
复制代码
由于socketserver的backlog,既等待队列设为50,所以下面的测试通过telnet客户端连接看阻塞式服务器对连接的处理。
Telnet1 先连上测试服务器IP 1.132,并打入tt3:
image_thumb6
服务器的console上响应,显示读到的数据:
image_thumb8
Telnet2 在telnet1之后连上测试服务器IP 1.132,并打入tt4:
image_thumb15
服务器没有显示输出,但其实socket已经连上,服务器线程被阻塞在还没有处理完成的telnet1上,没有返回,所以虽然操作系统已经接受了telnet2的连接,但是应用程序无法处理。 windows下可以netstat观察一下连接情况,以下图显示两个1.121的连接已经建立,处于ESTABLISHED状态。
image_thumb4
这时停掉telnet1, 既让socket断开,如下:
image_thumb11
这时可以看到telnet2之前输入的tt4在服务器端才有响应,说明之前telnet2的输入被缓冲在操作系统的缓冲区。
image_thumb13
实际的处理流程就是这个样子,incoming连接被排成队列一个一个由 while(true)中的socketServer.accept方法一个一个处理:
image_thumb21
这种只能处理一个连接的服务器程序明显是很鸡肋的,没有服务器会这样处理。那么为什么ServerSocket的构造函数支持这个backlog的缓冲队列呢? 下面在看看JVM代码中封装了什么
Backlog属性的本地代码
前面提到的backlog属性,JVM如何将这个队列与本地操作系统API结合呢,先看下构造ServerSocket的部分代码:
复制代码
public void bind(SocketAddress endpoint, int backlog) throws IOException {
...
if (backlog < 1)
backlog = 50;
try {
SecurityManager security = System.getSecurityManager();
if (security != null)
security.chec