Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лабы / 3 / 2.tar / 2 / 2 / net_copy / os_spec

.cpp
Скачиваний:
16
Добавлен:
17.04.2013
Размер:
15.16 Кб
Скачать
/*
  netFileCopyer
  Copyright (C) 2004 Dmitry S. Vasilchenko.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian
  system and in the file COPYING in the Linux kernel source.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <resolv.h>

#include <sys/stat.h>

#include <errno.h>
#include <fcntl.h>
#include <signal.h>

#include <time.h>

#include <sys/time.h>

#include "os_spec.h"

//#define DEBUG
//#define VDEBUG

#ifdef DEBUG
	static const int oi_debug = 1;
#else
	static const int oi_debug = 0;
#endif

#define dprintf if (oi_debug) printf
#define dperror if (oi_debug) perror


#ifdef VDEBUG
	static const int oi_vdebug = 1;
#else
	static const int oi_vdebug = 0;
#endif

#define vprintf if (oi_vdebug) printf
#define vperror if (oi_vdebug) perror

#include <pthread.h>

pthread_mutex_t oi_context_mutex = PTHREAD_MUTEX_INITIALIZER;
const int oi_context_maxclients = 40;
struct oi_context oi_context[oi_context_maxclients];
int oi_count = 0;


void *Malloc(size_t size)
{
	void *p = malloc(size);
	if (!p){
		perror("Memory allocation!");
		exit(1);
	}
	
	return p;	
}

void *Realloc(void *buf, size_t size)
{
	void *p = realloc(buf, size);
	if (!p){
		perror("Memory reallocation!");
		exit(1);
	}
	
	return p;	
}

void oi_handler(int signum)
{
	vprintf("%d:: catch %s\n", getpid(), (signum == SIGIO)?"SIGIO":"SIGALRM");
		
	if (pthread_mutex_trylock(&oi_context_mutex) < 0){
		printf("%d:: locked!\n", getpid());
			struct itimerval it_val;
			it_val.it_interval.tv_sec = 0;
			it_val.it_interval.tv_usec = 10000;
			it_val.it_value.tv_sec = 0;
			it_val.it_value.tv_usec = 100;
			setitimer(ITIMER_REAL, &it_val, NULL);
		return;
	}

	int i;
	pid_t pid = getpid();

	for (i=0; i < oi_count; i++){
		if (oi_context[i].pid == pid){
			class oi_queue *tp = oi_context[i].c_cl;
	
			pthread_mutex_unlock(&oi_context_mutex);
	
			if (signum == SIGALRM){
				tp->out_handler();
				tp->in_handler();
			}
			else{
				tp->in_handler();
				tp->out_handler();
			}

			return;
		}
	}

	pthread_mutex_unlock(&oi_context_mutex);

		vprintf("%d::done\n", getpid());
}

ssize_t get_fsize(const char *fname)
{
	struct stat mstat;
	if (stat(fname,&mstat) < 0)
		return -1;
	
	return mstat.st_size;
}

/* Create socket and init some struct */
oi_connect::oi_connect()
{
	vprintf("%d::oi_connect\n",getpid());
	port = 0;
	ip = NULL;
	
	err_state = 0;
	
	stop = 0; /* stop flag */
	
	q = NULL;
}

oi_connect::~oi_connect()
{
	/* free buffers here */
	vprintf("%d::~oi_connect\n",getpid());
	
	if (q) delete q;
	if (ip) free(ip);	

	stop = 1;	
}

