1. Overview
1.概述
To send and receive data over a network, we often use sockets. Sockets are nothing but a combination of an IP address and a port number, which can uniquely identify a program running on any given machine.
为了在网络上发送和接收数据,我们经常使用套接字。套接字只不过是一个IP地址和一个端口号的组合,它可以唯一地识别在任何特定机器上运行的程序。
In this tutorial, we’ll show how we can read data which is sent to us over a socket.
在本教程中,我们将展示如何读取通过套接字发送给我们的数据。
2. Reading Data From a Socket
2.从插座中读取数据
Let’s assume, we’ve got a basic understanding of socket programming.
让我们假设,我们已经有了对socket编程的基本了解。
Now, we’ll dig deeper into reading data on a port our server is listening on.
现在,我们将深入研究在我们的服务器所监听的端口上读取数据。
Firstly, we need to declare and initialize ServerSocket, Socket, and DataInputStream variables:
首先,我们需要声明并初始化ServerSocket,Socket,和DataInputStream变量。
ServerSocket server = new ServerSocket(port);
Socket socket = server.accept();
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
Note that we’ve chosen to wrap the socket’s InputStream in a DataInputStream. This allows us to read lines of a text and Java primitive data types in a portable way.
请注意,我们选择将套接字的InputStream包裹在一个DataInputStream中。这使我们能够以一种可移植的方式读取文本和Java原始数据类型的行。
This is nice since now, if we know the type of data we’ll receive, we can use specialized methods like readChar(), readInt(), readDouble(), and readLine().
这很好,因为现在,如果我们知道我们将收到的数据类型,我们可以使用专门的方法,如readChar()、readInt()、readDouble(),和readLine()。
However, it can be challenging if the type and length of data are not known beforehand.
然而,如果事先不知道数据的类型和长度,这可能是一个挑战。
In that case, we’ll get a stream of bytes from the socket, instead, using the lower-level read() function. But, there is a small problem in this approach: How do we know the length and type of data we’ll get?
在这种情况下,我们将使用较低级别的read()函数,从套接字中获得一个字节流。但是,这种方法有一个小问题。我们如何知道我们将得到的数据的长度和类型?。
Let’s explore this scenario in the next section.
让我们在下一节探讨这种情况。
3. Reading Binary Data from a Socket
3.从插座中读取二进制数据
When reading data in bytes, we need to define our own protocol for communication between server and client. The simplest protocol which we can define is called TLV (Type Length Value). It means that every message written to the socket is in the form of the Type Length Value.
当以字节为单位读取数据时,我们需要为服务器和客户端之间的通信定义自己的协议。我们可以定义的最简单的协议叫做TLV(类型长度值)。这意味着写到套接字的每条信息都是以类型长度值的形式出现。
So we define every message sent as:
因此,我们将发送的每条信息定义为。
- A 1 byte character that represents the data type, like s for String
- A 4 byte integer that indicates the length to the data
- And then the actual data, whose length was just indicated
Once the client and the server establish the connection, each message will follow this format. Then, we can write our code to parse each message and read n bytes of data of a specific type.
一旦客户端和服务器建立了连接,每个消息都将遵循这种格式。然后,我们可以编写代码来解析每个消息,并读取特定类型的n字节的数据。
Let’s see how we can implement this using a simple example with a String message.
让我们用一个简单的例子来看看我们如何用一个String消息来实现这一点。
Firstly, we need to call the readChar() function, to read the type of data and then call the readInt() function to read the length of it:
首先,我们需要调用readChar()函数,读取数据的类型,然后调用readInt()函数,读取它的长度。
char dataType = in.readChar();
int length = in.readInt();
After that, we need to read the data which we are receiving. An important point to note here is that the read() function might not be able to read all data in one call. So, we need to call the read() in a while loop:
之后,我们需要读取我们所收到的数据。这里需要注意的一点是,read()函数可能无法在一次调用中读取所有数据。因此,我们需要在一个while循环中调用read():。
if(dataType == 's') {
byte[] messageByte = new byte[length];
boolean end = false;
StringBuilder dataString = new StringBuilder(length);
int totalBytesRead = 0;
while(!end) {
int currentBytesRead = in.read(messageByte);
totalBytesRead = currentBytesRead + totalBytesRead;
if(totalBytesRead <= length) {
dataString
.append(new String(messageByte, 0, currentBytesRead, StandardCharsets.UTF_8));
} else {
dataString
.append(new String(messageByte, 0, length - totalBytesRead + currentBytesRead,
StandardCharsets.UTF_8));
}
if(dataString.length()>=length) {
end = true;
}
}
}
4. Client Code to Send Data
4.发送数据的客户端代码
And what about the client side code? Actually, it’s quite simple:
而客户端的代码呢?事实上,这很简单。
char type = 's'; // s for string
String data = "This is a string of length 29";
byte[] dataInBytes = data.getBytes(StandardCharsets.UTF_8);
out.writeChar(type);
out.writeInt(dataInBytes.length);
out.write(dataInBytes);
That’s all our client is doing!
这就是我们的客户所做的一切!
5. Conclusion
5.总结
In this article, we discussed how to read data from a socket. We looked at different functions which help us to read data of a particular type. Also, we saw how to read binary data.
在这篇文章中,我们讨论了如何从一个套接字中读取数据。我们看了不同的函数,它们帮助我们读取特定类型的数据。此外,我们还看到了如何读取二进制数据。
The full implementation of this tutorial can be found on GitHub.