• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Komunikacja klient-serwer, przesyłanie plików

Object Storage Arubacloud
0 głosów
1,025 wizyt
pytanie zadane 29 grudnia 2017 w C i C++ przez B0nkers Początkujący (310 p.)

Witam,

Mam problem z komunikacją serwer - klient. Mianowicie chodzi mi o to że nie mam pojęcia w jaki sposób mam zaimplementować przesyłanie plików po przez enkapsulację danych i podzielenie pliku na mniejsze pakiety o rozmiarze nie większym niż 512 bajtów. Program w zależności od wyboru użytkownika ma przesyłać tekst lub plik. Z przesyłaniem tekstu jakoś sobie poradziłem (chociaż nie wiem czy dobrze), ale z przesyłaniem pliku mam problem. Komunikacje klient-serwer zrobiłem na podstawie windowsowego tutoriala: https://msdn.microsoft.com/pl-pl/library/windows/desktop/ms738545(v=vs.85).aspx  

Poniżej zamieszczam kod źródłowy klas CSerwer i CClient wraz z mainami:

CClient

#pragma once
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <cstdint>
#include <fstream>



// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


#define DEFAULT_BUFLEN 512
//#define DEFAULT_PORT "10000"
struct frame_t
{
	uint8_t type;
	uint8_t res;
	uint16_t len;
	uint8_t data[508];
};


class CClient
{
	uint8_t * bytes_to_send;

	SOCKET ConnectSocket;
	int iResult;
	

public:
	CClient();
	bool Connect(const char * addr, const char * port);
	bool Disconnect();
	~CClient();
	int Send(char * sendbuf);
	int Recieve(char * recvbuf);
	bool sendFile(const char * filename);
};

#include "CClient.h"

size_t capsulate_data(uint8_t * bytes, const frame_t &frame)
{
	bytes[0] = frame.type;
	bytes[1] = frame.res;
	memcpy(&bytes[2], (uint8_t *)&frame.len, 2);
	memcpy(&bytes[4], frame.data, frame.len);
	return frame.len + 4;
}

CClient::CClient()
{
	WSADATA wsaData;
	// Initialize Winsock
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		printf("WSAStartup failed with error: %d\n", iResult);
		//return ;
	}
}

bool CClient::Connect(const char * addr, const char * port)
{

	struct addrinfo *result, *ptr, hints;

	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;

	// Resolve the server address and port
	iResult = getaddrinfo(addr, port, &hints, &result);
	if (iResult != 0) {
		printf("getaddrinfo failed with error: %d\n", iResult);
		WSACleanup();
		return false;
	}

	// Attempt to connect to an address until one succeeds
	for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

		// Create a SOCKET for connecting to server
		ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
			ptr->ai_protocol);
		if (ConnectSocket == INVALID_SOCKET) {
			printf("socket failed with error: %ld\n", WSAGetLastError());
			WSACleanup();
			//return false;
		}
		// Connect to server.
		iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
		if (iResult == SOCKET_ERROR) {
			closesocket(ConnectSocket);
			ConnectSocket = INVALID_SOCKET;
			continue;
		}
		break;

	}

	freeaddrinfo(result);

	if (ConnectSocket == INVALID_SOCKET) {
		printf("Unable to connect to server!\n");
		WSACleanup();
		return false;
	}
	
	return true;
}

bool CClient::Disconnect()
{
	char * info = "Client has been disconnected\n";
	send(ConnectSocket, "Client has been disconnected\n",strlen(info) + 1, 0);

	// Send an initial buffer
	iResult = send(ConnectSocket, info, (int)strlen(info) + 1, 0);
	if (iResult == SOCKET_ERROR) {
		printf("Client is not connected with Server.\n");
		WSACleanup();
		return false;
	}
	// cleanup
	closesocket(ConnectSocket);
	WSACleanup();

	return true;
}

CClient::~CClient()
{
	iResult = shutdown(ConnectSocket, SD_SEND);
	closesocket(ConnectSocket);
	WSACleanup();

	/*if (iResult == SOCKET_ERROR) {
		printf("shutdown failed with error: %d \n", WSAGetLastError());
		closesocket(ConnectSocket);
		WSACleanup();
	}*/
}