int oi_connect::d_connect(const char *p_ip, unsigned int p_port)
{
	vprintf("%d::d_connect\n",getpid());
	
	ip = (char *)Malloc(sizeof(char)*(strlen(p_ip)+1));
	
	strncpy(ip,p_ip,strlen(p_ip)+1);
	
	port = p_port;
	
	struct sockaddr_in r_addr;
	memset(&r_addr, 0, sizeof(r_addr));
	r_addr.sin_family = AF_INET;
	r_addr.sin_port = htons(port);
	if (inet_pton(AF_INET, ip, &r_addr.sin_addr)<=0){
		hold_error("d_connect:: inet_pton");
		return -1;
	}

	int sd = socket(PF_INET, SOCK_DGRAM, 0);

	if ( sd < 0 ){
		hold_error("d_connect:: create socket");
		return -1;
	}
		
	if (connect(sd, (SA *)&r_addr, sizeof(r_addr)) == -1){
		hold_error("d_connect:: connect");
		return -1;
	}
		
	if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0){
		hold_error("d_connect:: fcntl");
		return -1;
	}
	
	q = new oi_queue(sd, 1);
		
	vprintf("%d::d_connect::send hello\n",getpid());
	char dchar = OI_HELLO;
	if (d_send(&dchar, 1) < 0){
		hold_error("d_connect:: send hello");
		return -1;
	}

	vprintf("%d::d_connect::recv ack\n",getpid());
	int res = d_recv(&dchar, 1);
	if ( res == 0){
		hold_error("d_connect:: recv timeout");
		return -1;
	}
	else if ( res < 0){
		hold_error("d_connect:: recv error");
		return -1;
	}

	if (dchar != OI_ACK){
		hold_error("d_connect:: unknown acknowledge");
		return -1;
	}
	
	vprintf("%d::d_connect::connected\n",getpid());
		
	return 0;
}

int oi_connect::d_accept(unsigned int p_port)
{
	vprintf("%d::d_accept\n",getpid());
	
	port = p_port;
	
	struct sockaddr_in r_addr;
	memset(&r_addr, 0, sizeof(r_addr));
	r_addr.sin_family = AF_INET;
	r_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	r_addr.sin_port = htons(port);

	int sd = socket(PF_INET, SOCK_DGRAM, 0);
	
	if ( sd < 0 ){
		hold_error("d_accept:: create socket");
		return -1;
	}
		
	if ( bind(sd, (SA *)&r_addr, sizeof(r_addr)) == -1){
		hold_error("d_accept:: bind");
		return -1;
	}
	
	if (fcntl(sd,F_SETFL,O_NONBLOCK) < 0){
		hold_error("d_accept:: fcntl");
		return -1;
	}

	q = new oi_queue(sd, 0);
	
	vprintf("%d::d_accept::wait hello\n",getpid());
	char dchar = 0;
	while ( dchar != OI_HELLO ){
		if (d_recv(&dchar, 1) <= 0 ){
			hold_error("d_accept:: recv timeout or error");
			return -1;
		}
	}
	
	vprintf("%d::d_accept::send ack\n",getpid());
	dchar = OI_ACK;
	if (d_send(&dchar, 1) < 0){
		hold_error("d_accept:: send ACK");
		return -1;
	}

	vprintf("%d::d_accepte end\n",getpid());
		
	return 0;
}

void oi_connect::d_disconnect()
{
}

int oi_connect::d_send(const void *buf, size_t len)
{
	if ( q->out((char *)buf, len) < 0 ){
		hold_error("d_send:: send data");
		return -1;
	}
	
	return len;
}

int oi_connect::d_recv(void *buf, size_t len)
{
	int r_len = q->in((char *)buf, len);
	
	if (r_len < 0){
		hold_error("d_recv:: recv data");
		return -1;
	}
	
	return r_len;
}

void oi_connect::d_stop()
{
	if (q) q->q_stop();
	stop ++;
}

QString oi_connect::get_last_error()
{
	return err_str;
}

int oi_connect::estate()
{
	return err_state;
}

void oi_connect::hold_error(const char *str)
{
	printf("Error:: %s\n", str);
	perror("");
		
	err_str.append(str);
	err_str.append(":: ");

	err_state = 1;
}

/* ==================== oi_queue ============================================ */

unsigned long oi_queue::get_time()
{
	unsigned long t;
	struct timeval tv;
	gettimeofday(&tv, NULL);
	t = (tv.tv_sec - init_time)*1000;
	t += tv.tv_usec / 1000;

	return t;
}

