我正在尝试使用UDP将服务器进程中的序列化对象发送到Java中的客户端进程。问题是客户端在receive方法上被阻止。有人可以帮忙吗?!
这是发送对象的服务器代码:
ClientModel C1= new ClientModel(100,"Noor","Noor",38,38,"asd");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(C1);
oos.flush();
byte[] Buf= baos.toByteArray();
packet = new DatagramPacket(Buf, Buf.length, client, port);
socket.send(packet);
这是接收对象的客户端代码:
byte[] buffer = new byte[100000];
packet = new DatagramPacket(buffer, buffer.length );
socket.receive(packet);
System.out.println("packet received");
我只想接收能够重建的对象但我无法收到数据包。
答案 0 :(得分:13)
我最后不知道你想要完成什么,但使用UDP并不那么容易......主要原因在于DatagramPacket对象的描述:
数据报包用于实现 无连接数据包传递 服务。每条消息都来自 一台机器到另一台机器 关于其中包含的信息 包。从中发送多个数据包 一台机器到另一台机器可能会被路由 不同的,可能会到达任何 订购。数据包传递不是 保证。
使用udp时的一个很好的教程是http://download.oracle.com/javase/tutorial/networking/datagrams/clientServer.html
关于阻止:
从此接收数据报包 插座。当这个方法返回时, DatagramPacket的缓冲区已填充 收到的数据。数据报包 还包含发件人的IP地址, 以及发件人的端口号 机。
此方法将阻塞,直到数据报为止 接收。长度字段 数据报包对象包含 收到的消息的长度。如果 消息比数据包长 长度,消息被截断。
我没有真正测试它,但我很确定 - 根据描述 - datagramsocket.reseive函数将阻塞,直到数据包被填满(在你的情况下直到收到100000字节)。
我建议你从具有固定已知长度的datagrampacket开始,在那里传输实际有效负载的大小。类似的东西:
public static void main(String[] args) {
ClientModel c1 = new ClientModel ();
c1.data = 123;
c1.name = "test";
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(c1);
oos.flush();
// get the byte array of the object
byte[] Buf= baos.toByteArray();
int number = Buf.length;;
byte[] data = new byte[4];
// int -> byte[]
for (int i = 0; i < 4; ++i) {
int shift = i << 3; // i * 8
data[3-i] = (byte)((number & (0xff << shift)) >>> shift);
}
DatagramSocket socket = new DatagramSocket(1233);
InetAddress client = InetAddress.getByName("localhost");
DatagramPacket packet = new DatagramPacket(data, 4, client, 1234);
socket.send(packet);
// now send the payload
packet = new DatagramPacket(Buf, Buf.length, client, 1234);
socket.send(packet);
System.out.println("DONE SENDING");
} catch(Exception e) {
e.printStackTrace();
}
}
另一方面,你现在知道你的尺寸:
public static void main(String[] args) {
try {
DatagramSocket socket = new DatagramSocket(1234);
byte[] data = new byte[4];
DatagramPacket packet = new DatagramPacket(data, data.length );
socket.receive(packet);
int len = 0;
// byte[] -> int
for (int i = 0; i < 4; ++i) {
len |= (data[3-i] & 0xff) << (i << 3);
}
// now we know the length of the payload
byte[] buffer = new byte[len];
packet = new DatagramPacket(buffer, buffer.length );
socket.receive(packet);
ByteArrayInputStream baos = new ByteArrayInputStream(buffer);
ObjectInputStream oos = new ObjectInputStream(baos);
ClientModel c1 = (ClientModel)oos.readObject();
c1.print();
} catch(Exception e) {
e.printStackTrace();
}
}
使用的CientModel clas:
public class ClientModel implements Serializable{
private static final long serialVersionUID = -4507489610617393544L;
String name = "";
int data = 1;
void print() {
System.out.println(data +": " + name);
}
}
我测试了这段代码,它运行得很好。希望有所帮助(我从http://www.tutorials.de/java/228129-konvertierung-von-integer-byte-array.html得到了byte-To-int和around)
编辑:正如评论中所述,主要使用UDP通常是一个非常糟糕的主意,因为您不知道您的数据包是以正确的顺序接收,还是根本不知道。 UDP不保证。我没有做太多的udp编程,但你可以依赖的唯一部分(如果我理解正确)是,如果你得到一个数据包并且它适合数据报(65,527字节 - 见https://en.wikipedia.org/wiki/User_Datagram_Protocol)它将包含整个事物。因此,如果您不关心消息的来源以及您的对象是否适合数据报,那么您应该没问题。
Edit2:至于代码:不要按原样使用它。它只是一个例子,在UDP中你应该只有一种类型的数据包,而且这个数据包已知。那样你就不需要发送“大小”了。如果您使用上面显示的代码,并且丢弃了一个数据包,则下一个数据包将是错误的大小(即第一个数据包被丢弃,突然您正在检查有效负载的第一个字节以获得大小)。
答案 1 :(得分:1)
我没有真正测试它,但我很确定 - 基于 description - datagramsocket.reseive函数将阻塞 直到数据包被填满(在你的情况下,直到100000字节为止) 接收到的)。
这是错误的。接收函数将一直阻塞,直到收到一个数据报,该数据报可能小于缓冲区大小(通常是)。 packet.getLength()方法会告诉你它有多大。