int CClient::Send(char * sendbuf)
{
	frame_t frame;
	frame.type = 1;	//dane tekstowe
	frame.len = (int)strlen(sendbuf) + 1;

	memcpy(frame.data, sendbuf, frame.len);

	bytes_to_send = new uint8_t[frame.len + sizeof(frame_t)];
	

	memcpy(bytes_to_send, &frame, frame.len);
	size_t il_do_wyslania = capsulate_data(bytes_to_send, frame);

	
	// Send an initial buffer
	iResult = send(ConnectSocket,(const char *)bytes_to_send, il_do_wyslania, 0);
	if (iResult == SOCKET_ERROR) {
		printf("send failed with error: %d\n", WSAGetLastError());
		delete[] bytes_to_send;
		WSACleanup();
		return 1;
	}

	printf("Bytes Sent: %ld\n", iResult);

	delete[] bytes_to_send;
	return 0;
}

int CClient::Recieve(char * recvbuf)
{
	int recvbuflen = DEFAULT_BUFLEN;
	// Receive until the peer closes the connection
	

		iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
		if (iResult > 0)
			printf("Bytes received: %d\n", iResult);
		if (iResult == 0)
			printf("Connection closed\n");

		return 0;
}

bool CClient::sendFile(const char * filename)
{
	std::ifstream file;

	file.open(filename, std::ios::in | std::ios::binary);

	if (!file.is_open())
	{
		printf("File does not exist!\n");
		return false;
	}

	if (file)
	{
		file.seekg(0, file.end);
		int length = file.tellg();
		file.seekg(0, file.beg);

		bytes_to_send = new uint8_t[length + sizeof(frame_t)];

		printf("Reading %d bytes...\n", length);

		frame_t frame;
		frame.type = 2;		//plik
		frame.len = length;

		memcpy(&frame.data, filename, strlen(filename) + 1);
		

		file.read((char*)bytes_to_send, length);
		size_t il_do_wyslania = capsulate_data(bytes_to_send, frame);

		int nr_ramki = 0;
		int il_byt_wysl = 0;


		while (il_byt_wysl < il_do_wyslania)
		{
			//budowanie ramki
			frame.type = 2; //dane punktów
			frame.res = nr_ramki;
			frame.len = il_do_wyslania - il_byt_wysl;
			if (frame.len > 508)
				frame.len = 508;
			memcpy(frame.data, (uint8_t *)bytes_to_send + il_byt_wysl, frame.len);

			//kapsulacja danych
			size_t il_do_wyslania = capsulate_data(bytes_to_send, frame);

			// Send an initial buffer
			iResult = send(ConnectSocket, (const char *)bytes_to_send, il_do_wyslania, 0);
			if (iResult == SOCKET_ERROR) {
				printf("send failed with error: %d\n", WSAGetLastError());
				delete[] bytes_to_send;
				WSACleanup();
				return 1;
			}

			il_byt_wysl += 508;
			nr_ramki++;
		}

		printf("Bytes Sent: %ld\n", iResult);
	}
	
	delete[] bytes_to_send;

	return 0;
}


CSerwer:

#pragma once
#undef UNICODE

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <cstdint>

// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "10000"

class CSerwer
{
	WSADATA wsaData;
	int iResult;

	SOCKET ListenSocket;
	SOCKET ClientSocket;

	struct addrinfo *result;
	struct addrinfo hints;

	struct frame_t
	{
		uint8_t type;
		uint8_t res;
		uint16_t len;
		uint8_t data[508];
	};

	
public:
	CSerwer();
	~CSerwer();
	int Accept();
	int Echo();
	void Shutdown();
	void Loop();
};

#include "CSerwer.h"
#include <fstream>

CSerwer::CSerwer()
{
	ListenSocket = INVALID_SOCKET;
	ClientSocket = INVALID_SOCKET;


	result = NULL;
	//struct addrinfo *result = NULL;
	struct addrinfo hints;

	// Initialize Winsock
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		printf("WSAStartup failed with error: %d\n", iResult);
		//return;
	}

	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;  
	hints.ai_flags = AI_PASSIVE;

	// Resolve the server address and port
	iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
	if (iResult != 0) {
		printf("getaddrinfo failed with error: %d\n", iResult);
		WSACleanup();
		//return;
	}

	// Create a SOCKET for connecting to server
	ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
	if (ListenSocket == INVALID_SOCKET) {
		printf("listen socket failed with error: %ld\n", WSAGetLastError());
		freeaddrinfo(result);
		WSACleanup();
		//return;
	}

	// Setup the TCP listening socket
	iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
	if (iResult == SOCKET_ERROR) {
		printf("bind failed with error: %d\n", WSAGetLastError());
		freeaddrinfo(result);
		closesocket(ListenSocket);
		WSACleanup();
		//return;
	}

	freeaddrinfo(result);

}


CSerwer::~CSerwer()
{
	closesocket(ListenSocket);
	closesocket(ClientSocket);
	WSACleanup();
}

