Java Socket Server的演进 (一)

时间:2021-05-28 02:05:42

最近在看一些网络服务器的设计, 本文就从起源的角度介绍一下现代网络服务器处理并发连接的思路, 例子就用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 <code>backlog</code> 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];
            </span><span style="color: #0000ff">while</span><span style="color: #000000"> (scan.hasNextLine()){
System.out.println(</span>&quot;start read.&quot;<span style="color: #000000">);
String str </span>=<span style="color: #000000"> scan.nextLine();
System.out.println(str); }
socket.getOutputStream().write(</span><span style="color: #0000ff">new</span> String(&quot;HTTP-Version Status-Code Reason-Phrase CRLF\r\nHTTP/1.1 200 OK\r\n&quot;<span style="color: #000000">).getBytes());
Thread.sleep(</span>1000<span style="color: #000000">);
log.info(</span>&quot;awake.&quot;<span style="color: #000000">);
socket.close();
} </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (IOException e) {
log.severe(e.getMessage()); </span><span style="color: #008000">//</span><span style="color: #008000">To change body of catch statement use File | Settings | File Templates.</span>

}

}

}

 </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span><span style="color: #000000"> main(String[] args){
</span><span style="color: #0000ff">try</span><span style="color: #000000">{
SocketServer ss </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> SocketServer();
ss.service();
} </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (Exception e){
e.printStackTrace();
} }</span></pre>

由于socketserver的backlog,既等待队列设为50,所以下面的测试通过telnet客户端连接看阻塞式服务器对连接的处理。

 

Telnet1 先连上测试服务器IP 1.132,并打入tt3:

Java Socket Server的演进 (一)

服务器的console上响应,显示读到的数据:

Java Socket Server的演进 (一)

Telnet2 在telnet1之后连上测试服务器IP 1.132,并打入tt4:

Java Socket Server的演进 (一)

服务器没有显示输出,但其实socket已经连上,服务器线程被阻塞在还没有处理完成的telnet1上,没有返回,所以虽然操作系统已经接受了telnet2的连接,但是应用程序无法处理。windows下可以netstat观察一下连接情况,以下图显示两个1.121的连接已经建立,处于ESTABLISHED状态。

Java Socket Server的演进 (一)

这时停掉telnet1, 既让socket断开,如下:

Java Socket Server的演进 (一)

这时可以看到telnet2之前输入的tt4在服务器端才有响应,说明之前telnet2的输入被缓冲在操作系统的缓冲区。

Java Socket Server的演进 (一)

实际的处理流程就是这个样子,incoming连接被排成队列一个一个由 while(true)中的socketServer.accept方法一个一个处理:

Java Socket Server的演进 (一)

这种只能处理一个连接的服务器程序明显是很鸡肋的,没有服务器会这样处理。那么为什么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.checkListen(epoint.getPort());
getImpl().bind(epoint.getAddress(), epoint.getPort());
getImpl().listen(backlog);
bound = true;
} ...

}

可以看到调用了SocketImpl的listen方法,这个listen方法又做了什么呢?先看一下,SocketImpl类有几个实现类,这里直接根据名字直接选了PlainSocketImpl类。

Java Socket Server的演进 (一)

进来发现已经是native代码了:

private native void socketListen(int count)
throws IOException;

想看JVM干了什么,只能找OpenJDK的source代码了。(代码在linux下获取比较简单,

hg clone http://hg.openjdk.java.net/jdk7/jdk7 jdk7_tl;
运行./get_source.sh,不细说了, 不过找这个native方法的时候遇到点问题,发现没有windows版的同名c代码,只有solaris版的,有点费解, 
Java Socket Server的演进 (一)
该方法源码为:
/*
* Class: java_net_PlainSocketImpl
* Method: socketListen
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_java_net_PlainSocketImpl_socketListen (JNIEnv *env, jobject this,
jint count)
{
/* this FileDescriptor fd field */
jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
/* fdObj's int fd field */
int fd;
</span><span style="color: #0000ff">if</span><span style="color: #000000"> (IS_NULL(fdObj)) {
JNU_ThrowByName(env, JNU_JAVANETPKG </span><span style="color: #800000">&quot;</span><span style="color: #800000">SocketException</span><span style="color: #800000">&quot;</span><span style="color: #000000">,
</span><span style="color: #800000">&quot;</span><span style="color: #800000">Socket closed</span><span style="color: #800000">&quot;</span><span style="color: #000000">);
</span><span style="color: #0000ff">return</span><span style="color: #000000">;
} </span><span style="color: #0000ff">else</span><span style="color: #000000"> {
fd </span>= (*env)-&gt;<span style="color: #000000">GetIntField(env, fdObj, IO_fd_fdID);
} </span><span style="color: #008000">/*</span><span style="color: #008000">
* Workaround for bugid 4101691 in Solaris 2.6. See 4106600.
* If listen backlog is Integer.MAX_VALUE then subtract 1.
</span><span style="color: #008000">*/</span>
<span style="color: #0000ff">if</span> (count == <span style="color: #800080">0x7fffffff</span><span style="color: #000000">)
count </span>-= <span style="color: #800080">1</span><span style="color: #000000">; </span><span style="color: #0000ff">if</span> (JVM_Listen(fd, count) ==<span style="color: #000000"> JVM_IO_ERR) {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG </span><span style="color: #800000">&quot;</span><span style="color: #800000">SocketException</span><span style="color: #800000">&quot;</span><span style="color: #000000">,
</span><span style="color: #800000">&quot;</span><span style="color: #800000">Listen failed</span><span style="color: #800000">&quot;</span><span style="color: #000000">);
}

}

