Добавил:
korayakov
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:
/*
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