是否可以在同一端口上仅使用1个UDPSocket进行发送/接收?

我正在尝试发送一个DatagramPacket,然后必须等待来自服务器的Acknowlegment,以便我知道是否必须重新发送相同的数据包或发送下一个数据包。

我在客户端使用相同的套接字,发送数据包并接收确认(ack),在服务器端使用相同的,另一个用于接收数据包的套接字,然后将确认发送给客户..

第一个问题是客户端正在发送数据包,服务器正在接收它,然后将确认发送到客户端,但客户端在接收确认数据包时阻塞。

我正在制作一些System.out.println以确定问题所在,但我无法找到解决此问题的方法。

第二个问题是服务器仍然总是接收数据,并且不要等待客户端发送内容,我检查了因为我得到了那些行(例如“得到长度为xxx的数据包”“ack与ackNr yyy一起发送”… “虽然客户端在发送第一个数据包后仍在阻塞,但始终打印在服务器端,因为它正在等待未收到的确认!

这是CLIENT的代码:

package blatt7; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.zip.CRC32; public class FileSender { String zielRechner; String filePath; InetAddress host; File file; FileInputStream fis; int readLength; int sequenceNr = 0; int receivedSeqNr = 1; static int port = 7777; int packetNr = 0; byte[] packet = new byte[1216]; byte[] data = new byte[1200]; byte[] header = new byte[16]; byte[] readLengthByte = new byte[4]; byte[] sequenceNrByte = new byte[4]; byte[] checksumByte = new byte[8]; byte[] ackBuffer = new byte[4]; CRC32 checksumCalculator = new CRC32(); DatagramPacket dp; DatagramPacket ackPacket; DatagramSocket sendSocket = null; //DatagramSocket ackSocket = null; static boolean ackReceived = true; public FileSender(String zielRechner, String filePath) throws UnknownHostException, FileNotFoundException { this.zielRechner = zielRechner; this.filePath = filePath; this.host = InetAddress.getByName(zielRechner); this.file = new File(filePath); fis = new FileInputStream(file); } public void sendFile() throws IOException { while((readLength = fis.read(data)) != -1) { if (sequenceNr == 1) sequenceNr = 0; else sequenceNr = 1; readLengthByte = intToBytes(readLength); sequenceNrByte = intToBytes(sequenceNr); for(int i=0; i<4; i++) { header[8+i] = readLengthByte[i]; } for(int i=0; i<4; i++) { header[12+i] =sequenceNrByte[i]; } int j=0; for (int i=0; i<packet.length; i++) { if (i < header.length) packet[i] = header[i]; else { packet[i] = data[j]; j++; } } checksumCalculator.reset(); checksumCalculator.update(packet,8,8+readLength); checksumByte = longToBytes(checksumCalculator.getValue()); for(int i=0; i < 8; i++) { packet[i] = checksumByte[i]; } dp = new DatagramPacket(packet, packet.length, host, port); while(receivedSeqNr == sequenceNr && ackReceived) { try { ackReceived = false; sendSocket = new DatagramSocket(); sendSocket.send(dp); sendSocket.setSoTimeout(10000); packetNr++; System.out.println("Packet sent with seqNr: " + sequenceNr + " and length: " + bytesToInt(readLengthByte, 0) + " - PACKET NR: " + packetNr); ackPacket = new DatagramPacket(ackBuffer, ackBuffer.length); System.out.println("TEST!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); sendSocket.receive(ackPacket); System.out.println("Receiving ACK!!"); ackReceived = true; ackBuffer = ackPacket.getData(); receivedSeqNr = bytesToInt(ackBuffer,0); System.out.println("got SequenceNr with receivedSeq-Nr: " + receivedSeqNr); } catch (SocketTimeoutException e) { e.printStackTrace(); break; } } } fis.close(); System.out.println("Transfer Completed Successfully!"); sendSocket.close(); } public static byte[] longToBytes(long value) { ByteBuffer buffer = ByteBuffer.allocate(8); buffer.putLong(value); return buffer.array(); } public static long bytesToLong(byte[] bytes, int index) { ByteBuffer buffer = ByteBuffer.allocate(8); buffer.put(bytes); buffer.flip();//need flip return buffer.getLong(index); } public static byte[] intToBytes(int value) { ByteBuffer buffer = ByteBuffer.allocate(4); buffer.putInt(value); return buffer.array(); } public static int bytesToInt(byte[] bytes, int index) { ByteBuffer buffer = ByteBuffer.allocate(4); buffer.put(bytes); buffer.flip();//need flip return buffer.getInt(index); } public static void main(String[] args) throws IOException,ClassNotFoundException { FileSender sender = new FileSender("localhost", "C:/Users/Kb/Desktop/Deepophile - Psychedelic Sessions.wav"); sender.sendFile(); } } 

这是SERVER的代码:

 package blatt7; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.util.zip.CRC32; public class FileReceiver { byte[] incomingBuffer; DatagramPacket incomingPacket; DatagramSocket receiveSocket; DatagramPacket ackPacket; int packetCounter = 0; int dataLength; int receivedSeqNr; long calculatedChecksum; long receivedChecksum; CRC32 checksumCalculator = new CRC32(); byte[] dataLengthByte = new byte[4]; byte[] receivedSeqNrByte = new byte[4]; byte[] receivedChecksumByte = new byte[8]; byte[] ackArray; public FileReceiver() throws SocketException { incomingBuffer = new byte[1500]; incomingPacket = new DatagramPacket(incomingBuffer, incomingBuffer.length); } public void receive() throws IOException { receiveSocket = new DatagramSocket(FileSender.port); receiveSocket.setSoTimeout(10000); System.out.println("Server socket created. Waiting for incoming data..."); while(true && FileSender.ackReceived) { receiveSocket.receive(incomingPacket); packetCounter++; for (int i=0; i <4; i++) { dataLengthByte[i] = incomingBuffer[8+i]; } dataLength = FileSender.bytesToInt(dataLengthByte,0); checksumCalculator.reset(); checksumCalculator.update(incomingBuffer, 8, dataLength+8); calculatedChecksum = checksumCalculator.getValue(); for (int i=0; i <4; i++) { receivedSeqNrByte[i] = incomingBuffer[12+i]; } receivedSeqNr = FileSender.bytesToInt(receivedSeqNrByte,0); for (int i=0; i <8; i++) { receivedChecksumByte[i] = incomingBuffer[i]; } long receivedChecksum = FileSender.bytesToLong(receivedChecksumByte,0); System.out.println("Got packet with checksum: " + receivedChecksum); System.out.println("Server-calculated checksum: " + calculatedChecksum); System.out.println("Got packet with seqNr: " + receivedSeqNr + " and length: " + dataLength); if (calculatedChecksum != receivedChecksum) { sendACK(receivedSeqNr); System.out.println("Packet have erros(s)! It must be sent another time!"); } else if(calculatedChecksum == receivedChecksum && receivedSeqNr == 1) { sendACK(0); System.out.println("SeqNr '0' sent"); } else if (calculatedChecksum == receivedChecksum && receivedSeqNr == 0) { sendACK(1); System.out.println("SeqNr '1' sent"); } } } public void sendACK(int seqNum) throws IOException { byte[] ackArray = FileSender.intToBytes(seqNum); ackPacket = new DatagramPacket(ackArray, ackArray.length, InetAddress.getByName("localhost"), FileSender.port); receiveSocket.send(ackPacket); } public static void main(String[] args) throws IOException,ClassNotFoundException { FileReceiver receiver = new FileReceiver(); receiver.receive(); } } 

您可以尝试执行它以查看问题所在…所以如果您有任何想法我怎么能解决这个问题,请告诉我!

非常感谢你!

谁能告诉我在哪里可以找到收到的文件? 或者我应该如何更改我的代码,以便我选择保存它的位置?

对的,这是可能的。 您的问题是发送ACK数据报时目标地址:端口错误。 您应该从接收到的DatagramPacket获取目标地址:端口,或者更简单地仍然只是将具有不同数据的数据报重用为ACK数据报。

服务器如何使用自己的端口发送到客户端? 您正在服务器端口上从服务器向客户端发送ACK,您应该从接收到的数据包中获取客户端的UDP端口并将数据发送到该端口。

编辑

将SendACK方法中的Server更改为:

 ackPacket = new DatagramPacket(ackArray, ackArray.length, InetAddress.getByName("localhost"), incomingPacket.getPort()); 

现在通过运行来分析代码。