int CSerwer::Accept()
{
	iResult = listen(ListenSocket, SOMAXCONN);
	if (iResult == SOCKET_ERROR) {
		printf("listen failed with error: %d\n", WSAGetLastError());
		closesocket(ListenSocket);
		WSACleanup();
		return 0;
	}

	// Accept a client socket
	ClientSocket = accept(ListenSocket, NULL, NULL);
	if (ClientSocket == INVALID_SOCKET) {
		printf("accept failed with error: %d\n", WSAGetLastError());
		closesocket(ListenSocket);
		WSACleanup();
		return -1;
	}

	return 1;
}

int CSerwer::Echo()
{
	int iSendResult;
	char recvbuf[DEFAULT_BUFLEN];


		iResult = recv(ClientSocket, recvbuf, DEFAULT_BUFLEN, 0);
		if (iResult > 0) {

			frame_t frame;
			memcpy(&frame, recvbuf, sizeof(frame_t));

			unsigned int downloaded = iResult;
			while (downloaded < frame.len) {
				iResult = recv(ClientSocket, recvbuf + downloaded, DEFAULT_BUFLEN, 0);



				if (iResult == 0)
					break;
				else if (iResult < 0)
					return 0;

				downloaded += iResult;

			}


			if (frame.type == 1)
			{
				if (frame.data[0] == 'Q')
				{
					printf("Klient zakonczyl polaczenie z serwerem\n");
					closesocket(ClientSocket);
					WSACleanup();
					return 0;
				}

				printf("Przeslano slowo: %s\n", frame.data);
				printf("Bytes received: %d\n", iResult);

				iSendResult = send(ClientSocket, recvbuf, frame.len , 0);
				if (iSendResult == SOCKET_ERROR) {
					printf("send failed with error: %d\n", WSAGetLastError());
					closesocket(ClientSocket);
					WSACleanup();
					return 0;
				}
				printf("Bytes sent: %d\n", iSendResult);
			}
			
			else if (frame.type == 2)
			{
				
				int nr_ramki = 0, offset = 0;
				bool something_recv = false;
				while (true)
				{
					if (iResult == 512)
					{
						nr_ramki = recvbuf[0];
						offset = nr_ramki * 511;
						memcpy((uint8_t *)(frame.res)+offset, &recvbuf[1], 511);
						something_recv = true;
					}
					if (nr_ramki == 0 && something_recv)
					{
						break;
					}
				}

				std::ofstream file((const char *)frame.data, std::ios::out | std::ios::binary);

				file.write(recvbuf + sizeof(frame_t), frame.len);

				iSendResult = send(ClientSocket, (const char *)frame.data, sizeof(frame.data), 0);

				if (iSendResult == SOCKET_ERROR) {
					return 0;
				}
				printf("Saving %d bytes -> file ( %s )\n", frame.len, frame.data);

				printf("Bytes sent: %d\n", iSendResult);

			}

		}
		else if (iResult == 0)
		{
			printf("Connection closing...\n");
			return 0;
		}	
		else {
			printf("recv failed with error: %d\n", WSAGetLastError());
			closesocket(ClientSocket);
			WSACleanup();
			return 0;
		}


	return 1;
}

void CSerwer::Shutdown()
{
	// shutdown the connection since we're done
	iResult = shutdown(ClientSocket, SD_SEND);
	if (iResult == SOCKET_ERROR) {
		printf("shutdown failed with error: %d\n", WSAGetLastError());
		closesocket(ClientSocket);
		WSACleanup();
	}
	//closesocket(ClientSocket);
	//WSACleanup();
}

void CSerwer::Loop()
{
	int check = 1;
	do 
	{
		printf("Listening for client...\n");
		if (Accept())
			printf("Client has been connected!\n");

		else
			printf("Client has been disconnected!\n");
			
		do
		{
			check = Echo();
		} while (check > 0);
		

	} while (check > 0);

	printf("Shut down\n");
	Shutdown();

}


komentarz 30 grudnia 2017 przez j23 Mędrzec (194,920 p.)

ale z przesyłaniem pliku mam problem.

Ok, ale jaki to problem?

komentarz 30 grudnia 2017 przez B0nkers Początkujący (310 p.)
Taki problem, że nie potrafię poprawnie zaimplementować tej pętli, która dzieli plik na kolejne paczki (ramki), aby serwer mógł odbierać paczki i zapisywać plik w całości.

1 odpowiedź

