udp接收数据java线程_java – 数据报套接字在我的UDP程序中饿死其他线程

作为课程作业的一部分,我们的任务是在UDP层上提供额外的可靠性层,以便发送大图片文件.这是使用Go-Back-N协议:http://en.wikipedia.org/wiki/Go_back_N完成的

根据我的理解,这个问题的关键在于能够发送数据包,同时检查是否有任何致谢已经进入旧数据包,这将允许您移动窗口.

我目前通过两个线程执行此操作:如果窗口中有空间,则发送下一个数据包;并且不断地听取任何传入的确认并做出适当的反应.

我的问题是该程序应该是线程化的,就好像这两个线程同时动作一样,但事实上,似乎ACKReceiver线程正在获得非常不成比例的时间.从线程转储中,当它到达DataSocket.receive()行时,它似乎“饿死”了发送线程一段时间,阻止了执行,而没有让其他线程有机会在此期间运行.

我已经看过以下问题,似乎暗示问题与DatagramSocket.receive同步的事实有关……但是没有提供问题的可用解决方案:

这是我的代码的发件人部分的代码,我相对确定另一方的接收器是完全正常的(一方面,我没有必要使用任何线程来使其工作!):

import java.io.*;

import java.net.*;

import java.nio.ByteBuffer;

public class Sender3 {

short base = 0;

short nextSeqNum = 0;

DatagramPacket[] packets;

ByteBuffer bb;

String endSys;

int portNum;

String fileName;

int retryTime;

int windowSize;

DatagramSocket clientSocket;

InetAddress IPAddress;

boolean timedOut = false;

public Sender3(String endSys, int portNum, String fileName, int retryTime, int windowSize){

this.endSys = endSys;

this.portNum = portNum;

this.fileName = fileName;

this.retryTime = retryTime;

this.windowSize = windowSize;

}

public static void main(String args[]) throws Exception{

//Check for current arguments and assign them

if(args.length != 5){

System.out.println("Invalid number of arguments. Please specify: ");

System.exit(1);

}

Sender3 sendy = new Sender3(args[0], Integer.parseInt(args[1]), args[2], Integer.parseInt(args[3]), Integer.parseInt(args[4]));

sendy.go();

}

private void go() throws Exception{

clientSocket = new DatagramSocket();

bb = ByteBuffer.allocate(2);

byte[] picData = new byte[1021];

byte[] sendData = new byte[1024];

Thread.yield()

short seqNum = 0;

byte[] seqBytes = new byte[2];

byte EOFFlag = 0;

boolean acknowledged = false;

int lastPacketRetrys = 0;

int resends = 0;

IPAddress = InetAddress.getByName(endSys);

FileInputStream imReader = new FileInputStream(new File(fileName));

double fileSizeKb = imReader.available() / 1021.0; //We add 3 bytes to every packet, so dividing by 1021 will give us total kb sent.

int packetsNeeded = (int) Math.ceil(fileSizeKb);

packets = new DatagramPacket[packetsNeeded];

long startTime = System.currentTimeMillis();

long endTime;

double throughput;

//Create array of packets to send

for(int i = 0; i < packets.length; i++){

if(i == packets.length - 1){

EOFFlag = 1;

picData = new byte[imReader.available()];

sendData = new byte[picData.length + 3];

}

imReader.read(picData);

bb.putShort((short)i);

bb.flip();

seqBytes = bb.array();

bb.clear();

System.arraycopy(seqBytes, 0, sendData, 0, seqBytes.length);

sendData[2] = EOFFlag;

System.arraycopy(picData, 0, sendData, 3, picData.length);

packets[i] = new DatagramPacket((byte[])sendData.clone(), sendData.length, IPAddress, portNum);

}

ACKGetter ackGet = new ACKGetter();

Thread ackThread = new Thread(ackGet);

ackThread.start();

//System.out.println("timeout is: " + timedOut + " base is: " + base + " packet length is: " + packets.length + " nextSeqNum: " + nextSeqNum);

while(base != packets.length){

if(timedOut){

//System.out.println("Timed out waiting for acknowledgement, resending all unACKed packets in window");

clientSocket.setSoTimeout(retryTime);

resends++;

if(nextSeqNum == packets.length)

lastPacketRetrys++;

//Resend all packets in window

for (int i = base; i < nextSeqNum; i++){

// System.out.println("Resending packets with number: " + i);

clientSocket.send(packets[i]);

}

timedOut = false;

}

if(nextSeqNum - base < windowSize && nextSeqNum < packets.length){

//System.out.println("sending packet with seqNum: " + nextSeqNum);

clientSocket.send(packets[nextSeqNum]);

if(base == nextSeqNum){

clientSocket.setSoTimeout(retryTime);

}

nextSeqNum++;

}

else{

//Thread.yield();

}

}

if(lastPacketRetrys > 10){

System.out.println("Last packet ACK was lost (we think). So we just gave up, number of retransmissions will probably be higher");

}

endTime = System.currentTimeMillis();

throughput = 1000 * fileSizeKb / (endTime - startTime);

clientSocket.close();

imReader.close();

System.out.println("Number of retransmissions: " + resends);

System.out.println("Average throughput is: " + throughput + "Kb/s");

}

private class ACKGetter implements Runnable {

//Listen out for ACKs and update pointers accordingly

DatagramPacket ackPacket;

byte[] ackData = new byte[2];

public void run() {

while(base != packets.length){

if(base != nextSeqNum){

try{

ackPacket = new DatagramPacket(ackData, ackData.length);

clientSocket.receive(ackPacket);

ackData = ackPacket.getData();

bb.put(ackData[0]);

bb.put(ackData[1]);

bb.flip();

short ack = bb.getShort();

bb.clear();

if(base <= ack){

//System.out.println("acknowledgement for base num: " + base + "ack num:" + ack);

base = (short) (ack + 1);

//If theres nothing left in window, stop timing, otherwise restart the timer

if(base == nextSeqNum){

clientSocket.setSoTimeout(0);

}

else{

clientSocket.setSoTimeout(retryTime);

}

}

else{

//System.out.println("ACK didnt change anything: " + ack);

}

}

catch(Exception ex){

timedOut = true;

//System.out.println("Packet timed out...resending..");

}

}

Thread.yield();

}

}

}

}

解决方法:

我认为你在这里遇到了死锁,因为读者线程在clientSocket.receive()中,而发送者调用clientSocket.setSoTimeout().请参阅以下DatagramSocket方法定义:

public synchronized void setSoTimeout(int timeout) throws SocketException {

...

public synchronized void receive(DatagramPacket p) throws IOException {

如果您收到的套接字超时为0,则接收挂起等待数据包.如果您发出SIGQUIT,您的JVM将转储线程并显示死锁,您可以跟踪堆栈帧以查看发送方和接收方卡在哪里.

要解决此问题,您应该停止更改setSoTimeout值,这对我来说听起来非常糟糕.我会切换到使用DatagramChannel,使套接字无阻塞,并使用NIO接收来进行读取.有关如何使用通道选择器的更多信息,请参阅NIO docs.

标签:java,multithreading,udp,go-back-n

来源: https://codeday.me/bug/20190902/1790318.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值