/** @brief Handle an "event" on the unicast file descriptors * If the event is on an already open client connection, it handle the message * If the event is on the master connection, it accepts the new connection * If the event is on a channel specific socket, it accepts the new connection and starts streaming * */ int unicast_handle_fd_event(unicast_parameters_t *unicast_vars, fds_t *fds, mumudvb_channel_t *channels, int number_of_channels, strength_parameters_t *strengthparams, auto_p_t *auto_p, void *cam_p, void *scam_vars) { int iRet; //We look what happened for which connection int actual_fd; for(actual_fd=1;actual_fd<fds->pfdsnum;actual_fd++) { iRet=0; if((fds->pfds[actual_fd].revents&POLLHUP)&&(unicast_vars->fd_info[actual_fd].type==UNICAST_CLIENT)) { log_message( log_module, MSG_DEBUG,"We've got a POLLHUP. Actual_fd %d socket %d we close the connection \n", actual_fd, fds->pfds[actual_fd].fd ); unicast_close_connection(unicast_vars,fds,fds->pfds[actual_fd].fd); //We check if we hage to parse fds->pfds[actual_fd].revents (the last fd moved to the actual one) if(fds->pfds[actual_fd].revents) actual_fd--;//Yes, we force the loop to see it } if((fds->pfds[actual_fd].revents&POLLIN)||(fds->pfds[actual_fd].revents&POLLPRI)) { if((unicast_vars->fd_info[actual_fd].type==UNICAST_MASTER)|| (unicast_vars->fd_info[actual_fd].type==UNICAST_LISTEN_CHANNEL)) { //Event on the master connection or listenin channel //New connection, we accept the connection log_message( log_module, MSG_FLOOD,"New client\n"); int tempSocket; unicast_client_t *tempClient; //we accept the incoming connection tempClient=unicast_accept_connection(unicast_vars, fds->pfds[actual_fd].fd); if(tempClient!=NULL) { tempSocket=tempClient->Socket; fds->pfdsnum++; fds->pfds=realloc(fds->pfds,(fds->pfdsnum+1)*sizeof(struct pollfd)); if (fds->pfds==NULL) { log_message( log_module, MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); set_interrupted(ERROR_MEMORY<<8); return -1; } //We poll the new socket fds->pfds[fds->pfdsnum-1].fd = tempSocket; fds->pfds[fds->pfdsnum-1].events = POLLIN | POLLPRI | POLLHUP; //We also poll the deconnections fds->pfds[fds->pfdsnum-1].revents = 0; fds->pfds[fds->pfdsnum].fd = 0; fds->pfds[fds->pfdsnum].events = POLLIN | POLLPRI; fds->pfds[fds->pfdsnum].revents = 0; //Information about the descriptor unicast_vars->fd_info=realloc(unicast_vars->fd_info,(fds->pfdsnum)*sizeof(unicast_fd_info_t)); if (unicast_vars->fd_info==NULL) { log_message( log_module, MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); set_interrupted(ERROR_MEMORY<<8); return -1; } //client connection unicast_vars->fd_info[fds->pfdsnum-1].type=UNICAST_CLIENT; unicast_vars->fd_info[fds->pfdsnum-1].channel=-1; unicast_vars->fd_info[fds->pfdsnum-1].client=tempClient; log_message( log_module, MSG_FLOOD,"Number of clients : %d\n", unicast_vars->client_number); if(unicast_vars->fd_info[actual_fd].type==UNICAST_LISTEN_CHANNEL) { //Event on a channel connection, we open a new socket for this client and //we store the wanted channel for when we will get the GET log_message( log_module, MSG_DEBUG,"Connection on a channel socket the client will get the channel %d\n", unicast_vars->fd_info[actual_fd].channel); tempClient->askedChannel=unicast_vars->fd_info[actual_fd].channel; } } } else if(unicast_vars->fd_info[actual_fd].type==UNICAST_CLIENT) { //Event on a client connectio i.e. the client asked something log_message( log_module, MSG_FLOOD,"New message for socket %d\n", fds->pfds[actual_fd].fd); iRet=unicast_handle_message(unicast_vars,unicast_vars->fd_info[actual_fd].client, channels, number_of_channels, strengthparams, auto_p, cam_p, scam_vars); if (iRet==-2 ) //iRet==-2 --> 0 received data or error, we close the connection { unicast_close_connection(unicast_vars,fds,fds->pfds[actual_fd].fd); //We check if we hage to parse fds->pfds[actual_fd].revents (the last fd moved to the actual one) if(fds->pfds[actual_fd].revents) actual_fd--;//Yes, we force the loop to see it again } } else { log_message( log_module, MSG_WARN,"File descriptor with bad type, please contact\n Debug information : actual_fd %d unicast_vars->fd_info[actual_fd].type %d\n", actual_fd, unicast_vars->fd_info[actual_fd].type); } } } return 0; }
/** @brief Send the buffer for the channel * * This function is called when a buffer for a channel is full and have to be sent to the clients * */ void unicast_data_send(mumudvb_channel_t *actual_channel, mumudvb_channel_t *channels, fds_t *fds, unicast_parameters_t *unicast_vars) { if(actual_channel->clients) { unicast_client_t *actual_client; unicast_client_t *temp_client; int written_len; unsigned char *buffer; int buffer_len; int data_from_queue; int packets_left; struct timeval tv; actual_client=actual_channel->clients; while(actual_client!=NULL) { buffer=actual_channel->buf; buffer_len=actual_channel->nb_bytes; data_from_queue=0; if(actual_client->queue.packets_in_queue!=0) { //already some packets in the queue we enqueue the new one and try to send the queued ones data_from_queue=1; packets_left=UNICAST_MULTIPLE_QUEUE_SEND; if((actual_client->queue.data_bytes_in_queue+buffer_len)< unicast_vars->queue_max_size) unicast_queue_add_data(&actual_client->queue, buffer, buffer_len ); else { if(!actual_client->queue.full) { actual_client->queue.full=1; log_message( log_module, MSG_DETAIL,"The queue is full, we now throw away new packets for client %s:%d\n", inet_ntoa(actual_client->SocketAddr.sin_addr), actual_client->SocketAddr.sin_port); } } buffer=unicast_queue_get_data(&actual_client->queue, &buffer_len); } else packets_left=1; while(packets_left>0) { //we send the data written_len=write(actual_client->Socket,buffer, buffer_len); //We check if all the data was successfully written if(written_len<buffer_len) { //No ! packets_left=0; //we don't send more packets to this client if(written_len==-1) { if(errno != actual_client->last_write_error) { log_message( log_module, MSG_DEBUG,"New error when writing to client %s:%d : %s\n", inet_ntoa(actual_client->SocketAddr.sin_addr), actual_client->SocketAddr.sin_port, strerror(errno)); actual_client->last_write_error=errno; written_len=0; } } else { log_message( log_module, MSG_DEBUG,"Not all the data was written to %s:%d. Asked len : %d, written len %d\n", inet_ntoa(actual_client->SocketAddr.sin_addr), actual_client->SocketAddr.sin_port, actual_channel->nb_bytes, written_len); } if(!(unicast_vars->flush_on_eagain &&(errno==EAGAIN)))//Debug feature : we can drop data if eagain error { //No drop on eagain or no eagain if(!data_from_queue) { //We store the non sent data in the queue if((actual_client->queue.data_bytes_in_queue+buffer_len-written_len)< unicast_vars->queue_max_size) { unicast_queue_add_data(&actual_client->queue, buffer+written_len, buffer_len-written_len); log_message( log_module, MSG_DEBUG,"We start queuing packets ... \n"); } } else if(written_len > 0) { unicast_queue_remove_data(&actual_client->queue); unicast_queue_add_data(&actual_client->queue, buffer+written_len, buffer_len-written_len); log_message( log_module, MSG_DEBUG,"We requeue the non sent data ... \n"); } }else{ //this is an EAGAIN error and we want to drop the data if(!data_from_queue) { //Not from the queue we dont do anything log_message( log_module, MSG_DEBUG,"We drop not from queue ... \n"); } else { unicast_queue_clear(&actual_client->queue); log_message( log_module, MSG_DEBUG,"Eagain error we flush the queue ... \n"); } } if(!actual_client->consecutive_errors) { log_message( log_module, MSG_DETAIL,"Error when writing to client %s:%d : %s\n", inet_ntoa(actual_client->SocketAddr.sin_addr), actual_client->SocketAddr.sin_port, strerror(errno)); gettimeofday (&tv, (struct timezone *) NULL); actual_client->first_error_time = tv.tv_sec; actual_client->consecutive_errors=1; } else { //We have errors, we check if we reached the timeout gettimeofday (&tv, (struct timezone *) NULL); if((unicast_vars->consecutive_errors_timeout > 0) && (tv.tv_sec - actual_client->first_error_time) > unicast_vars->consecutive_errors_timeout) { log_message( log_module, MSG_INFO,"Consecutive errors when writing to client %s:%d during too much time, we disconnect\n", inet_ntoa(actual_client->SocketAddr.sin_addr), actual_client->SocketAddr.sin_port); temp_client=actual_client->chan_next; unicast_close_connection(unicast_vars,fds,actual_client->Socket,channels); actual_client=temp_client; } } } else { //data successfully written if (actual_client->consecutive_errors) { log_message( log_module, MSG_DETAIL,"We can write again to client %s:%d\n", inet_ntoa(actual_client->SocketAddr.sin_addr), actual_client->SocketAddr.sin_port); actual_client->consecutive_errors=0; actual_client->last_write_error=0; if(data_from_queue) log_message( log_module, MSG_DEBUG,"We start dequeuing packets Packets in queue: %d. Bytes in queue: %d\n", actual_client->queue.packets_in_queue, actual_client->queue.data_bytes_in_queue); } packets_left--; if(data_from_queue) { //The data was successfully sent, we can dequeue it unicast_queue_remove_data(&actual_client->queue); if(actual_client->queue.packets_in_queue!=0) { //log_message( log_module, MSG_DEBUG,"Still packets in the queue,next one\n"); //still packets in the queue, we continue sending if(packets_left) buffer=unicast_queue_get_data(&actual_client->queue, &buffer_len); } else //queue now empty { packets_left=0; log_message( log_module, MSG_DEBUG,"The queue is now empty :) client %s:%d \n", inet_ntoa(actual_client->SocketAddr.sin_addr), actual_client->SocketAddr.sin_port); } } } } if(actual_client) //Can be null if the client was destroyed actual_client=actual_client->chan_next; } } }