可以看到后面调用了JVM_Listen这个方法,继续搜索这个方法在哪:

Java Socket Server的演进 (一)

OK,这个路径下hotspot/src/share/vm/prims/jvm.cpp 的包装方法看起来很像,找过来看一下内容:

JVM_LEAF(jint, JVM_Listen(jint fd, jint count))
JVMWrapper2("JVM_Listen (0x%x)", fd);
//%note jvm_r6
return os::listen(fd, count);
JVM_END

发现是调用os::listen的,再到这个文件的头上找os的引用:

#include "runtime/os.hpp"

因为我是windows系统,绕了一圈,还是直接msdn上找到winsock的listen方法,

http://msdn.microsoft.com/en-us/library/windows/desktop/ms739168(v=vs.85).aspx

Java Socket Server的演进 (一)

backlog属性还是win32 API的底层设施支持的。

 

本文出自 “祝坤荣” 博客,请务必保留此出处

Java Socket Server的演进 (一)的更多相关文章

  1. Java Socket TCP编程(Server端多线程处理)

    package com; import java.io.*; import java.net.Socket; /** * Socket Client * <p> * Created by ...

  2. Caused by&colon; java&period;lang&period;ClassNotFoundException&colon; org&period;springframework&period;web&period;socket&period;server&period;standard&period;ServerEndpointExporter

    Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/web/socket/ ...

  3. java网络编程socket&bsol;server&bsol;TCP笔记(转)

    java网络编程socket\server\TCP笔记(转) 2012-12-14 08:30:04|  分类: Socket |  标签:java  |举报|字号 订阅     1 TCP的开销 a ...

  4. JAVA通信系列一:Java Socket技术总结

    本文是学习java Socket整理的资料,供参考. 1       Socket通信原理 1.1     ISO七层模型 1.2     TCP/IP五层模型 应用层相当于OSI中的会话层,表示层, ...

  5. java socket收发http协议内容

    来自:https://www.oschina.net/code/snippet_2009881_48232 import java.io.BufferedReader; import java.io. ...

  6. java socket编程&lpar;网络编程&rpar;

    一,网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可 ...

  7. JAVA Socket 编程学习笔记(一)

    1. Socket 通信简介及模型 Java Socket 可实现客户端--服务器间的双向实时通信.java.net包中定义的两个类socket和ServerSocket,分别用来实现双向连接的cli ...

  8. 简单的java socket 示例

    一.搭建服务器端 a).创建ServerSocket对象绑定监听端口. b).通过accept()方法监听客户端的请求. c).建立连接后,通过输入输出流读取客户端发送的请求信息. d).通过输出流向 ...

  9. JAVA Socket超时浅析

    JAVA Socket超时浅析 套接字或插座(socket)是一种软件形式的抽象,用于表达两台机器间一个连接的"终端".针对一个特定的连接,每台机器上都有一个"套接字&q ...

随机推荐

  1. jquery与各种UI框架的导入要注意的地方

    前端的处理我们会使用easyUI,amazeUI,bootstrap等等框架,然而每个页面都要导入这些js   css 文件,所以我们将要导入的文件提取出来,写在一个页面上,每次只要倒入该页面就行,如 ...

  2. Android--入门

    之前自己在学校写过一些安卓应用,那时候没有系统地学过安卓,用到什么就网上找博客.找Demo,然后自己跟着敲一遍,有些东西也不太理解,现在打算做android开发这一块了,趁毕业之前赶紧多学些技术.先是 ...

  3. paper 48: Latex中如何制作参考文献

    文章写到现在,最后一步就要大功告成了!reference,let's go! 一.用Google来做Latex的bib文件 1. 打开scholar.google.com 2. 定制   Schola ...

  4. Android上使用MP3格式录制声音

    0. 下载LAME 并解压缩 http://lame.sourceforge.net/download.php http://sourceforge.net/projects/lame/files/l ...

  5. javascript学习笔记2

    二.下列哪些变量是不正确的  说明原因 var a1; var b1 = 3; c1 ='good'; var d1 = c1 = e1;   ×  连等必须赋值 var g1 = 'hei' goo ...

  6. javascript (二) 事件

    <script></script> 函数写法: function  fun_name(){ x=docment.getElementById("demo") ...

  7. jdbc 增删改查以及遇见的 数据库报错Can&&num;39&semi;t get hostname for your address如何解决

    最近开始复习以前学过的JDBC今天肝了一晚上 来睡睡回笼觉,长话短说 我们现在开始. 我们先写一个获取数据库连接的jdbc封装类 以后可以用 如果不是maven环境的话在src文件下新建一个db.pr ...

  8. jQueryValidate的表单提交ajax刷新代码

    $("#form-member-add").validate({ rules:{ username:{ required:true, minlength:2, maxlength: ...

  9. Rarfile解压不了的问题

    最近用python调用rarfile进行解压rar压缩包时,报了如下错误: rarfile.RarCannotExec: Unrar not installed? (rarfile.UNRAR_TOO ...

  10. 入门系列之在Ubuntu上安装Drone持续集成环境

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由小铁匠米兰的v 发表于云+社区专栏 介绍 Drone是一个流行的持续集成和交付平台.它集成了许多流行的版本控制存储库服务,如GitHu ...