Beispiel #1
0
TreqMode
transfer_mode(MyString mode)
{
	return transfer_mode(mode.Value());
}
Beispiel #2
0
/******************************************************************************
	Start obsluhy klienta

	Inspirace (vytvoreni socketu, bind):
	http://beej.us/guide/bgnet/output/html/multipage/clientserver.html#datagram
*******************************************************************************/
int Client::start(Opt *options, struct sockaddr_storage client_addr, int index, char *buf, int numbytes, int mtu)
{
	// Generovani nahodneho cisla pro port, na kterem bude klient obsluhovan
	srand (getpid());
	int port = rand() % MAX_PORT + MIN_PORT;

	client_port = to_string(port);
	service_ip = options->address_at(index);

	int sockfd;
	struct addrinfo hints, *servinfo, *p;
	int rv;
	socklen_t addr_len;
	char s[INET6_ADDRSTRLEN];

	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_DGRAM;

	signal(SIGINT, client_signal_reaction); // Registrace funkce pro odchyt signalu

	if ((rv = getaddrinfo(service_ip.c_str(), client_port.c_str(), &hints, &servinfo)) != 0)
	{
		cerr << "Error - Service " << service_ip << ", " << client_port << gai_strerror(rv) << endl;
		return EXIT_FAILURE;
	}

	for(p = servinfo; p != NULL; p = p->ai_next)
	{
		if ((sockfd = socket(p->ai_family, p->ai_socktype,
			p->ai_protocol)) == -1)
		{
			continue;
		}

		if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1)
		{
			close(sockfd);
			continue;
		}

		break;
	}

	if (p == NULL)
	{
		cerr << "Error - Service " << service_ip << ", " << client_port << " - failed to bind socket" << endl;
		return EXIT_FAILURE;
	}

	freeaddrinfo(servinfo);

	client_socket = sockfd; // Ulozim si deskr. socketu

	// Nastavi se vychozi timeout socketu
	if((set_socket_timeout(options, IMPLICIT_TIMEOUT)) == SET_FAILED) return EXIT_FAILURE;

	// ulozim si klientovu IP pro potreby vypisu informacnich hlasek
	string client_ip = inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), s, sizeof s);

	mtu_size = mtu; // Ulozim si MTU

	// Cinnost obsluhy serveru
	string err_msg;
	unsigned int data_counter = 0;		// Pocitadlo dat
	unsigned int block_counter = 0;		// Pocitadlo bloku
	int data_len;						// Pocet odeslanych B
	int mode;							// Mod prenosu
	int tryouts = 0;					// Pocet pokusu o znovu navazani komunikace
	bool reading_data = false;			// Flag cteni dat
	bool writing_data = false;			// Flag zapisu dat
	int ack;							// Cislo potvrzeneho bloku
	int type;							// Typ paketu
	int file_status;					// Info o uspesnosti operace se souborem
	int bytes_write;					// Pocet zapsanych bajtu
	int block_num;						// Cislo bloku
	int recv_status;					// Pocet prijatych B pri volani funkce recvfrom()
	char *file_buf;						// Buffer pro nacitani dat ze souboru
	int file_buf_size;// Velikost bufferu

	File *f = new File(); // Nova instance objektu pro praci se soubory

	while(1)
	{// Smycka obsluhy klienta

	   if(writing_data == true) type = packet_type(file_buf); // buffer na heapu
	   else type = packet_type(buf); // buffer na zasobniku

	   switch(type)
	   {// Podle typu paketu se provadi dana operace

			case RRQ:
			{// Zadost o cteni
				get_filename(buf); // ukladani jmena souboru

				// Zjisti se mod prenosu

				mode = transfer_mode(buf);

				if(mode == UNKNOWN_MODE)
				{// neznamy mod
				   err_msg = "Unknown mode";
				   send_error(sockfd, client_addr, err_msg, ERROR_IL);
				   cout << current_time() << client_ip << " Unknown mode, operation aborted" << endl;
					
					clean_sources(f, sockfd);
					return 1;
				}
				else if(mode == NETASCII)
				{
					cout << current_time() + client_ip + " Requested READ of " << filename << " [netascii]"<< endl;
				}
				else if(mode == OCTET)
				{
					cout << current_time() + client_ip + " Requested READ of " << filename << " [octet]"<< endl;
				}

				// Otevre se soubor pro cteni
				file_status = f->open(filename, options->working_path(), FILE_IN, mode_tr);

				if(file_status == NOT_FOUND)
				{// Soubor nebyl nalezen
					err_msg = "File not found";
					send_error(sockfd, client_addr, err_msg, ERROR_NF);
					cout << current_time() + client_ip + " File " << filename << " not found, operation aborted" << endl;

					clean_sources(f, sockfd);
					return 1;
				}
				else if(file_status == PERM_DEN)
				{// Nedostatecne opravneni
					err_msg = "Permission denided";
					send_error(sockfd, client_addr, err_msg, ERROR_AV);
					cout << current_time() << client_ip << " Permission denided, operation aborted" << endl;

					clean_sources(f, sockfd);
					return EXIT_FAILURE;
				}

				// Zjisti se pritomnost rozsireni

				if(opt_extension(buf, numbytes, options, f) == EXTENSION)
				{// request obsahuje options
					send_oack(sockfd, client_addr);
					
					reading_data = true;

					// Alokace bufferu pro nacitani souboru
					if(block_size.used == true)
					{// byla specifikovana velikost bloku
						file_buf = new char[block_size.value + 1];
						file_buf_size = block_size.value + 1;

						if(f->get_file_size() > MAX_BLOCK_COUNT * block_size.value)
						{// Soubor je prilis velky na prenos pres tftp
							err_msg = "File is too large to send";
							send_error(sockfd, client_addr, err_msg, ERROR_DF);
							cout << current_time() + client_ip + " File is too large to send. Operation aborted\n";
							
							clean_sources(f, sockfd);
							return EXIT_FAILURE;
						}
					}
					else
					{// mezi parametry nebyla velikost bloku, uvazuje se standardni
						file_buf = new char[STANDARD_BLOCK + 1];
						file_buf_size = STANDARD_BLOCK + 1;
					}
				}
				else
				{// bez parametru, posilani prvnich dat

					if(f->get_file_size() > MAX_BLOCK_COUNT * STANDARD_BLOCK)
					{// Soubor je prilis velky na prenos pres tftp
						err_msg = "File is too large to send";
						send_error(sockfd, client_addr, err_msg, ERROR_IL);
						cout << current_time() + client_ip + " File is too large to send. Operation aborted\n";
						
						clean_sources(f, sockfd);
						return 1;
					}

					// Alokace bufferu pro nacitani souboru - standardni velikost
					file_buf = new char[STANDARD_BLOCK + 1];

					reading_data = true;

					cout << current_time() + client_ip + " Sending DATA\n";

					// poslou se prvni data
					data_len = send_data(sockfd, client_addr, 1, STANDARD_BLOCK, f, file_buf);
					data_counter += data_len;

					if(data_len < STANDARD_BLOCK)
					{// Prvni blok je zaroven i posledni, tim konci cinnost
						reading_data = false;
					}
				}

				break;
			}

			case WRQ:
			{// Zadost o zapis

				get_filename(buf); // ulozim jmeno souboru

				// Zjistim mod prenosu

				mode = transfer_mode(buf);

				if(mode == UNKNOWN_MODE)
				{// neznamy mod
					err_msg = "Unknown transfer mode";
					send_error(sockfd, client_addr, err_msg, ERROR_IL);
					break;
				}
				else if(mode == NETASCII)
				{
					cout << current_time() << client_ip << " Requested WRITE of " << filename << " [netascii]" << endl;
				}
				else if(mode == OCTET)
				{
					cout << current_time() << client_ip << " Requested WRITE of " << filename << " [octet]" << endl;
				}

				// Otevre se soubor pro zapis

				if(mode == OCTET)
				{
					file_status = f->open(filename, options->working_path(), FILE_OUT, OCTET);
				}
				else if(mode == NETASCII)
				{
					file_status = f->open(filename, options->working_path(), FILE_OUT, NETASCII);
				}

				if(file_status == FILE_EXISTS)
				{// Soubor j*z existuje
					err_msg = "File already exists";
					send_error(sockfd, client_addr, err_msg, ERROR_AE);
					cout << current_time() << client_ip << " File " << filename << " already exists, operation aborted" << endl;
					clean_sources(f, sockfd);
					return 1;
				}
				else if(file_status == CANNOT_OPEN)
				{// Nelze otevrit
					err_msg = "Cannot open file for writing";
					send_error(sockfd, client_addr, err_msg, ERROR_IL);
					cout << current_time() << client_ip << " Cannot opet file " << filename << " for writing, operation aborted" << endl;
					clean_sources(f, sockfd);
					return 1;
				}

				// Zjisti se rozsireni

				if(opt_extension(buf, numbytes, options, f) == EXTENSION)
				{// request obsahuje options
					send_oack(sockfd, client_addr);

					// Alokace bufferu pro nacitani souboru
					if(block_size.used == true)
					{// byla specifikovana velikost bloku
						file_buf = new char[block_size.value + 10];
						file_buf_size = block_size.value + 10;
					}
					else
					{// mezi parametry nebyla velikost bloku, uvazuje se standardni
						file_buf = new char[STANDARD_BLOCK + 10];
						file_buf_size = STANDARD_BLOCK + 10;
					}	  

					writing_data = true;
					cout << current_time() + client_ip + " Receiving DATA\n";
				}
				else
				{// Bez rozsireni, zasle se ack 0
					send_ack(sockfd, client_addr, 0);

					// Alokace bufferu pro zapis souboru - standardni velikost
					file_buf = new char[STANDARD_BLOCK + 10];
					file_buf_size = STANDARD_BLOCK + 10;

					writing_data = true;
					cout << current_time() + client_ip + " Receiving DATA\n";
				}

				break;
			}

			case DATA:
			{// Datovy paket od klienta

				if(writing_data == true)
				{// Probiha prenos
					block_num = ack_number(file_buf); // zjisti se cislo bloku

					if((block_counter + 1) != block_num)
					{// Prisel blok, ktery nenavazuje na predchozi

						err_msg = "Error while file transfer";
						send_error(sockfd, client_addr, err_msg, ERROR_IL);

						break;
					}

					int actual_block;

					if(block_size.used == true)
					{// Pouziva se nestandardni velikost bloku
						actual_block = block_size.value;
						bytes_write = recv_data(file_buf, numbytes, actual_block, f); // zapisou se data
					}
					else
					{// Standardni velikost bloku
						actual_block = STANDARD_BLOCK;
						bytes_write = recv_data(file_buf, numbytes, actual_block, f); // zapisou se data
					}

					if(bytes_write >= 0)
					{// Zapis byl uspesny, potvrdi se klientovi
						send_ack(sockfd, client_addr, block_num);

						block_counter++; // zvetsi se pocitadlo ulozenych bloku
						data_counter += bytes_write; // pricte se pocet ulozenych dat k pocitadlu

						if((numbytes - 4) < actual_block)
						{// dat bylo min nez je velikost bloku
							writing_data = false;

							f->close_file(FILE_OUT); // uzavre se soubor pro zapis

							cout << current_time() << client_ip << " File " << filename << " has been stored [" << data_counter << " B, " << block_counter << " blocks]" << endl;

							clean_sources(f, sockfd);

							return EXIT_FAILURE;
						}
					}
					else if(bytes_write == READ_ERR)
					{// Zapis nebyl uspesny
						err_msg = "Error while writing to file";
						send_error(sockfd, client_addr, err_msg, ERROR_IL);
						clean_sources(f, sockfd);
						return EXIT_FAILURE;
					}
				}

				break;
			}

			case ACK:
			{// Potvrzeni od klienta, ze obdrzel konkretni datovy paket

				ack = ack_number(buf);

				if(ack == 0) cout << current_time() + client_ip + " Sending DATA\n";

				if(reading_data == true)
				{// prenos jeste nebyl dokoncen

					if(block_size.used == true)
					{// Pouziva se nestandardni velikost bloku
						data_len = send_data(sockfd, client_addr, ack + 1, block_size.value, f, file_buf);

						if(data_len == SEND_FAIL)
						{// Chyba pri odesilani
							cerr << current_time() << client_ip << " Error in sendto, operation aborted" << endl;
							clean_sources(f, sockfd);
							return EXIT_FAILURE;
						}
						else if(data_len < block_size.value) reading_data = false;
					}
					else
					{// Standardni velikost bloku
						data_len = send_data(sockfd, client_addr, ack + 1, STANDARD_BLOCK, f, file_buf);

						if(data_len == SEND_FAIL)
						{// Chyba pri odesilani
							cerr << current_time() << client_ip << " Error in sendto, operation aborted" << endl;
							clean_sources(f, sockfd);
							return EXIT_FAILURE;
						}
						else if(data_len < STANDARD_BLOCK) reading_data = false;
					}

					data_counter += data_len;
				}
				else
				{// Prenos byl dokoncen
					cout << current_time() << client_ip << " File " << filename << " has been sent [" << data_counter << " B, " << ack << " blocks]\n";

					clean_sources(f, sockfd);

					return EXIT_SUCCESS;
				}

				break;
			}

			case ERR:
			{// Error paket

				int err;

				if(reading_data == true)
				{// Klient poslal error pri cteni dat
					err = recv_error(buf);

					switch(err)
					{
						case ERROR_UN:
						{// Unknown transfer ID

							f->close_file(FILE_IN);
							cout << current_time() << client_ip << " Client aborted file read (transport error)" << endl;
							return EXIT_FAILURE;
						}

						case ERROR_DF:
						{// Disk full
							f->close_file(FILE_IN);
							cout << current_time() << client_ip << " Client aborted file read (too large)" << endl;
							return EXIT_FAILURE;
						}

						case ERROR_ND:
						{// Nedefinovana chyba
							f->close_file(FILE_IN);
							cout << current_time() << client_ip << " Client aborted file read (undefined error)" << endl;
							return EXIT_FAILURE;
						}
					}
				}
				else if(writing_data == true)
				{// Klient poslal error pri zapisu dat
					err = recv_error(buf);

					switch(err)
					{
						case ERROR_UN:
						{// Unknown transfer ID
							f->close_file(FILE_OUT);
							cout << current_time() << client_ip << " Client aborted file write" << endl;
							return EXIT_FAILURE;
						}

						case ERROR_ND:
						{// Nedefinovana chyba
							f->close_file(FILE_OUT);
							cout << current_time() << client_ip << " Client aborted file read (undefined error)" << endl;
							return EXIT_FAILURE;
						}
					}
				}

				break;
			}

			case UNKNOWN_OPCODE:
			{// Neznamy opcode
				err_msg = "Unknown request";
				send_error(sockfd, client_addr, err_msg, ERROR_IL);
				clean_sources(f, sockfd);
				return EXIT_FAILURE;
			}

		}// konec switch(type)

		if(writing_data == true)
		{// Pokud probiha zapis, pouziva se vetsi buffer alokovany na halde
			recv_status = recv_packet(sockfd, client_addr, file_buf, file_buf_size);
		}
		else
		{// Pri cteni staci mensi velikost na zasobniku
			recv_status = recv_packet(sockfd, client_addr, buf, BUFSIZE);
		}

		if(recv_status == TIMEOUT_ELAPSED)
		{// Vyprsel cas cekani, probehne 3x pokus o znovu navazani komunikace
			tryouts++;

			if(tryouts == 3)
			{// Probehly tri pokusy o znovu navazani komunikace
				cout << current_time() << client_ip << " Timeout elapsed, operation aborted" << endl;
				clean_sources(f, sockfd);
				return 1;
			}
			else
			{// Probehne pokus
				cout << current_time() << client_ip << " Timeout elapsed, retrying..." << endl;
			}
		}
		else
		{// Byl normalne prijat paket
			numbytes = recv_status;
			tryouts = 0; // Vynuluje se pocitadlo neuspesnych pokusu
		}
	}
}