UDP 客户端收不到字节
UDP client does not receive bytes
这个问题已经被问了很多,但到目前为止,我从以前的答案中应用的解决方案中 none 对我有所帮助。
主要目标
我正在尝试学习 UDP 连接,这是我的尝试。我想让客户端通过 UDP 在服务器上请求图片,服务器将发送它。然后客户端将使用给定的信息创建一个文件。
说明
我的主要想法是使用 "GET" 命令(不是 HTTP,只是 GET)向服务器请求图像,后跟图像名称(包括扩展名)。然后客户端等待一个答案,这是请求的图像。
问题
客户等着没来的回答
研究
另一个类似的问题是我在接收和连接时使用相同的端口,所以我添加了两个端口,receivingPORT 和 sendingPORT,客户端没有结果。
从其他类似问题来看,是防火墙的问题。因此,在 Win10 机器上,我在防火墙中为我用于此应用程序的端口创建了一个新的 UDP 规则,但客户端没有收到任何东西...
我已经检查图像是否已加载到 byte[]
并且图像已发送。但是在客户端上,没有收到任何东西并停留在那里等待连接通过
来自服务器的代码
public class UDPserver {
static DatagramSocket serverUDP;
static DatagramPacket packet;
static InetAddress address;
static byte[] buffer = new byte[65507];//65507
final static int receivingPORT = 6668;
final static int sendingPORT = 6669;
public static void main(String[] args) throws SocketException, IOException, InterruptedException{
boolean serverActive = true;
String order = "";
String file = "";
//Instantiate server
serverUDP = new DatagramSocket(receivingPORT);
while(serverActive){
//Kind of packet we want to receive
packet = new DatagramPacket(buffer, buffer.length);
System.out.println("Server awaiting connection...");
//Receive it
serverUDP.receive(packet);
System.out.println("Received packet from: " + packet.getAddress() + "/" + packet.getPort());
//What does the packet contain?
String msg = new String(packet.getData());
address = packet.getAddress();
System.out.println("Order from: " + address + "/" + receivingPORT + " says: " + msg);
try{
order = msg.split(" ")[0].trim();
file = msg.split(" ")[1].trim();
} catch (Exception e){
}
switch(order){
case("GET"):{
System.out.println("Sending back an image...");
buffer = loadImageFromServer(file);
packet = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
Thread.sleep(5000);
serverUDP.send(packet);
System.out.println("Client served");
break;
}
case("DISCONNECT"):{
buffer = "Server is disconnecting...".getBytes();
packet = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
serverUDP.send(packet);
serverActive = false;
serverUDP.close();
break;
}
}
}
}
static byte[] loadImageFromServer(String path) {
try {
System.out.println("Loading path: " + path);
//Instantiate a buffer from the image for it
BufferedImage img = ImageIO.read(UDPserver.class.getResource(path));
//Create a byte[] stream object to handle the data
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//Write the image data into those above with jpg format
ImageIO.write(img, "png", baos);
//Flush the information
baos.flush();
byte[] buffer = baos.toByteArray(); //Write it out on a byte string and return it
return buffer;
} catch (IOException ex) {
Logger.getLogger(UDPserver.class.getName()).log(Level.SEVERE, null, ex.fillInStackTrace());
System.exit(-1);
}
return null;
}
}
CODE 客户端
public class Client {
static DatagramSocket clientUDP;
static InetAddress address;
static DatagramPacket packetSend;
static DatagramPacket packetReceive;
static int SIZE = 65507;
final static int receivingPORT = 6669;
final static int sendingPORT = 6668;
static byte[] buffer = new byte[SIZE];
static Scanner scan = new Scanner(System.in);
public static void main(String[] args) throws SocketException, UnknownHostException, IOException{
boolean clientLoop = true;
//Get address
address = InetAddress.getByName("localhost");
//Instantiate Client -> UDP
clientUDP = new DatagramSocket();
while(clientLoop){
System.out.print("Enter any key and press enter");
scan.next(); //Just to stop the loop
//Load the buffer
buffer = "GET imagenServidor.png".getBytes();
//buffer = "DISCONNECT".getBytes();
System.out.println("Buffer is ready");
//Arm the packet
packetSend = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
System.out.println("Packet is armed!");
//Send the packet to the server
clientUDP.send(packetSend);
System.out.println("Order sent to server");
System.out.println("Waiting an answer");
packetReceive = new DatagramPacket(buffer, buffer.length, address, receivingPORT);
clientUDP.receive(packetReceive);
System.out.println("Server answered!");
ByteArrayInputStream bais = new ByteArrayInputStream(packetReceive.getData());
BufferedImage image = ImageIO.read(bais);
System.out.println(image);
}
clientUDP.close();
}
}
注意事项
- 这是一个 UDP 练习
首先,我觉得你在调试的时候需要学习如何使用wirshark或者tcmpdump来分析网络流,这将有助于你发现问题并解决问题。
关于你的程序,user207421提到的几个问题。我觉得用TCP比较好,但是如果你想通过这种方式学习UDP,你需要的是自己做一个slim可靠的UDP。
例如,您可能需要以下型号
建立发送缓冲区和接收缓冲区,每次检查缓冲区是否为空,如果没有,send/receive并处理。(因为UDP有MTU)
在每个数据报的头部添加一些额外的格式信息,包括整个报文的大小,数据报的顺序,左边的大小等(因为需要裁剪你的消息分为很多部分)
搭建controller,需要有重传、重建消息等功能(因为UDP不可靠,需要检查各个部分的完整性)
希望对您有所帮助。
原因
MTU!
您正在通过 UDP 直接发送具有长缓冲区的数据包,这在大多数网络环境下可能不起作用。
通过UDP发送的数据包不能超过网络MTU,否则会被丢弃。大多数网络节点(routers/switchs/hosts...)上的网络 MTU 可能不会超过 1500,有时甚至更小。虽然有些 nods 可能会对 ip 数据包进行 sigmentation,但是当你使用 UDP 时,你不应该指望它。
建议
在此应用中改用TCP,至于:
您正在发送预期完整的数据(否则将毫无用处)。
你不关心拥塞控制算法。
所以只用 TCP。
根据问题的更新进行编辑
因此,由于这是一个练习,您只能在其中使用 UDP。
文件不完整就没有用,您必须确保:
- 所有数据包都可以通过该路径。这意味着网络应该在物理上和虚拟上都连接,并且数据包大小应始终小于 MTU。
- 如果任何数据包丢失,接收方和发送方都应该能够知道。
- 如果任何 apckets 出问题,接收者应该能够知道。
- 发送方应该能够缓存并重新发送尚未被接收方确认的数据包。
确保您的网络连接良好。将图像缓冲区拆分为缓冲区数组,每个缓冲区项长度小于 1000 字节(应该是安全的)。
那么让我们为此设计一个成熟但简单的协议:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| type | sequence number |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| payload ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
对于类型,我们可能需要:
- 你好:0x01
- 再见:0x02
- 确认:0x03
- nack: 0x04
- 数据:0x05
- 反馈:0x06
- ...
序列应该是单增的。例如1, 2, 3, 4....(不必从 1 开始但可以)
它的工作原理如下:
Sender->Receiver: hello(seq=i)
Receiver->Sender: ack(seq=i)
# Sender->Receiver: hello(seq=i)
# if timeout and got no ack for seq=i
Sender->Receiver: data(seq=i+1)
Receiver->Sender: ack(seq=i+1)
# Sender->Receiver: hello(seq=i+1)
# if timeout and got no ack for seq=i+1
Sender->Receiver: data(seq=i+2)
Sender->Receiver: data(seq=i+3)
Receiver->Sender: ack(seq=i+2)
Receiver->Sender: ack(seq=i+3)
# Sender->Receiver: hello(seq=i+2)
# if timeout and got no ack for seq=i+2 or got nack for seq=i+2
Sender->Receiver: bye(seq=n)
Receiver->Sender: ack(seq=n)
# bye is not necessory
这个问题已经被问了很多,但到目前为止,我从以前的答案中应用的解决方案中 none 对我有所帮助。
主要目标
我正在尝试学习 UDP 连接,这是我的尝试。我想让客户端通过 UDP 在服务器上请求图片,服务器将发送它。然后客户端将使用给定的信息创建一个文件。
说明
我的主要想法是使用 "GET" 命令(不是 HTTP,只是 GET)向服务器请求图像,后跟图像名称(包括扩展名)。然后客户端等待一个答案,这是请求的图像。
问题
客户等着没来的回答
研究
另一个类似的问题是我在接收和连接时使用相同的端口,所以我添加了两个端口,receivingPORT 和 sendingPORT,客户端没有结果。
从其他类似问题来看,是防火墙的问题。因此,在 Win10 机器上,我在防火墙中为我用于此应用程序的端口创建了一个新的 UDP 规则,但客户端没有收到任何东西...
我已经检查图像是否已加载到 byte[]
并且图像已发送。但是在客户端上,没有收到任何东西并停留在那里等待连接通过
来自服务器的代码
public class UDPserver {
static DatagramSocket serverUDP;
static DatagramPacket packet;
static InetAddress address;
static byte[] buffer = new byte[65507];//65507
final static int receivingPORT = 6668;
final static int sendingPORT = 6669;
public static void main(String[] args) throws SocketException, IOException, InterruptedException{
boolean serverActive = true;
String order = "";
String file = "";
//Instantiate server
serverUDP = new DatagramSocket(receivingPORT);
while(serverActive){
//Kind of packet we want to receive
packet = new DatagramPacket(buffer, buffer.length);
System.out.println("Server awaiting connection...");
//Receive it
serverUDP.receive(packet);
System.out.println("Received packet from: " + packet.getAddress() + "/" + packet.getPort());
//What does the packet contain?
String msg = new String(packet.getData());
address = packet.getAddress();
System.out.println("Order from: " + address + "/" + receivingPORT + " says: " + msg);
try{
order = msg.split(" ")[0].trim();
file = msg.split(" ")[1].trim();
} catch (Exception e){
}
switch(order){
case("GET"):{
System.out.println("Sending back an image...");
buffer = loadImageFromServer(file);
packet = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
Thread.sleep(5000);
serverUDP.send(packet);
System.out.println("Client served");
break;
}
case("DISCONNECT"):{
buffer = "Server is disconnecting...".getBytes();
packet = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
serverUDP.send(packet);
serverActive = false;
serverUDP.close();
break;
}
}
}
}
static byte[] loadImageFromServer(String path) {
try {
System.out.println("Loading path: " + path);
//Instantiate a buffer from the image for it
BufferedImage img = ImageIO.read(UDPserver.class.getResource(path));
//Create a byte[] stream object to handle the data
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//Write the image data into those above with jpg format
ImageIO.write(img, "png", baos);
//Flush the information
baos.flush();
byte[] buffer = baos.toByteArray(); //Write it out on a byte string and return it
return buffer;
} catch (IOException ex) {
Logger.getLogger(UDPserver.class.getName()).log(Level.SEVERE, null, ex.fillInStackTrace());
System.exit(-1);
}
return null;
}
}
CODE 客户端
public class Client {
static DatagramSocket clientUDP;
static InetAddress address;
static DatagramPacket packetSend;
static DatagramPacket packetReceive;
static int SIZE = 65507;
final static int receivingPORT = 6669;
final static int sendingPORT = 6668;
static byte[] buffer = new byte[SIZE];
static Scanner scan = new Scanner(System.in);
public static void main(String[] args) throws SocketException, UnknownHostException, IOException{
boolean clientLoop = true;
//Get address
address = InetAddress.getByName("localhost");
//Instantiate Client -> UDP
clientUDP = new DatagramSocket();
while(clientLoop){
System.out.print("Enter any key and press enter");
scan.next(); //Just to stop the loop
//Load the buffer
buffer = "GET imagenServidor.png".getBytes();
//buffer = "DISCONNECT".getBytes();
System.out.println("Buffer is ready");
//Arm the packet
packetSend = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
System.out.println("Packet is armed!");
//Send the packet to the server
clientUDP.send(packetSend);
System.out.println("Order sent to server");
System.out.println("Waiting an answer");
packetReceive = new DatagramPacket(buffer, buffer.length, address, receivingPORT);
clientUDP.receive(packetReceive);
System.out.println("Server answered!");
ByteArrayInputStream bais = new ByteArrayInputStream(packetReceive.getData());
BufferedImage image = ImageIO.read(bais);
System.out.println(image);
}
clientUDP.close();
}
}
注意事项
- 这是一个 UDP 练习
首先,我觉得你在调试的时候需要学习如何使用wirshark或者tcmpdump来分析网络流,这将有助于你发现问题并解决问题。
关于你的程序,user207421提到的几个问题。我觉得用TCP比较好,但是如果你想通过这种方式学习UDP,你需要的是自己做一个slim可靠的UDP。
例如,您可能需要以下型号
建立发送缓冲区和接收缓冲区,每次检查缓冲区是否为空,如果没有,send/receive并处理。(因为UDP有MTU)
在每个数据报的头部添加一些额外的格式信息,包括整个报文的大小,数据报的顺序,左边的大小等(因为需要裁剪你的消息分为很多部分)
搭建controller,需要有重传、重建消息等功能(因为UDP不可靠,需要检查各个部分的完整性)
希望对您有所帮助。
原因
MTU!
您正在通过 UDP 直接发送具有长缓冲区的数据包,这在大多数网络环境下可能不起作用。
通过UDP发送的数据包不能超过网络MTU,否则会被丢弃。大多数网络节点(routers/switchs/hosts...)上的网络 MTU 可能不会超过 1500,有时甚至更小。虽然有些 nods 可能会对 ip 数据包进行 sigmentation,但是当你使用 UDP 时,你不应该指望它。
建议
在此应用中改用TCP,至于:
您正在发送预期完整的数据(否则将毫无用处)。
你不关心拥塞控制算法。
所以只用 TCP。
根据问题的更新进行编辑
因此,由于这是一个练习,您只能在其中使用 UDP。
文件不完整就没有用,您必须确保:
- 所有数据包都可以通过该路径。这意味着网络应该在物理上和虚拟上都连接,并且数据包大小应始终小于 MTU。
- 如果任何数据包丢失,接收方和发送方都应该能够知道。
- 如果任何 apckets 出问题,接收者应该能够知道。
- 发送方应该能够缓存并重新发送尚未被接收方确认的数据包。
确保您的网络连接良好。将图像缓冲区拆分为缓冲区数组,每个缓冲区项长度小于 1000 字节(应该是安全的)。
那么让我们为此设计一个成熟但简单的协议:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| type | sequence number |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| payload ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
对于类型,我们可能需要:
- 你好:0x01
- 再见:0x02
- 确认:0x03
- nack: 0x04
- 数据:0x05
- 反馈:0x06
- ...
序列应该是单增的。例如1, 2, 3, 4....(不必从 1 开始但可以)
它的工作原理如下:
Sender->Receiver: hello(seq=i)
Receiver->Sender: ack(seq=i)
# Sender->Receiver: hello(seq=i)
# if timeout and got no ack for seq=i
Sender->Receiver: data(seq=i+1)
Receiver->Sender: ack(seq=i+1)
# Sender->Receiver: hello(seq=i+1)
# if timeout and got no ack for seq=i+1
Sender->Receiver: data(seq=i+2)
Sender->Receiver: data(seq=i+3)
Receiver->Sender: ack(seq=i+2)
Receiver->Sender: ack(seq=i+3)
# Sender->Receiver: hello(seq=i+2)
# if timeout and got no ack for seq=i+2 or got nack for seq=i+2
Sender->Receiver: bye(seq=n)
Receiver->Sender: ack(seq=n)
# bye is not necessory