void RUDPRecvBuffer::on_timer(uint64_t now_timer, uint32_t rtc)
{
	if(check_loss(now_timer, rtc))
	{
		recv_new_packet_ = false;
	}

	uint32_t rtc_threshold = core_min(20, rtc / 2);
	if(last_ack_ts_ + rtc_threshold <= now_timer && recv_new_packet_)
	{
		net_channel_->send_ack(first_seq_);
		set_send_last_ack_ts(now_timer);
	}

	//判断是否可以读
	if(!recv_data_.empty() && net_channel_ != NULL)
		net_channel_->on_read();
}
STATIC int
test_mod(test_pars_t *tp, pdu_t *pdu, iscsi_pdu_kind_t kind, int rxtx, int err)
{
	mod_desc_t *mod;
	uint32_t mpoff, off;
	int i, rc = 0, s;

	tp->pdu_count[kind][rxtx]++;
	tp->pdu_count[ANY_PDU][rxtx]++;

	do {
		if ((mod = TAILQ_FIRST(&tp->mods)) == NULL) {
			return check_loss(tp, rxtx);
		}
		if (mod->pars.which_pdu != ANY_PDU &&
		    mod->pars.which_pdu != kind) {
			return check_loss(tp, rxtx);
		}
		mpoff = mod->pars.pdu_offset;

		switch (mod->pars.which_offset) {
		case ABSOLUTE_ANY:
			off = tp->pdu_count[ANY_PDU][CNT_TX] +
				  tp->pdu_count[ANY_PDU][CNT_RX];
			break;
		case RELATIVE_ANY:
			off = (tp->pdu_count[ANY_PDU][CNT_TX] +
				   tp->pdu_count[ANY_PDU][CNT_RX]) -
				(tp->pdu_last[ANY_PDU][CNT_TX] + tp->pdu_last[ANY_PDU][CNT_RX]);
			break;

		case ABSOLUTE_PDUKIND:
			off = tp->pdu_count[kind][rxtx];
			break;
		case RELATIVE_PDUKIND:
			off = tp->pdu_count[kind][rxtx] - tp->pdu_last[kind][rxtx];
			break;

		case ABSOLUTE_TX:
			if (rxtx != CNT_TX)
				return check_loss(tp, rxtx);
			off = tp->pdu_count[ANY_PDU][CNT_TX];
			break;
		case RELATIVE_TX:
			if (rxtx != CNT_TX)
				return check_loss(tp, rxtx);
			off = tp->pdu_count[ANY_PDU][CNT_TX] -
				  tp->pdu_last[ANY_PDU][CNT_TX];
			break;

		case ABSOLUTE_RX:
			if (rxtx != CNT_RX)
				return check_loss(tp, rxtx);
			off = tp->pdu_count[ANY_PDU][CNT_RX];
			break;
		case RELATIVE_RX:
			if (rxtx != CNT_RX)
				return check_loss(tp, rxtx);
			off = tp->pdu_count[ANY_PDU][CNT_RX] -
				  tp->pdu_last[ANY_PDU][CNT_RX];
			break;

		default:
			/* bad offset - skip this entry */
			mpoff = off = 0;
			break;
		}

		DEB(1, ("test_mod: kind=%d, rxtx=%d, pdukind=%d, mpoff=%d, "
				"whichoff=%d, off=%d\n", kind, rxtx, mod->pars.which_pdu,
				mpoff, mod->pars.which_offset, off));

		if (!off || (mpoff != 0 && mpoff < off)) {
			/* This might happen in some cases. Just discard the modification. */
			s = splbio();
			TAILQ_REMOVE(&tp->mods, mod, link);
			splx(s);

			update_options(tp, mod);

			if (mod->pars.options & ISCSITEST_OPT_WAIT_FOR_COMPLETION) {
				mod->pars.status = ISCSI_STATUS_TEST_MODIFICATION_SKIPPED;
				wakeup(mod);
			}
			free(mod, M_TEMP);
		}
	} while (mpoff && mpoff < off);

	if (mpoff > off)
		return check_loss(tp, rxtx);

	DEB(1, ("test_mod: opt=%x, pdu_ptr=%x, num_mods=%d\n", mod->pars.options,
			(int) mod->pdu_ptr, mod->pars.num_pdu_mods));

	if (mod->pdu_ptr)
		test_get(pdu, mod, err);

	if (mod->pars.options & ISCSITEST_OPT_DISCARD_PDU)
		rc = 1;
	else if (check_loss(tp, rxtx))
		rc = 1;
	else if (mod->pars.num_pdu_mods) {
		if (!(mod->pars.options & ISCSITEST_OPT_MOD_PERMANENT)) {
			/*
             * Note: if the PDU is later resent, the unmodified one will be
             * used as resend_pdu restores the original io vector.
             */
			pdu->mod_pdu = pdu->pdu;
			pdu->io_vec[0].iov_base = &pdu->mod_pdu;
		}
		for (i = 0; i < mod->pars.num_pdu_mods; i++) {
			mod_pdu(pdu, &mod->mods[i]);
		}
	}

	if (rxtx == CNT_TX) {
		if (mod->pars.options & ISCSITEST_OPT_NO_RESPONSE_PDU) {
			ccb_t *ccb = pdu->owner;

			DEB(1, ("test_mod: No response expected, completing CCB %x\n",
					(int)ccb));

			if (ccb != NULL &&
				(ccb->disp == CCBDISP_WAIT || ccb->disp == CCBDISP_SCSIPI)) {
				/* simulate timeout */
				wake_ccb(ccb, ISCSI_STATUS_TIMEOUT);
			}
		}

		if ((mod->pars.options & ISCSITEST_SFLAG_UPDATE_FIELDS) &&
			mod->pars.num_pdu_mods) {
			connection_t *conn = pdu->connection;

			if (conn->HeaderDigest &&
				!(mod->pars.options & ISCSITEST_SFLAG_NO_HEADER_DIGEST))
				pdu->pdu.HeaderDigest = gen_digest(&pdu->pdu, BHS_SIZE);

			if (pdu->uio.uio_iovcnt > 1 && conn->DataDigest &&
				!(mod->pars.options & ISCSITEST_SFLAG_NO_DATA_DIGEST))
				pdu->data_digest = gen_digest_2(
						pdu->io_vec[1].iov_base,
						pdu->io_vec[1].iov_len,
						pdu->io_vec[2].iov_base,
						pdu->io_vec[2].iov_len);
		}
	}

	s = splbio();
	TAILQ_REMOVE(&tp->mods, mod, link);
	update_options(tp, mod);
	/* we've modified a PDU - copy current count into last count */
	memcpy(tp->pdu_last, tp->pdu_count, sizeof(tp->pdu_last));
	splx(s);

	if (mod->pars.options & ISCSITEST_OPT_WAIT_FOR_COMPLETION) {
		wakeup(mod);
	}
	if (mod->pars.options & ISCSITEST_KILL_CONNECTION) {
		kill_connection(tp->connection,
				ISCSI_STATUS_TEST_CONNECTION_CLOSED,
				NO_LOGOUT, TRUE);
	}
	free(mod, M_TEMP);

	return rc;
}