oi_queue::oi_queue(int p_sd, int p_conn)
{
	oi_pid = getpid();

	sd = p_sd;

	conn = p_conn;		

	stop = 0;

	init_time = time(NULL);

	out_len = 0;
	out_size = 100;
	out_queue = (struct oi_packet **)Malloc(
		sizeof(struct oi_packet *) * out_size);
	int i;
	for (i=0; i < out_size; i++){
		out_queue[i] = (struct oi_packet *)Malloc( sizeof(struct oi_packet));
		out_queue[i]->buffer = NULL;		
	}
	
	pthread_mutex_lock(&oi_context_mutex);

	if (oi_count >= oi_context_maxclients){
		perror("Max Clients limit exceed!\n");
		sd = -1;
		return;
	}
	oi_context[oi_count].pid = oi_pid;
	oi_context[oi_count].c_cl = this;
	oi_count++;
	
	pthread_mutex_unlock(&oi_context_mutex);
	
	counter = 0; /* incremental packet number counter */	
	
	pipe(in_buffer);
	in_len = 0;
	
	w_last = ~0;
	
	/*if (oi_debug || oi_vdebug)*/ fcntl(STDOUT_FILENO, F_SETFL, O_DIRECT);
	
	sigemptyset(&block_mask);
	sigaddset(&block_mask, SIGALRM);
	sigaddset(&block_mask, SIGIO);

	sigemptyset(&zero_mask);

	/* SET in_interrupt() handler */
	struct sigaction nact;
	memset(&nact, 0, sizeof(nact));
	nact.sa_handler = &oi_handler;
	sigaddset(&nact.sa_mask, SIGIO);
	sigaddset(&nact.sa_mask, SIGALRM);
	nact.sa_flags = SA_RESTART;
	
	sigaction(SIGIO, &nact, NULL);
	sigaction(SIGALRM, &nact, NULL);
	
	fcntl(sd, F_SETOWN, oi_pid);
	fcntl(sd, F_SETFL, O_ASYNC | O_NONBLOCK);
}

oi_queue::~oi_queue()
{
	/* STOP interrupt handling */
	sigprocmask(SIG_BLOCK, &block_mask, &old_mask);

	pthread_mutex_lock(&oi_context_mutex);

	pid_t pid = oi_pid;

	int i;	
	for (i=0; i < oi_count; i++)
		if (oi_context[i].pid == pid){
			oi_context[i].pid = oi_context[oi_count-1].pid;
			oi_context[i].c_cl = oi_context[oi_count-1].c_cl;
			oi_count--;
			break;
		}
		
	pthread_mutex_unlock(&oi_context_mutex);

	/* STOP interrupt handling */
	sigprocmask(SIG_BLOCK, &old_mask, NULL);

	if (out_queue){
		int i;
		for (i=0; i < out_size; i++){
			if (out_queue[i])
				free(out_queue[i]->buffer);
			free(out_queue[i]);
		}
		free(out_queue);
	}
			
	close(in_buffer[0]);
	close(in_buffer[1]);
	
	if (sd >= 0){
		close(sd);
		sd = -1;
	}
}

/* add packet to output queue and start transmiton routine */
ssize_t oi_queue::out(char *buf, size_t len)
{	
	if (sd < 0)
		return -1;
		
	vprintf("%d:: send len=%d\n",oi_pid, len);

	/* STOP interrupt handling! */	
	sigprocmask(SIG_BLOCK, &block_mask, &old_mask);

	if (out_len >= out_size){
		perror("OUT queue overflow!!!");
		exit(1);
	}

	/* create and fill packet */	
	out_queue[out_len]->flag = 0;
	out_queue[out_len]->number = counter++;

	if (out_queue[out_len]->buffer == NULL)
		out_queue[out_len]->buffer = (char *)Malloc(sizeof(char)*OI_MAXBUFFER);

	if (len > OI_MAXBUFFER){		
		perror("TOO large data amount. Pls, look at OI_MAXBUFFER at <os_spec.h>");
		return -1;
	}
	memcpy(out_queue[out_len]->buffer, buf, len);

	out_queue[out_len]->len = len;
	out_queue[out_len]->t_time = 0;
	out_queue[out_len]->t_retry = 0;

	out_len++;

	/* RESTORE interrupt handling */
	sigprocmask(SIG_SETMASK, &old_mask, NULL);
		
	kill(oi_pid, SIGALRM);
	
	return len;
}