0 głosów
odpowiedź 30 grudnia 2017 przez j23 Mędrzec (194,920 p.)
edycja 30 grudnia 2017 przez j23
	frame_t frame;
	frame.type = 2;
	frame.res = 0;

	while(file.read(frame.data, sizeof(flame.data)))
	{
		frame.len = file.gcount();

		iResult = send(ConnectSocket, (const char *)&frame, sizeof(frame), 0);
		if (iResult == SOCKET_ERROR) 
		{
			...
		}

		++frame.res; // po co to, nie wiem. Protokół TCP/IP gwarantuje kolejność dostarczonych danych
	}

	/* tu możesz wysłać pustą ramkę oznaczającą koniec transferu (choć sensowniej byłoby dać pełną długość pliku w pierwszej ramce) */

O to chodzi?

 

PS. tam, zamiast send() powinna być (napisana) funkcja send_all(), która wyśle wszystkie wskazane dane - send() może wysłać tylko część, jeśli bufor socketa będzie pełny (trzeba o tym pamiętać).

komentarz 30 grudnia 2017 przez B0nkers Początkujący (310 p.)
edycja 30 grudnia 2017 przez B0nkers

Tak, tylko że przy send_all() wyskakuje error, że funkcja jest niezdefiniowana.


bool CClient::sendFile(const char * filename)
{
	std::ifstream file;

	file.open(filename, std::ios::binary);

	if (!file.is_open())
	{
		printf("File does not exist!\n");
		return false;
	}

	if (file)
	{
		frame_t frame;
		frame.type = 2;
		frame.res = 0;
		int all_bytes = 0;

		while (file.read((char*)frame.data, sizeof(frame.data)))
		{
			frame.len = file.gcount();
			iResult = send(ConnectSocket, (const char *)&frame, sizeof(frame), 0);
			if (iResult == SOCKET_ERROR)
			{
				printf("send failed with error: %d\n", WSAGetLastError());
				WSACleanup();
				return false;
			}
			all_bytes += iResult;
			++frame.res;
		}
		printf("Bytes Sent: %ld\n", all_bytes);


		
	}
	
	return true;
}
komentarz 30 grudnia 2017 przez j23 Mędrzec (194,920 p.)
Musisz ją sobie napisać ;)
komentarz 30 grudnia 2017 przez B0nkers Początkujący (310 p.)
Aha no tak :) a w jaki sposób mam zrobić odbieranie pliku w serwerze?
komentarz 30 grudnia 2017 przez j23 Mędrzec (194,920 p.)
bool send_all(SOCKET s, const char* data, size_t n)
{
	int c;

	while(n > 0 && (c = send(s, data, n)) > 0)
	{
		n -= c;	
		data += c;
	}

	return n == 0;
}

Coś w ten deseń...

 

Co do odbierania: recv_all() odbierasz ramkę i metodą write() zapisujesz frame_t::len bajtów z frame_t::data.

komentarz 30 grudnia 2017 przez B0nkers Początkujący (310 p.)
edycja 30 grudnia 2017 przez B0nkers

W tej funkcji chyba powinno być 0 na końcu send, i funkcja ma zwrócić 0? Coś mi tu nie pasuje

 while(n > 0 && (c = send(s, data, n, 0)) > 0)

 

komentarz 30 grudnia 2017 przez j23 Mędrzec (194,920 p.)
Tak, moje przeoczenie...
komentarz 30 grudnia 2017 przez B0nkers Początkujący (310 p.)

Chyba nie dam rady tego zrobić :/

Tak ta funkcja ma wyglądać?

bool recv_all(SOCKET s, char* data, size_t n)
{
	int c;

	while (n > 0 && (c = recv(s, data, n, 0)) > 0)
	{
		n -= c;
		data += c;
	}

	return n == 0;
}

 

komentarz 30 grudnia 2017 przez j23 Mędrzec (194,920 p.)
Wygląda ok.
komentarz 30 grudnia 2017 przez B0nkers Początkujący (310 p.)
No to teraz jak mam tą odebrać, tak samo jak przy wysyłaniu w postaci pętli, czy po prostu odbieram wszystko i zapisuję do pliku?
komentarz 30 grudnia 2017 przez B0nkers Początkujący (310 p.)
Ok, dzięki chyba będe musiał napisać wszystko od początku

Podobne pytania

0 głosów
1 odpowiedź 509 wizyt
0 głosów
1 odpowiedź 544 wizyt
pytanie zadane 19 lutego 2018 w C# przez mus Użytkownik (700 p.)
0 głosów
1 odpowiedź 250 wizyt

92,568 zapytań

141,422 odpowiedzi

319,642 komentarzy

61,958 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...