示例#1
0
文件: v_stream.c 项目: laishi/verse
/**
 * \brief This function try to pack message that is going to be
 * sent in STREAM OPEN state
 *
 * \param[in] *C The pointer at context
 *
 * \return This function return 1, when there is something to send,
 * it returns -1, when there is nothing to send and it returns 0, when
 * there is some error
 */
int v_STREAM_pack_message(struct vContext *C)
{
	struct VS_CTX *vs_ctx = CTX_server_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VStreamConn *conn = CTX_current_stream_conn(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VMessage *s_message = CTX_s_message(C);
	struct Generic_Cmd *cmd, *fake_cmd;
	int ret = -1, queue_size = 0, buffer_pos = 0, prio_cmd_count, cmd_rank=0;
	int8 cmd_share;
	int16 prio, max_prio, min_prio;
	uint16 cmd_count, cmd_len, prio_win, swin, sent_size, tot_cmd_size;
	real32 prio_sum_high, prio_sum_low, r_prio;
	int is_fake_cmd_received = 0;

	/* Is here something to send? */
	if((v_out_queue_get_count(vsession->out_queue) > 0) ||
			(vsession->tmp_flags & SYS_CMD_NEGOTIATE_FPS))
	{

		/* Get current size of data in TCP outgoing buffer */
#ifdef __linux__
		if( ioctl(io_ctx->sockfd, SIOCOUTQ, &queue_size) == -1 ) {
			v_print_log(VRS_PRINT_ERROR,
					"ioctl(%d, SIOCOUTQ, ...): %s\n",
					io_ctx->sockfd,
					strerror(errno));
			return 0;
		}
#endif

		/* Compute, how many data could be added to the TCP stack? */
		swin = conn->socket_buffer_size - queue_size;

		buffer_pos = VERSE_MESSAGE_HEADER_SIZE;

		s_message->sys_cmd[0].cmd.id = CMD_RESERVED_ID;

		/* When negotiated and used FPS is different, then pack negotiate command
		 * for FPS */
		if(vsession->fps_host != vsession->fps_peer) {
			cmd_rank += v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank,
					CMD_CHANGE_L_ID, FTR_FPS, &vsession->fps_host, NULL);
		} else {
			if(vsession->tmp_flags & SYS_CMD_NEGOTIATE_FPS) {
				cmd_rank += v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank,
						CMD_CONFIRM_L_ID, FTR_FPS, &vsession->fps_peer, NULL);
				/* Send confirmation only once for received system command */
				vsession->tmp_flags &= ~SYS_CMD_NEGOTIATE_FPS;
			}
		}

		buffer_pos += v_pack_stream_system_commands(s_message, &io_ctx->buf[buffer_pos]);

		max_prio = v_out_queue_get_max_prio(vsession->out_queue);
		min_prio = v_out_queue_get_min_prio(vsession->out_queue);

		prio_sum_high = v_out_queue_get_prio_sum_high(vsession->out_queue);
		prio_sum_low = v_out_queue_get_prio_sum_low(vsession->out_queue);

		v_print_log(VRS_PRINT_DEBUG_MSG, "Packing prio queues, cmd count: %d\n",
				v_out_queue_get_count(vsession->out_queue));

		/* Go through all priorities and pick commands from priority queues */
		for(prio = max_prio; prio >= min_prio; prio--)
		{
			prio_cmd_count = v_out_queue_get_count_prio(vsession->out_queue, prio);

			if(prio_cmd_count > 0) {

				r_prio = v_out_queue_get_prio(vsession->out_queue, prio);

				/* Compute size of buffer that could be occupied by
				 * commands from this queue */
				if(prio >= VRS_DEFAULT_PRIORITY) {
					prio_win = ((swin - buffer_pos)*r_prio)/prio_sum_high;
				} else {
					prio_win = ((swin - buffer_pos)*r_prio)/prio_sum_low;
				}

				/* Debug print */
				v_print_log(VRS_PRINT_DEBUG_MSG, "Queue: %d, count: %d, r_prio: %6.3f, prio_win: %d\n",
						prio, prio_cmd_count, r_prio, prio_win);

				/* Get total size of commands that were stored in queue (sent_size) */
				sent_size = 0;
				tot_cmd_size = 0;

				while(prio_cmd_count > 0) {
					cmd_share = 0;
					cmd_count = 0;
					cmd_len = prio_win - sent_size;

					/* Pack commands from queues with high priority to the buffer */
					cmd = v_out_queue_pop(vsession->out_queue, prio, &cmd_count, &cmd_share, &cmd_len);
					if(cmd != NULL) {

						/* Is this command fake command? */
						if(cmd->id < MIN_CMD_ID) {
							if(cmd->id == FAKE_CMD_CONNECT_TERMINATE) {
								/* Close connection */
								struct VS_CTX *vs_ctx = CTX_server_ctx(C);
								if(vs_ctx != NULL) {
									vsession->stream_conn->host_state = TCP_SERVER_STATE_CLOSING;
								} else {
									vsession->stream_conn->host_state = TCP_CLIENT_STATE_CLOSING;
								}
							} else if(cmd->id == FAKE_CMD_FPS) {
								struct Fps_Cmd *fps_cmd = (struct Fps_Cmd*)cmd;
								/* Change value of FPS. It will be sent in negotiate command
								 * until it is confirmed be the peer (server) */
								vsession->fps_host = fps_cmd->fps;
							}
						} else {
							buffer_pos += tot_cmd_size = v_cmd_pack(&io_ctx->buf[buffer_pos], cmd, v_cmd_size(cmd), 0);
							if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
								printf("%c[%d;%dm", 27, 1, 32);
								v_cmd_print(VRS_PRINT_DEBUG_MSG, cmd);
								printf("%c[%dm", 27, 0);
							}
							sent_size += tot_cmd_size;
						}

						fake_cmd = v_cmd_fake_ack(cmd);

						if(fake_cmd != NULL) {
							is_fake_cmd_received = 1;
							/* Push command to the queue of incoming commands */
							v_in_queue_push(vsession->in_queue, fake_cmd);
							/* Print content of fake command */
							v_fake_cmd_print(VRS_PRINT_DEBUG_MSG, fake_cmd);
						}

						/* It is not necessary to put cmd to history of sent commands,
						 * when TCP is used. */
						v_cmd_destroy(&cmd);
						prio_cmd_count--;
					} else {
						break;
					}
				}
			}
		}

		s_message->header.len = io_ctx->buf_size = buffer_pos;
		s_message->header.version = VRS_VERSION;

		/* Pack header to the beginning of the buffer */
		v_pack_message_header(s_message, io_ctx->buf);

		/* Debug print of command to be send */
		if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
			v_print_send_message(C);
		}

		/* When pure ack packet caused adding some fake commands to the queue, then
		 * poke server data thread */
		if( (vs_ctx != NULL) && (is_fake_cmd_received == 1)) {
			sem_post(vs_ctx->data.sem);
		}

		ret = 1;

	}

	return ret;
}
示例#2
0
/**
 * \brief This function send packets in OPEN and CLOSEREQ state.
 */
