#java #udp #datagram
#java #udp #дейтаграмма
Вопрос:
У меня есть программа клиент-сервер, пытающаяся отправить файл jpg.
Я пытаюсь добавить байт ПОДТВЕРЖДЕНИЯ и байт номера блока ДАННЫХ в UDP-пакет. У меня возникли проблемы с этой частью кода (этот раздел кода представляет собой всего лишь код обработки блоков данных из клиентской программы):
if(pType == TftpUtil.DATA){ //If byte 0 = 2 meaning it's a DATA block.
private int blockNumber = 1;
byte[] bn = intToBytes(blockNumber);
ByteBuffer buff = ByteBuffer.allocate(8); // For 2 ints, an int is 4 bytes long
buff.put(TftpUtil.ACK); // This is an "ACKNOWLEDGE byte", at position zero.
buff.put(bn); // This is supposed to be a byte containing the integer "blockNumber" meaning, what DATA block in the series it is.
buff.rewind(); // Not sure what this does. What does this do? Saw it in the code I copied.
DatagramPacket dataPacket = new DatagramPacket(buff.array(), buff.limit(), echoServerIP, 69); //Create datagramPacket
System.out.println("TftpClient sending a ACK on port " clientSocket.getLocalPort());
clientSocket.send(dataPacket);
blockNumber ;
}
Приведенный выше код не работает с сервером, который у меня есть, и сервер был создан Учителем. Я проверил код сервера, выглядит отлично.
По какой-то причине он зависает в той части, где предполагается отправить байт ПОДТВЕРЖДЕНИЯ, содержащий целое число, «3», что означает «Подтверждено». И предполагается, что он должен содержать этот байт ACK в нулевой позиции buff.array(). Но сервер говорит, что не получен подтверждающий байт.
TftpUtil начинается так:
public class TftpUtil {
//read request packet
public static final byte RRQ = 1;
//data packet
public static final byte DATA = 2;
//ack packet
public static final byte ACK = 3;
//error packet
public static final byte ERROR = 4;
//the maximum number of resent
public static final int MAX_RESEND = 5;
//data buffer size: 512
public static int DATA_BUFFER_SIZE = 512;
//packet buffer size: 2 bytes header 512 bytes file data;
public static int PACKET_BUFFER_SIZE = DATA_BUFFER_SIZE 2;
}
Сервер отправляет файл .jpg в блоках данных по 512 байт. Сервер обнаруживает, что установлено соединение, и пытается отправить файл. Он отправляет первый блок данных, который, кажется, проходит, потому что, когда я проверяю папку, появляется asd.jpg файл размером 512 байт. Но когда клиент отправляется, чтобы отправить байт подтверждения обратно, сервер сообщает, что он неверен, и истекает время ожидания.
Можете ли вы увидеть, что не так с кодом, который я вставил, или мне следует вставить больше кода?
Спасибо.
ОБНОВИТЬ полный код сервера:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class server_v004 {
private InetAddress serverAddress;
private int serverPort;
private DatagramSocket serverSocket;
private static final ExecutorService exec = Executors.newFixedThreadPool(10);
public void startServer()
{
try {
String serverIpAddress = "192.168.1.32";
InetAddress echoServerIP = InetAddress.getByName(serverIpAddress);
serverSocket = new DatagramSocket(serverPort, echoServerIP);
System.out.println("TftpServer on port " serverSocket.getLocalPort());
while(true) {
byte[] buf = new byte[1472];
DatagramPacket p = new DatagramPacket(buf, 1472);
System.out.println("waiting for connection ....");
serverSocket.receive(p);
server_v004ServerWorker worker = new server_v004ServerWorker(p);
exec.execute(worker);
}
}
catch(Exception e) {
System.err.println("Exception: " e);
}
serverSocket.close();
return;
}
public static void main(String[] args) {
server_v004 tftpServer = new server_v004();
//tftpServer.checkArgs(args);
try{
tftpServer.parseArgsAndInit(args);
tftpServer.startServer();
}
catch(Exception e){
e.printStackTrace();
}
}
private void checkArgs(String[] args){
if(args.length <0) { //<1 //<2
System.out.println("Usage: TftpServer"); //server_port server_ip
System.exit(0);
}
}
private void parseArgsAndInit(String[] args) throws Exception{
//serverAddress = InetAddress.getByName(args[0]);
serverPort = 12345; //Integer.parseInt(args[1]);
}
}
class server_v004ServerWorker implements Runnable
{
//use to store RRQ request
private DatagramPacket req;
//client address who sent the RRQ request
private SocketAddress clientAddress;
//socket used to send the file data packets to client
private DatagramSocket sendfileSocket;
//byte buffer to store the file data
private byte[] dataBuffer = new byte[TftpUtil.DATA_BUFFER_SIZE];
//the first block sequence
private byte currentBlockSeq = 1;
//use to retrieve the ack packet, only two bytes long
private DatagramPacket ackDP = new DatagramPacket(new byte[2], 2);
private int TIME_OUT = 1000; //1 second
public void run(){
try{
sendfileSocket = new DatagramSocket(69);
System.out.println("TftpServer sending a file on port " sendfileSocket.getLocalPort());
byte pType = TftpUtil.checkPacketType(req);
clientAddress = req.getSocketAddress();
//checking if the first packet from client is a RRQ packet
if(pType == TftpUtil.RRQ){
String filename = getFileName(req);
System.out.println("Requested file name:" filename);
//if the file doesn't exist, send ERROR packet and close socket
if(!(new File(filename)).exists()) {
DatagramPacket errorDP = TftpUtil.packErrorPacket(filename);
errorDP.setSocketAddress(clientAddress);
sendfileSocket.send(errorDP);
}
else{
//the file does exist, send file
sendfile(filename);
}
}// end if
}catch(Exception e){
e.printStackTrace();
}
sendfileSocket.close();
return;
}
private void sendfile(String filename) throws Exception{
FileInputStream fileInput = new FileInputStream(filename);
while(true){
int rec = fileInput.read(dataBuffer);
//the file size is a multiple of 512, send empty packet
if(rec == -1){
sendDataPacket(new byte[0],0);
System.out.println("The last packet [0 byte in size]:#" currentBlockSeq);
break;
}
//send a file data packet
boolean successed = sendDataPacket(dataBuffer,rec);
//tried five times
if (!successed) {
System.out.println("Tried five times, give up");
System.exit(0);
}
// the last packet (the file size if not a multiple of 512)
if (rec < 512 amp;amp; rec > 0 ) {
System.out.println("The last packet [" rec " bytes in size]:#" currentBlockSeq);
break;
}
currentBlockSeq ;
}//while
fileInput.close();
}
//
private boolean sendDataPacket(byte[] databuffer,int length) throws Exception{
int resendCount = 0;
DatagramPacket dataPacket = packFileDataPacket(databuffer,length);
//try five times
while(resendCount < TftpUtil.MAX_RESEND){
try{
sendfileSocket.send(dataPacket);
sendfileSocket.setSoTimeout(TIME_OUT);
System.out.println("sent data block #" currentBlockSeq ", waiting for ack #" currentBlockSeq);
//ack arrives
sendfileSocket.receive(ackDP);
byte ackedBlockseq = TftpUtil.extractACKNumber(ackDP);
System.out.println("received ack #" ackedBlockseq);
if(ackedBlockseq != currentBlockSeq) {
//the acked block seq is not the seq of block sent
//ignore this ack and resend
resendCount ;
continue;
}
//this data packet has been acked, return
return true;
}//end of try
catch(SocketTimeoutException ste){
resendCount ;
System.out.println("timeout #" resendCount );
}
}//end of while
return false;
}
private DatagramPacket packFileDataPacket(byte[] dataBuffer, int length){
int packetLength = 2 length;//type (1) block seq (1) data length
ByteBuffer byteBuffer = ByteBuffer.allocate(packetLength);
byteBuffer.put(TftpUtil.DATA);//type
byteBuffer.put(currentBlockSeq);//block seq
byteBuffer.put(dataBuffer,0,length);//data
DatagramPacket dataPacket = new DatagramPacket(byteBuffer.array(), packetLength);
dataPacket.setSocketAddress(clientAddress);
return dataPacket;
}
private String getFileName(DatagramPacket dataDP){
byte[] data = dataDP.getData();
int dataLength = dataDP.getLength();
ByteBuffer byteBuffer = ByteBuffer.allocate(dataLength-1);
//remove the packet type (RRQ)
byteBuffer.put(data,1,dataLength-1);
return new String(byteBuffer.array());
}
public server_v004ServerWorker(DatagramPacket req)
{
this.req = req;
}
}
и полный код TftpUtil:
import java.net.DatagramPacket;
import java.nio.ByteBuffer;
public class TftpUtil {
//read request packet
public static final byte RRQ = 1;
//data packet
public static final byte DATA = 2;
//ack packet
public static final byte ACK = 3;
//error packet
public static final byte ERROR = 4;
//the maximum number of resent
public static final int MAX_RESEND = 5;
//data buffer size: 512
public static int DATA_BUFFER_SIZE = 512;
//packet buffer size: 2 bytes header 512 bytes file data;
public static int PACKET_BUFFER_SIZE = DATA_BUFFER_SIZE 2;
//return the type (RRQ, DATA, ACK or ERROR) of a packet
public static byte checkPacketType(DatagramPacket dataDP){
byte[] payload = dataDP.getData();
return payload[0];
}
//return a RRQ packet
public static DatagramPacket packRRQDatagramPacket(byte[] filename) throws Exception{
return packDatagramPacket(RRQ, filename);
}
//return a "file not found" error packet
public static DatagramPacket packErrorPacket(String filename) throws Exception{
String errorMessage = filename " not found";
return packDatagramPacket(ERROR, errorMessage.getBytes());
}
/*
* utility method that wrap a packet type, data into a DatagramPacket
*/
private static DatagramPacket packDatagramPacket(byte type, byte[] payload) throws Exception{
int dataLength = 1 payload.length;
ByteBuffer byteBuffer = ByteBuffer.allocate(dataLength);
byteBuffer.put(type);
byteBuffer.put(payload);
return new DatagramPacket(byteBuffer.array(), dataLength);
}
//return the ack number of a ACK packet
public static byte extractACKNumber(DatagramPacket ackDP){
byte[] payload = ackDP.getData();
return payload[1];
}
//print the string content of a ERROR packet
public static void printErrorString(DatagramPacket p){
byte[] data = p.getData();
int dataLength = p.getLength();
ByteBuffer byteBuffer = ByteBuffer.allocate(dataLength-1);
//ignore the packet type
byteBuffer.put(data,1, dataLength-1);
System.out.print(new String(byteBuffer.array()));
}
//return the block sequence of a data packet
public static byte extractBlockSeq(DatagramPacket dataDP){
byte[] payload = dataDP.getData();
if(payload.length <=1) return -1; //-1: no block sequence in data
int type = payload[0];
if(type == DATA){
return payload[1];
}
return -1; //-1: not a data packet
}
}
Комментарии:
1. Вероятно, мы могли бы использовать этот серверный код, просто чтобы посмотреть, что на самом деле ожидается. Мы также могли бы использовать сообщение об ошибке, которое выдается. Чтобы отладить это, настройте и клиент, и сервер на своей собственной машине, чтобы вы могли наблюдать, что делают оба конца. Вам нужно увидеть оба, или вы просто догадываетесь, в чем проблема.
2. Я могу видеть оба и запускать их локально. Должен ли я использовать wireshark, это было бы полезно?
3. Если это работает локально, вам следует поговорить со своим преподавателем. Проблема может быть на его или ее стороне. Можете ли вы найти пути ошибок, которые может использовать сервер? Какие сообщения об ошибках может выдавать сервер? Где вы можете найти эти ошибки?
4. О, говоря о локальном и удаленном: это UDP. Вы уверены, что пакет достигает сервера? Запуск Wireshark на стороне сервера может быть хорошей идеей, если есть вероятность, что эти пакеты просто отбрасываются.
5. Они оба находятся на одном и том же IP-адресе, передавая пакеты между сокетом 69 (сервер) и другим случайным сокетом (клиент).