java - C# 和 Java 服务器之间的 TCP 通信
问题描述
我正在尝试使用 TCP 将 json 从 C# 服务器传递到 Java 服务器,问题是 Java 服务器第一次似乎接收到一个空的 json。第二次及以后它工作正常,请参阅下面的输出。欢迎任何想法或建议,在此先感谢。
输出:
Starting server...
Waiting for a connection..
Connected!
Reading...
Received empty object
Received:
Connected!
Reading...
Received: "Request:gethotellist"
Connected!
Reading...
Received: "Request:gethotellist"
下面是发送 json 的 C# 代码片段:
public void GetHotelList()
{
TcpClient clientSocket = new TcpClient();
clientSocket.Connect("127.0.0.1", 6767);
NetworkStream ns = clientSocket.GetStream();
string jsonRequest = "Request:gethotellist";
string jsonToSend = JsonConvert.SerializeObject(jsonRequest);
byte[] dataBytes = Encoding.UTF8.GetBytes(jsonToSend);
ns.Write(dataBytes, 0, dataBytes.Length);
ns.Close();
}
Java服务器:
public class JHotelServer
{
public static void main(String[] args) throws IOException
{
final int PORT = 6767;
System.out.println("Starting server...");
@SuppressWarnings("resource")
ServerSocket welcomeSocket = new ServerSocket(PORT);
System.out.println("Waiting for a connection..");
while(true)
{
try
{
Socket connectionSocket = welcomeSocket.accept();
System.out.println("Connected!");
Thread connectionThread = new Thread(new TcpConnectionManager(connectionSocket));
connectionThread.start();
}
catch(IOException ioe)
{
ioe.printStackTrace();
}
}
}
}
这是 Tcp 通信管理器:
public class TcpConnectionManager implements Runnable
{
private DataInputStream inFromDotNet;
public TcpConnectionManager(Socket socket) throws IOException
{
inFromDotNet = new DataInputStream(socket.getInputStream());
}
@Override
public void run()
{
try
{
System.out.println("Reading...");
byte[] rvdMsgByte = new byte[inFromDotNet.available()];
// Collecting data into byte array
for (int i = 0; i < rvdMsgByte.length; i++)
{
rvdMsgByte[i] = inFromDotNet.readByte();
}
if (rvdMsgByte.length == 0)
{
System.out.println("Received empty object");
}
// Converting collected data in byte array into String.
String rvdMsgTxt = new String(rvdMsgByte);
System.out.println("Received: " + rvdMsgTxt);
}
catch(IOException ioe)
{
ioe.printStackTrace();
}
}
}
解决方案
根据显示的代码,完全可以预期有时会得到一个空的有效载荷,因为如果有效载荷数据包尚未到达:inFromDotNet.available()
将为零。
基本上,.available()
(和等效的)不应该用于确定有关内容的任何内容,除了“当前缓冲的字节”(可能在同步读取和异步读取之间进行选择)。
这里常用的方法有两种:
- 使用 EOF 表示有效载荷的结束,即读取直到套接字说它已关闭并且所有内容都已读取;如何检测这取决于特定的套接字 API(例如,在 .NET 中,它会在
Receive
返回非正数时) - 实现某种基本的成帧协议
如果每个套接字只发送一条消息,则第一个选项是相关的;如果您将在每个套接字上发送多条消息,则第二个选项是必要的。一个基本的框架协议可以像“消息由换行符分隔”一样简单,但在许多情况下,您可能需要二进制安全的框架,例如长度前缀。例如,您的 JSON 可能包含换行符,这可能会被错误地解释为帧的结尾。
无论哪种情况:使用套接字,您几乎总是需要循环读取,因为数据可以拆分为多个数据包,和/或您的接收缓冲区大小可能太小。因此,通常,您会循环和缓冲所有有效负载,直到检测到 EOF 或帧结束,然后才开始尝试处理内容。特别是,您通常不应该尝试解码文本,直到您知道您拥有整个内容,因为多字节字符可能跨越多个“读取”/“接收”调用,所以:除非您使用有状态文本解码器,否则您可能会错误地解码此类字符。
您看到的行为对于几乎所有平台和语言都是通用的;它并不特定于 Java/C#。
推荐阅读
- sql-server - Debezium 错误:ConnectException:数据行小于列索引
- python-3.x - 有没有一种快速的方法可以找到熊猫数据框的全局分位数?
- python - 关闭笔记本后检索模型训练的状态
- c - 为什么 pthread_equal 是线程安全的?
- mysql - 三张表合并结果不重复
- c - Makefile 没有这样的文件或目录
- reactjs - 单选按钮单击类型错误:无法读取未定义的属性“setState”
- javascript - 渲染没有更新?
- cordova - 使用带有模拟器的 Cordova 和 Android Studio,事件日志一直显示“无法打开连接到:localhost/127.0.0.1:5037”
- docker - Docker-compose关于Domjudge服务器的设置问题