void oi_queue::out_handler()
{
	int i;
	unsigned long c_time;
	ssize_t len;

	static struct oi_hdr oi_hdr;
	struct iovec p_data[2];
	p_data[0].iov_base = &oi_hdr;
	p_data[0].iov_len = sizeof(struct oi_hdr);	
	
	/* go through awaiting packets */
	for (i=0; i < out_len; i++){
		c_time = get_time(); // current time
		
		if ( (out_queue[i]->t_time != 0) &&
				(c_time > (out_queue[i]->t_time + OI_TIMEWAIT*TSCALE)) ){
			/* stop sending */
			printf("%d::out_handler:: timeout packet num=%ld\n",
					oi_pid,out_queue[i]->number);
			out_remove(i);
		}
				
		else if ( (out_queue[i]->t_time == 0) ||
				( c_time > (out_queue[i]->t_retry + OI_RETR_TOUT*TSCALE)) ){
			/* (re)send it! */
			
			/* packet data for sendmsg() */
			oi_hdr.flag = out_queue[i]->flag;
			oi_hdr.number = out_queue[i]->number;
			
			p_data[1].iov_base = out_queue[i]->buffer;
			p_data[1].iov_len = out_queue[i]->len;
			
			len = writev(sd, p_data, 2);
			
			if (len < 0){
				dperror("out_handler::sendmsg");
			}
			else{	/* sent OK */			
				if (out_queue[i]->t_time == 0){ /* first send :) */
					out_queue[i]->t_time = get_time();
					dprintf("%d::out_handler:: sent packet flag=%d num=%ld len=%d\n",
						oi_pid, oi_hdr.flag, oi_hdr.number, len);
				}
				else{
					dprintf("%d::out_handler:: resent packet flag=%d num=%ld len=%d\n",
						oi_pid, oi_hdr.flag, oi_hdr.number, len);
				}
				
				out_queue[i]->t_retry = get_time();
			}
		}		
	}

	struct itimerval it_val;
	memset(&it_val, 0, sizeof(struct itimerval));
	if (out_len == 0){
		dprintf("%d::out_handler:: all transfered\n", oi_pid);
		it_val.it_interval.tv_usec = 100000;
		it_val.it_value.tv_usec = 100000;
	}
	else{
		vprintf("%d::out_handler:: not all transfered\n", oi_pid);		
		it_val.it_interval.tv_usec = 10000;
		it_val.it_value.tv_usec = 2048;
	}
	
	setitimer(ITIMER_REAL, &it_val, NULL);
}

void oi_queue::out_remove(int index)
{	
	vprintf("%d::out_remove:: number=%ld\n",
				oi_pid, out_queue[index]->number);		

	if ( (index < 0) || (index >= out_len) ){
		printf("oi_queue::out_remove:: bad index\n");
		return;
	}
	
	if ( index != (out_len - 1) ){
		struct oi_packet *p = out_queue[index];
		out_queue[index] = out_queue[out_len-1];
		out_queue[out_len-1] = p;
	}
	out_len--;
}

/* wait for data in and return it to user
 * if timeout has expired - return 0 bytes */