int send_packet_in_OPEN_CLOSEREQ_state(struct vContext *C)
{
	struct VDgramConn *vconn = CTX_current_dgram_conn(C);
	struct IO_CTX *io_ctx = CTX_io_ctx(C);
	struct VSession *vsession = CTX_current_session(C);
	struct VPacket *s_packet = CTX_s_packet(C);
	struct VSent_Packet *sent_packet = NULL;
	unsigned short buffer_pos = 0;
	struct timeval tv;
	int ret, keep_alive_packet = -1, full_packet = 0;
	int error_num;
	uint16 swin, prio_win, sent_size = 0;
	uint32 rwin;
	int cmd_rank = 0;

	/* Verse packet header */
	s_packet->header.version = 1;

	/* Clear header flags */
	s_packet->header.flags = 0;

	/* Check if it is necessary to send payload packet */
	ret = check_pay_flag(C);
	if(ret!=0) {
		s_packet->header.flags |= PAY_FLAG;
		if(ret==2) {
			keep_alive_packet = 1;
		}
	}

	/* When server is in CLOSEREQ state, then FIN flag should be set up */
	if(vconn->host_state == UDP_SERVER_STATE_CLOSEREQ) {
		s_packet->header.flags |= FIN_FLAG;
	}

	/* Check if it is necessary to send acknowledgment of received payload
	 * packet */
	ret = check_ack_nak_flag(C);
	if(ret==1) {
		s_packet->header.flags |= ACK_FLAG;

		/* Update last acknowledged Payload packet */
		vconn->last_acked_pay = vconn->last_r_pay;

		/* Add ACK and NAK commands from the list of ACK and NAK commands to the
		 * packet (only max count of ACK and NAK commands could be added to
		 * the packet) */
		for(cmd_rank = 0;
				cmd_rank < vconn->ack_nak.count &&
				cmd_rank < MAX_SYSTEM_COMMAND_COUNT;
				cmd_rank++)
		{
			s_packet->sys_cmd[cmd_rank].ack_cmd.id = vconn->ack_nak.cmds[cmd_rank].id;
			s_packet->sys_cmd[cmd_rank].ack_cmd.pay_id = vconn->ack_nak.cmds[cmd_rank].pay_id;
		}
		s_packet->sys_cmd[cmd_rank].cmd.id = CMD_RESERVED_ID;

	}

	/* If there is no need to send Payload or AckNak packet, then cancel
	 * sending of packet */
	if(! ((s_packet->header.flags & PAY_FLAG) ||
			(s_packet->header.flags & ACK_FLAG)) ) return SEND_PACKET_CANCELED;

	s_packet->header.flags |= ANK_FLAG;
	s_packet->header.ank_id = vconn->ank_id;

	/* Compute current windows for flow control and congestion control */
	set_host_rwin(C);
	set_host_cwin(C);

	/* Set window of flow control that will sent to receiver */
	rwin = vconn->rwin_host >> vconn->rwin_host_scale;
	s_packet->header.window = (unsigned short)(rwin > 0xFFFF) ? 0xFFFF : rwin;
	/*printf("\t---real window: %d---\n", s_packet->header.window);*/

	/* Compute how many data could be sent to not congest receiver */
	rwin = vconn->rwin_peer - vconn->sent_size;

	/* Select smallest window for sending (congestion control window or flow control window)*/
	swin = (vconn->cwin < rwin) ? vconn->cwin : rwin;

	/* Set up Payload ID, when there is need to send payload packet */
	if(s_packet->header.flags & PAY_FLAG)
		s_packet->header.payload_id = vconn->host_id + vconn->count_s_pay;
	else
		s_packet->header.payload_id  = 0;

	/* Set up AckNak ID, when there are some ACK or NAK command in the packet */
	if(s_packet->header.flags & ACK_FLAG)
		s_packet->header.ack_nak_id = vconn->count_s_ack;
	else
		s_packet->header.ack_nak_id = 0;

	/* When negotiated and used FPS is different, then pack negotiate command
	 * for FPS */
	if(vsession->fps_host != vsession->fps_peer) {
		cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
				CMD_CHANGE_L_ID, FTR_FPS, &vsession->fps_host, NULL);
	} else {
		if(vsession->tmp_flags & SYS_CMD_NEGOTIATE_FPS) {
			cmd_rank += v_add_negotiate_cmd(s_packet->sys_cmd, cmd_rank,
					CMD_CONFIRM_L_ID, FTR_FPS, &vsession->fps_peer, NULL);
			/* Send confirmation only once for received system command */
			vsession->tmp_flags &= ~SYS_CMD_NEGOTIATE_FPS;
		}
	}

	v_print_send_packet(C);

	/* Fill buffer */
	buffer_pos += v_pack_packet_header(s_packet, &io_ctx->buf[buffer_pos]);
	buffer_pos += v_pack_dgram_system_commands(s_packet, &io_ctx->buf[buffer_pos]);

	/* When this is not pure keep alive packet */
	if(s_packet->header.flags & PAY_FLAG) {

		sent_packet = v_packet_history_add_packet(&vconn->packet_history, s_packet->header.payload_id);

		assert(sent_packet != NULL);

		if(keep_alive_packet != 1) {
			real32 prio_sum_high, prio_sum_low, r_prio;
			uint32 prio_count;
			int16 prio, max_prio, min_prio;
			uint16 tot_cmd_size;

			/* Print outgoing command with green color */
			if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%d;%dm", 27, 1, 32);
			}

			max_prio = v_out_queue_get_max_prio(vsession->out_queue);
			min_prio = v_out_queue_get_min_prio(vsession->out_queue);

			prio_sum_high = v_out_queue_get_prio_sum_high(vsession->out_queue);
			prio_sum_low = v_out_queue_get_prio_sum_low(vsession->out_queue);

			v_print_log(VRS_PRINT_DEBUG_MSG, "Packing prio queues, cmd count: %d\n", v_out_queue_get_count(vsession->out_queue));

			/* Go through all priorities and pick commands from priority queues */
			for(prio = max_prio; prio >= min_prio; prio--)
			{
				/* TODO: Add better check here */
				if(prio <= VRS_DEFAULT_PRIORITY && buffer_pos >= vconn->io_ctx.mtu) {
					break;
				}

				prio_count = v_out_queue_get_count_prio(vsession->out_queue, prio);

				if(prio_count > 0) {
					r_prio = v_out_queue_get_prio(vsession->out_queue, prio);

					/* Compute size of buffer that could be occupied by
					 * commands from this queue */
					if(prio >= VRS_DEFAULT_PRIORITY) {
						prio_win = ((swin - buffer_pos)*r_prio)/prio_sum_high;
					} else {
						prio_win = ((swin - buffer_pos)*r_prio)/prio_sum_low;
					}

					/* Debug print */
					v_print_log(VRS_PRINT_DEBUG_MSG, "Queue: %d, count: %d, r_prio: %6.3f, prio_win: %d\n",
							prio, prio_count, r_prio, prio_win);

					/* Get total size of commands that were stored in queue (sent_size) */
					tot_cmd_size = 0;
					/* Pack commands from queues with high priority to the buffer */
					buffer_pos = pack_prio_queue(C, sent_packet, buffer_pos, prio, prio_win, &tot_cmd_size);
					sent_size += tot_cmd_size;
				}
			}

			/* Use default color for output */
			if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%dm", 27, 0);
			}
		} else {
			if(is_log_level(VRS_PRINT_DEBUG_MSG)) {
				printf("%c[%d;%dm", 27, 1, 32);
				v_print_log(VRS_PRINT_DEBUG_MSG, "Keep alive packet\n");
				printf("%c[%dm", 27, 0);
			}
		}
	}

	/* Update sent_size */
	vconn->sent_size += sent_size;

	io_ctx->buf_size = buffer_pos;

	/* Send buffer */
	ret = v_send_packet(io_ctx, &error_num);

	if(ret==SEND_PACKET_SUCCESS) {
		gettimeofday(&tv, NULL);

		/* Update time of sending last payload packet */
		if(s_packet->header.flags & PAY_FLAG) {
			vconn->tv_pay_send.tv_sec = tv.tv_sec;
			vconn->tv_pay_send.tv_usec = tv.tv_usec;
			/* Store time of sending packet in history of sent packets. It is
			 * used for computing RTT and SRTT */
			if(sent_packet != NULL) {
				sent_packet->tv.tv_sec = tv.tv_sec;
				sent_packet->tv.tv_usec = tv.tv_usec;
			}
		}

		/* Update time of sending last acknowledgment packet */
		if(s_packet->header.flags & ACK_FLAG) {
			vconn->tv_ack_send.tv_sec = tv.tv_sec;
			vconn->tv_ack_send.tv_usec = tv.tv_usec;
		}

		/* Update counter of sent packets */
		if(s_packet->header.flags & PAY_FLAG) vconn->count_s_pay++;
		if(s_packet->header.flags & ACK_FLAG) vconn->count_s_ack++;

		/* If the packet was sent full and there are some pending data to send
		 * then modify returned value*/
		if(full_packet == 1) {
			ret = SEND_PACKET_FULL;
		}
	} else {
		/* When packet wasn't sent, then remove this packet from history */
		if(sent_packet != NULL) {
			v_packet_history_rem_packet(C, s_packet->header.payload_id);
		}
	}

	/*v_print_packet_history(&vconn->packet_history);*/

	return ret;
}