socket编程时的发送与接收数据时的问题解析
作者:Mzoro 发布时间:2022-12-31 03:57:28
socket 编程时的发送与接收数据时的问题
在编写一个测试方法时,需要用启动一个程序监听一个端口,测试发送的数据是事正常,但是总是出现两个问题,一是 Socked 总是在 OutputSteam.write () 方法之前被关闭,但是没有使用代码调用 Socket 的 Close 方法,另一个是在接收数据时,总是卡在 InputSteam.read () 方法处 (一般发生在前一个 Socket 在写数据时异常中断后再次有新 Socket 连接时),得不到数据,直到发送端关闭 Socket, 下面是代码
package com.zoro.example.subscribe.queue;
import com.zoro.util.SendUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
/**
* @author zoro
* @version 1.0
*/
public class TestSubscribeQueue {
private static final Logger LOGGER = LoggerFactory.getLogger(TestSubscribeQueue.class);
public static void main(String[] args) throws IOException {
new Thread(new EventListener()).start();
}
private static String test(InputStream is) {
BufferedReader br = null;
InputStreamReader isr = null;
StringBuilder sb = new StringBuilder();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
String line = null;
while (true) {
try {
line = br.readLine();
if (line == null || line.length() == 0) {
break;
}
} catch (IOException e) {
e.printStackTrace();
}
sb.append(line);
sb.append("\n");
line = null;
LOGGER.debug("当前读取的数据:{}", sb.toString());
}
return sb.toString();
}
static class EventListener implements Runnable {
private final ServerSocket ss;
public EventListener() throws IOException {
ss = new ServerSocket(8087);
}
@Override
public void run() {
while (true) {
/*
这里得到的InputStream 不能在OutputStream返回数据之前关闭,因为InputStream关闭之后会导致Socket关闭,你说奇怪不奇怪,这样一来就不能正常返回数据了,报错说Socket已经关闭
*/
try (Socket s = ss.accept();
InputStream is = s.getInputStream();
OutputStream os = s.getOutputStream()) {
LOGGER.debug("等待请求...");
LOGGER.debug("新请求进入");
if (s.isClosed() || !s.isConnected() || s.isInputShutdown()) {
continue;
}
// String result = SendUtil.resolveInputStream(is);
String result = test(is);
LOGGER.debug("收到请求:{}", result);
StringBuilder response = new StringBuilder();
response.append("HTTP/1.1 200 OK\r\n");
response.append("Content-Type:text/html\r\n");
response.append("\r\n");
response.append("123252321");
LOGGER.debug("即将返回数据:{}", response.toString());
if (!s.isClosed()) {
LOGGER.debug("正在返回数据");
os.write(response.toString().getBytes());
os.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
分析原因
在第 76 行代码调用的方法中 调用的 InputStream.close () 方法,导致了 Socket 也跟着关闭了,不清楚是什么原因引起的,但是去掉 InputStream.close () 确实 OutputStream 可以向请求端改善数据了
在 OutputStream 的问题解决之后,InputStream 的问题也跟着没有了,因为我在测试时使用的是浏览器 http 发起的这个 Socket,所以我猜测是在第一次 Socket 异常中断后浏览器再次触发了重试;;
问题更新
两天后发现新问题: InputStream 在包装成 BufferedReader 整行读取时到 http 请求的最后一行还会继续向下一行读,但是发送端这时已经不发送数据了,造成服务端一直卡在这里等待
问题原因
BufferedReader.readLine () 方法在读取文件只,因为文件最后会是一个 - 1 ,所以可以知道在哪里结束,但网络请求最后没有这个 - 1 所以在读了最后一行后不知道请求数据已经没有更多了,造成一直阻塞
解决思路
http 请求(只讨论 get 与 post 两种方法),get 请求只有请求头,在读取到第一个空行时就可以结束,post 请求有请求体,可以根据请求头中 ContentLength 判断是否读取完
来源:https://my.oschina.net/Mzoro/blog/3144732


猜你喜欢
- 在开发过程中,碰到生成一个List对象,需要对其里面的每个对象都进行校验。但是,这个Lis
- 本文实例讲述了Java实现求解一元n次多项式的方法。分享给大家供大家参考,具体如下:项目需要做趋势预测,采用线性拟合、2阶曲线拟合和指数拟合
- 本文实例为大家分享了C# Winform 自动更新程序,供大家参考,具体内容如下第一步:检查更新检查更新其实无非就是去比较更新包的版本和本地
- 1.多数元素题目描述思路详解这个思路比较简单,先排序,排序过后遍历如果后一个等于前一个输出就好代码与结果class Solution { &
- persistence.xml配置文件修改存放路径今天整合Spring、struts2和Spring Data JPA的时候遇到以下问题,现
- 理论上Object类是所有类的父类,即直接或间接的继承java.lang.Object类。由于所有的类都继承在Object类,因此省略了ex
- —举例(学生排课)—正常思路的处理方法和优化过后的处理方法:比如说给学生排课。学生和课程是一个多对多的关系。按照正常的逻辑 应该有一个关联表
- Java8新特性系列我们已经介绍了Stream、Lambda表达式、DateTime日期时间处理,最后以“NullPointerExcept
- maven的配置文件settings.xml存在于两个地方:1.安装的地方:${M2_HOME}/conf/settings.xml2.用户
- 简介 Spring Cloud Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。它
- 详解java 中Spring jsonp 跨域请求的实例jsonp介绍
- Redisson分布式锁之前的基于注解的锁有一种锁是基本redis的分布式锁,锁的实现我是基于redisson组件提供的RLock,这篇来看
- 简介基于springboot,mybatis plus集成了一套多数据源的解决方案,在使用时引入相应的插件dynamic-datasourc
- 1、在Android studio中进行打开一个项目的文件之后,然后进行点击Android stuio中菜单中的“tools”的选项。在弹出
- Java集合的主要分为三种类型:• Set(集)• List(列表)• Map(映射)要深入理解集合首先要了解
- Java 常量池的实例详解Java的常量池中包含了类、接口、方法、字符串等一系列常量值。常量池在编译期间就已经确定,并保存在*.class文
- 这篇文章memo一下Jvm中关于时区设定的基础操作。Java的时区设定这里列出如下三种方式方式说明TimeZone.setDefault方式
- 1 简介Springboot是最简单的使用Spring的方式,而MongoDB是最流行的NoSQL数据库。两者在分布式、微服务架构中使用率极
- 之前代码有一个逻辑,是在初始化时读取某个包下的所有class文件,放入到一个HashMap里。代码运行过程中,通过Key获取到对应class
- 前言总是觉得对HashMap很熟悉,但最近连续被问到几个关于它的问题,才发现它其实并不简单。这里对关于它的一些问题做个总结,也希望能够大家一