用java.net.Socket来模拟实现Tomcat,碰到了一些坑,大部分是没有想到的,记录下来自查。

直接上代码,

public class TomcatDemo {

    private static ExecutorService executorService = Executors.newCachedThreadPool();

    public static void main(String[] args) throws IOException {
//监听9000端口
@SuppressWarnings("resource")
ServerSocket serverSocket = new ServerSocket(9000);
System.out.println("Tomcat服务启动成功!");
while (!serverSocket.isClosed()) {
//阻塞式
Socket request = serverSocket.accept();
System.out.println(request.getInetAddress());
executorService.execute(() -> {
try {
InputStream inputStream = request.getInputStream();
System.out.println("收到请求...");
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream,"utf-8"));
StringBuffer sb = new StringBuffer();
String line;
while((line = br.readLine()) != null) {
sb.append(line).append("\r\n");
}
System.out.println(sb.toString());
//由servlet处理业务逻辑 System.out.println("-----------------end");
//请求结束...
OutputStream outputStream = request.getOutputStream();
outputStream.write("HTTP/1.1 200 OK\r\n".getBytes());
outputStream.write(("Content-Length: " + "Hello World!".getBytes().length + "\r\n\r\n").getBytes());
outputStream.write("Hello World!".getBytes());
outputStream.flush();
System.out.println("请求结束...");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
request.close();
} catch (Exception e2) {
e2.printStackTrace();
}
} });
}
} }

运行程序,使用Chrome访问localhost:9000

问题有两个,

(1) 竟然发起两次GET请求

(2) 出现socket write error

起初开始解决socket write error,大多数说是因为socket提前close或者超时导致的,但是发起的请求还没有结束。后来一步步调试,发现代码在 line = br.readLine()) != null 处阻塞等待了!

原来是br.readLine()的机制同自己想的并不一样,它是阻塞式的,叉掉http://localhost:9000/的请求后,后续代码才继续被执行。

增加如下判断代码解决,

while((line = br.readLine()) != null) {
if (line.length() == 0){
break
;
}

sb.append(line).append("\r\n");
}

但是socket write error的问题还在,这个应该就和重复请求有关系了。

改用Firefox访问http://localhost:9000/,发现请求正常,

看来是Chrome的问题,遂查了下,应该是插件造成的请求重复发送,这也导致了如上socket write error的问题。

用Chrome访问还会出现请求/favicon.ico,这个是Chrome后台默默做的,用来显示和网站相关的信息。这也正是平时大家说的Chrome一次GET请求,出现两次的根源。

Socket编程之Tomcat模拟_采坑汇总的更多相关文章

  1. PHP Socket 编程之9个主要函数的使用之测试案例

    php的socket编程算是比较难以理解的东西吧,不过,我们只要理解socket几个函数之间的关系,以及它们所扮演的角色,那么理解起来应该不是很难了,在笔者看来,socket编程,其实就是建立一个网络 ...

  2. [深入浅出WP8.1(Runtime)]Socket编程之UDP协议

    13.3 Socket编程之UDP协议 UDP协议和TCP协议都是Socket编程的协议,但是与TCP协议不同,UDP协议并不提供超时重传,出错重传等功能,也就是说其是不可靠的协议.UDP适用于一次只 ...

  3. iPhone socket 编程之BSD Socket篇

    iPhone socket 编程之BSD Socket篇 收藏在进行iPhone网络通讯程序的开发中,不可避免的要利用Socket套接字.iPhone提供了Socket网络编程的接口CFSocket, ...

  4. 老雷socket编程之websocket实现

    老雷socket编程之websocket实现 我们主要实现私聊和群聊两个功能,要在web端实现想微信QQ那样的即时通讯的功能,我们需要了解一下websocket.websocket是一种可以双向通讯的 ...

  5. 老雷socket编程之PHP利用socket扩展实现聊天服务

    老雷socket编程之PHP利用socket扩展实现聊天服务 socket聊天服务原理 PHP有两个socket的扩展 sockets和streamssockets socket_create(AF_ ...

  6. C#编程 socket编程之tcp服务器端和客户端

    基于Tcp协议的Socket通讯类似于B/S架构,面向连接,但不同的是服务器端可以向客户端主动推送消息. 使用Tcp协议通讯需要具备以下几个条件: (1).建立一个套接字(Socket) (2).绑定 ...

  7. C#编程 socket编程之udp服务器端和客户端

    基于Udp协议是无连接模式通讯,占用资源少,响应速度快,延时低.至于可靠性,可通过应用层的控制来满足.(不可靠连接) 使用Udp协议通讯需要具备以下几个条件: (1).建立一个套接字(Socket) ...

  8. Python socket编程之二:【struct.pack】&【struct.unpack】

    import struct """通过 socket 的 send 和 recv 只能传输 str 格式的数据""" "" ...

  9. Linux系统编程(37)—— socket编程之UDP服务器与客户端

    典型的UDP客户端/服务器通讯过程: 编写UDP Client程序的步骤 1.初始化sockaddr_in结构的变量,并赋值.这里使用"8888"作为连接的服务程序的端口,从命令行 ...

随机推荐

  1. Super Ugly Number -- LeetCode

    Write a program to find the nth super ugly number. Super ugly numbers are positive numbers whose all ...

  2. [51Nod1487]占领资源

    题目大意:​ 有一个$n\times m(x,m\leq 100)$的网格图,每个格子有一个权值$w_{i,j}(1\leq w_{i,j}\leq 9)$.你可以在图中选两个格子,每个格子$(x,y ...

  3. centos安装postgresql-10及操作

    安装postgresql: Linux启动: service postgresql start 创建用户: createuser username 创建数据库: createdb dbname -O ...

  4. elasticsearch 分布式部署

    修改配置文件 /config/elasticsearch.yml 我用两台机器,内网地址分别为230 和 231 处理启动报错一: [2017-01-12T15:55:55,433][INFO ][o ...

  5. CentOS 6.9安装类型选择(Basic Server/Web Server)

    Desktop :基本的桌面系统,包括常用的桌面软件,如文档查看工具. Minimal Desktop:基本的桌面系统,包含的软件更少. Minimal:基本的系统,不含有任何可选的软件包. Basi ...

  6. Markdown中超链接增加_blank的方法

    很遗憾,无法在语法上实现,只能通过额外的的JS代码实现,比如: var links = document.links; for (var i = 0; i < links.length; i++ ...

  7. delphi 设置开机自动启动函数

    有些程序要设置为开机启动,所以自己写了个函数方便以后使用,供大家参考 procedure TMainForm.SetAutoRun(ok: boolean); var   Reg:TRegistry; ...

  8. CSS3动画那么强,requestAnimationFrame还有毛线用?

    一.哟,requestAnimationFrame, 新同学,先自我介绍下 Hello, 大家好,我就是风姿卓越,万种迷人的requestAnimationFrame,呵呵呵呵.很高兴和大家见面,请多 ...

  9. [置顶] 使用kube-proxy让外部网络访问K8S service的ClusterIP

    配置方式 kubernetes版本大于或者等于1.2时,外部网络(即非K8S集群内的网络)访问cluster IP的办法是: 修改master的/etc/kubernetes/proxy,把KUBE_ ...

  10. ajty

    ] .Tag //; ].Columns["NodeID"].ColumnName ; ].Tag)["NodeID"]; //(DataRow)row[&qu ...