ssize_t oi_queue::in(char *buf, size_t len)
{
	if (sd < 0)
		return -1;
		
	vprintf("%d:: in need len=%d\n", oi_pid, len);
			
	/* STOP interrupt handling */
	sigprocmask(SIG_BLOCK, &block_mask, &old_mask);
	
	while ( (in_len < len) && (!stop) ){
		/* WAIT for interrupt */
		sigsuspend(&zero_mask);
	}
	
	if (stop){
		sigprocmask(SIG_SETMASK, &old_mask, NULL);
		return -1;
	}
	
	ssize_t r_len = read(in_buffer[0], buf, len);
	if ( r_len >= 0)
		in_len -= r_len;
		
	/* START interrupt handling */
	sigprocmask(SIG_SETMASK, &old_mask, NULL);
	
	return r_len;
}
	
	
/* datagram receive handler */
void oi_queue::in_handler()
{
	ssize_t len;
	size_t data_len;
	
		static struct oi_hdr oi_hdr;
		struct iovec p_data[2];
		p_data[0].iov_base = &oi_hdr;
		p_data[0].iov_len = sizeof(struct oi_hdr);
		p_data[1].iov_base = in_dgbuffer;
		p_data[1].iov_len = OI_MAXBUFFER;	
		
	for(;;){

		if (!conn){
			socklen_t nrpc_len = sizeof(struct sockaddr_in);
			struct sockaddr_in nrpc;
			
			struct msghdr p_hdr;	
			p_hdr.msg_iov = p_data;
			p_hdr.msg_name = (SA *)&nrpc;
			p_hdr.msg_namelen = nrpc_len;
			p_hdr.msg_iovlen = 2;

			len = recvmsg(sd, &p_hdr, 0);
		
			if (len <= 0){ /* kinda error */
				break;
			}
						
			if (connect(sd, (SA *)&nrpc, p_hdr.msg_namelen) == -1){
				printf("%d:: But connect faild\n", oi_pid);
			}
			printf("%d:: fasten\n", getpid());
			conn = 1;
			
		}
		else{
			len = readv(sd, p_data, 2);
		}

		if (len <= 0){ /* kinda error */
			break;
		}
		
		dprintf("%d:: in_handler:: len=%d flag=%d num=%ld w_last=%ld\n", 
				oi_pid, len, oi_hdr.flag, oi_hdr.number, w_last);
							
		if (oi_hdr.flag == OI_ACK){ /* acknowledge */
			/* remove appropriate packet from out_queue */
			int i;
			for (i = 0; i < out_len; i++)
				if (oi_hdr.number == out_queue[i]->number){
					out_remove(i);
					vprintf("%d:: out_remove done\n", oi_pid);
				}
		}
		else{ /* data */
			if ( (w_last != (unsigned long)~0) && (oi_hdr.number <= w_last) ){
				/* send ack again */
				printf("%d:: in_handler:: ACK (num=%ld w_last=%ld) lost!\n", 
							oi_pid, oi_hdr.number, w_last);
			}
			else{
				if (random()%500 == 5)
					continue;
				
				 if (w_last == (unsigned long)~0 ||
				 		oi_hdr.number == w_last + 1){
				 	w_last = oi_hdr.number;
					data_len = len - sizeof(oi_hdr);
					write(in_buffer[1], p_data[1].iov_base, data_len);
					in_len += data_len;
				 }
				 else{ /* ignore packets not in turn */
				 	dprintf("%d in_handler:: not in turn (num=%ld w_last=%ld)!\n",
				 		 oi_pid, oi_hdr.number, w_last);
				 	continue;
				 }
			}
						
			/* send ack */
			oi_hdr.flag = OI_ACK;
			
			if (random()%500 != 5)
				writev(sd, p_data, 1);
		}		
	}
	vprintf("%d:: in_done\n", oi_pid);
}

void oi_queue::q_stop()
{
    stop++;
    int i;
    for (i=0; i < oi_count; i++)
	kill(oi_context[i].pid,SIGALRM);
}

/*                   ^^^^^^^^^^^                     */
/* ^^^^^^^^^^^^^^^^^^ VERIFIED! ^^^^^^^^^^^^^^^^^^^^ */
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
Соседние файлы в папке net_copy