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

Лабы / 2 / lab.01.by mice / fexchange / fx_net

.cpp
Скачиваний:
10
Добавлен:
17.04.2013
Размер:
12.49 Кб
Скачать
/*******************************************************************************
* file:         fx_net.cpp                                                     *
* version:      0.0.1                                                          *
* author:       inba (mailto:inba@mail.ru)                                     *
* description:  not available                                                  *
*******************************************************************************/


////////////////////////////////////////////////////////////////////////////////
// program specific headers
#include "fx_net.h"


namespace fx_ns {

/*******************************************************************************
*  definitions                                                                 *
*******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
// constants
static BYTE winsock_ver_hi	=	2;
static BYTE winsock_ver_low	=	0;



////////////////////////////////////////////////////////////////////////////////
// functions (definitions)
WORD version_required() {
	return MAKEWORD(winsock_ver_hi, winsock_ver_low);
}



////////////////////////////////////////////////////////////////////////////////
// cfx_socket (public definitions)
cfx_socket::cfx_socket() {
	_kill_mtx = CreateMutex(NULL, FALSE, NULL);
	_socket = INVALID_SOCKET;
	_last_error = 0;
	_on_event = NULL;
	_owner = NULL;
	_ext_id = NULL;
}


cfx_socket::cfx_socket(void *const owner, const tfx_socket_event ev_handler, void *const ext_id) {
	_kill_mtx = CreateMutex(NULL, FALSE, NULL);
	_socket = INVALID_SOCKET;
	_last_error = 0;
	_on_event = ev_handler;
	_owner = owner;
	_ext_id = ext_id;
}


cfx_socket::~cfx_socket() {
	if (INVALID_SOCKET != _socket) closesocket(_socket);
	CloseHandle(_kill_mtx);
}


void cfx_socket::set_linger(unsigned short on_off, unsigned short t) {
	linger sling;
	sling.l_linger = on_off;
	sling.l_onoff = t;
	setsockopt(_socket, SOL_SOCKET, SO_LINGER, (char *)&sling, sizeof(sling));
}


void cfx_socket::get_linger(unsigned short *on_off, unsigned short *t) const {
	linger slinger;
	int opt_len = sizeof(slinger);
	getsockopt(_socket, SOL_SOCKET, SO_LINGER, (char *)&slinger, &opt_len);
	if (NULL != on_off) *on_off = slinger.l_onoff;
	if (NULL != t) *t = slinger.l_linger;
}


int cfx_socket::resolve_addr(const char *const addr, in_addr *const raddr) {
	raddr->s_addr = inet_addr(addr);
	// If the supplied server address wasn’t in the form "aaa.bbb.ccc.ddd"
	// it’s a host name, so try to resolve it
	if (INADDR_NONE == raddr->s_addr) {
		hostent *host;
		host = gethostbyname(addr);
		if (NULL == host) return ERC_SOCK_RESOLVE;
		memcpy(raddr, host->h_addr_list[0], host->h_length);
	}
	return OK_OK;
}


int cfx_socket::create() {
	_saddr_in.sin_family = AF_INET;

	_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == _socket) return _wsa_error(ERC_SOCK_SOCKET);

	return OK_OK;
}


int cfx_socket::create(const SOCKET sock, const sockaddr_in *const saddr_in) {
	_socket = sock;
	_saddr_in = *saddr_in;
	return OK_OK;
}


int cfx_socket::kill() {
	if (INVALID_SOCKET != _socket) {
		if (SOCKET_ERROR == closesocket(_socket))
			return _wsa_error(ERC_SOCK_CLOSE);
	}
	return OK_OK;
}



////////////////////////////////////////////////////////////////////////////////
// cfx_server (protected definitions)
void cfx_server::_init() {
	_stoped = true;
	_fs_sock = INVALID_SOCKET;
	_accept_thread_id = NULL;
	_accept_thread_h = NULL;
	_mx_pc = _def_mx_pc;
	_buf_sz = _def_buf_sz;
}


DWORD __stdcall cfx_server::_accept_proc(cfx_server *const server) {
	WaitForSingleObject(server->kill_mtx(), INFINITE);
	for (;;) {
		server->_stoped = false;
		server->_call_event(EV_LISTENING, NULL);

		sfx_accept_struct acst;
		acst.addr_len = sizeof(acst);
		acst.asock = accept(server->sock(), (sockaddr *)&acst.addr, &acst.addr_len);

		if (server->_stoped) {
			// if (0 != ioctlsocket(server->sock(), FIONBIO, 0)) {
			// 	server->_wsa_error();
			// 	server->_call_event(EV_ERROR, TO_PVOID(ERC_SOCK_IOSTL));
			// }
			if (INVALID_SOCKET != server->_fs_sock) {
				closesocket(server->_fs_sock);
				server->_fs_sock = INVALID_SOCKET;
			}

			int res = server->_call_event(EV_STOPPING, TO_PVOID(EV_STR_USER));
			if (OK_CANCEL != res) {
				server->_call_event(EV_STOPED, TO_PVOID(EV_STR_USER));
				if (INVALID_SOCKET != acst.asock) closesocket(acst.asock);
				ReleaseMutex(server->kill_mtx());
				return OK_OK;
			}
			continue;
		}

		if (INVALID_SOCKET == acst.asock) {
			server->_wsa_error();
			server->_call_event(EV_ERROR, TO_PVOID(ERC_SERV_INVSOCKAC));
			int res = server->_call_event(EV_STOPPING, TO_PVOID(ERC_SERV_INVSOCKAC));
			if (OK_CANCEL != res) {
				server->_call_event(EV_STOPED, TO_PVOID(ERC_SERV_INVSOCKAC));
				ReleaseMutex(server->kill_mtx());
				return ERC_SERV_INVSOCKAC;
			}
			continue;
		}

		// tell about new connection and if not accepted, continue listening
		int res = server->_call_event(EV_ACCEPTING, &acst);
		if (OK_CANCEL == res) {
			closesocket(acst.asock);
			continue;
		}
		if (OK_OK != res) {
			//TODO: handle some errors
		}

		// run client socket for downloading
		cfx_client *client = server->new_client();
		client->create(acst.asock, &acst.addr);
		client->set_parent(server);
		if (OK_OK != server->_call_event(EV_ACCEPTED, client)) server->del_client(client);
    }

	ReleaseMutex(server->kill_mtx());
	return (DWORD)OK_OK;
}


////////////////////////////////////////////////////////////////////////////////
// cfx_server (public definitions)
cfx_server::~cfx_server() {
	if (INVALID_SOCKET != _fs_sock) closesocket(_fs_sock);
}


int cfx_server::bind(const char *const addr, const unsigned short port) {
	saddr_in()->sin_port = htons(port);
	int res = resolve_addr(addr, &saddr_in()->sin_addr);
	if (OK_OK != res) return _wsa_error(res);

	res = ::bind(sock(), (sockaddr *)saddr_in(), saddr_in_sz());
	if (0 != res) return _wsa_error(ERC_SOCK_BIND);
	res = listen(sock(), _mx_pc);
	if (0 != res) return _wsa_error(ERC_SOCK_LISTEN);

	return OK_OK;
}


int cfx_server::run() {
	_accept_thread_h = CreateThread(NULL, 0, LPTHREAD_START_ROUTINE(_accept_proc), (void *)this,
				0, &_accept_thread_id);

	if (NULL == _accept_thread_h) return _not_wsa_error(ERC_CRTHREAD);

	return OK_OK;
}


int cfx_server::stop() {
	_stoped = true;
	// int nb_mode = 1;
	// if (0 != ioctlsocket(sock(), FIONBIO, (u_long *)&nb_mode))
	//	return _wsa_error(ERC_SOCK_STOP);

	// try to create fake socket to stop server
	_fs_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == _fs_sock) return _wsa_error(ERC_SERV_CANTSTOPC);

	int res = ::connect(_fs_sock, (sockaddr *)saddr_in(), sizeof(sockaddr_in));
	if (0 != res) {
		closesocket(_fs_sock);
		_fs_sock = INVALID_SOCKET;
		return _wsa_error(ERC_SERV_CANTSTOPC);
	}

	return OK_OK;
}


int cfx_server::kill(const bool wait) {
	if (wait) {
		stop();
		WaitForSingleObject(kill_mtx(), INFINITE);
		ReleaseMutex(kill_mtx());
		return cfx_socket::kill();
	} else {
		//FIXME:
		int res = cfx_socket::kill();
		stop();
		TerminateThread(_accept_thread_h, 0);
		return res;
	}

	return OK_OK;
}


cfx_client *cfx_server::new_client() const {
	return new cfx_client;
}


void cfx_server::del_client(cfx_client *clnt) {
	delete clnt;
}



////////////////////////////////////////////////////////////////////////////////
// cfx_client (public definitions)
int cfx_client::connect(const char *const addr, const unsigned short port) {
	saddr_in()->sin_port = htons(port);
	int res = resolve_addr(addr, &saddr_in()->sin_addr);
	if (OK_OK != res) return _wsa_error(res);

	res = ::connect(sock(), (sockaddr *)saddr_in(), saddr_in_sz());
	if (0 != res) return _wsa_error(ERC_SOCK_CONNECT);
	
	return OK_OK;
}


int cfx_client::recv_file() {
	_thread_h = CreateThread(NULL, 0, LPTHREAD_START_ROUTINE(_recv_proc), (void *)this,
				0, &_thread_id);

	if (NULL == _thread_h) return _not_wsa_error(ERC_CRTHREAD);

	return OK_OK;
}


int cfx_client::send_file(char *const filename) {
	_filename = filename;
	_thread_h = CreateThread(NULL, 0, LPTHREAD_START_ROUTINE(_send_proc), (void *)this,
				0, &_thread_id);

	if (NULL == _thread_h) return _not_wsa_error(ERC_CRTHREAD);

	return OK_OK;
}


////////////////////////////////////////////////////////////////////////////////
// cfx_client (protected definitions)
void cfx_client::_init() {
	_filename = NULL;
	_buf_sz = _def_buf_sz;
}


DWORD __stdcall cfx_client::_recv_proc(cfx_client *const client) {
	client->_call_event(EV_WAITING4DATA, NULL);

	unsigned int file_sz = -1;
	int res = recv(client->sock(), (char *)&file_sz, sizeof(file_sz), 0);
	client->_call_event(EV_RECVING_INFO, NULL);

	sfx_file_info finfo;
	finfo.total_sz = file_sz;
	finfo.filename = NULL;
	finfo.done_sz = 0;
	finfo.file = NULL;
	finfo.buf = NULL;
	finfo.buf_sz = 0;
	client->_call_event(EV_RECVED_INFO, &finfo);
	client->_call_event(EV_NEED_FILE2W, &finfo);
	client->_filename = finfo.filename;

	finfo.buf = new char[client->_buf_sz];
	finfo.buf_sz = client->_buf_sz;
	if (0 < strlen(finfo.filename)) {
		cfx_fblk fblk;
		if (0 != fblk.block(finfo.filename)) finfo.file = NULL;
		else finfo.file = fopen(finfo.filename, "wb");
	} else {
		finfo.file = NULL;
	}
	if (NULL == finfo.file) {
		client->_not_wsa_error();
		client->_call_event(EV_KILLING, TO_PVOID(ERC_CLNT_CANTCRFL));
		closesocket(client->sock());
		client->_call_event(EV_KILLED, NULL);
		return ERC_ERROR;
	}

	client->_call_event(EV_RECVING_DATA, &finfo);
	for(;;) {
		res = recv(client->sock(), finfo.buf, finfo.buf_sz, 0);
		if (res == SOCKET_ERROR) {
			client->_wsa_error();
			client->_call_event(EV_KILLING, TO_PVOID(ERC_CLNT_ERRECV));
			break;
		}
		if (res == 0) break;
		fwrite(finfo.buf, sizeof(char), res, finfo.file);
		finfo.done_sz += res;

		client->_call_event(EV_RECVED_DATA, &finfo);
	}
	if (SOCKET_ERROR != res && 0 != res)
		client->_call_event(EV_RECV_END, TO_PVOID(OK_OK));
	if (finfo.done_sz != finfo.total_sz)
		client->_call_event(EV_ERROR, TO_PVOID(ERC_CLNT_SZ_DM));

	cfx_fblk fblk;
	fblk.ublock(finfo.filename);
	fclose(finfo.file);
	delete[] finfo.buf;

	client->_call_event(EV_KILLED, NULL);

	return OK_OK;
}


DWORD __stdcall cfx_client::_send_proc(cfx_client *const client) {
	client->_call_event(EV_SENDING_INFO, NULL);

	sfx_file_info finfo;
	finfo.filename = client->_filename;
	if (0 < strlen(finfo.filename)) {
		cfx_fblk fblk;
		if (0 != fblk.block(finfo.filename)) finfo.file = NULL;
		else finfo.file = fopen(finfo.filename, "rb");
	} else {
		finfo.file = NULL;
	}
	if (NULL == finfo.file) {
		client->_not_wsa_error();
		client->_call_event(EV_KILLING, TO_PVOID(ERC_CLNT_CANTOPNFL));
		closesocket(client->sock());
		client->_call_event(EV_KILLED, NULL);
		return ERC_ERROR;
	}
	fseek(finfo.file, 0, SEEK_END);
	// finfo.total_sz = ftell(finfo.file);
	fpos_t filesize64;
	fgetpos(finfo.file, &filesize64);
	finfo.total_sz = filesize64;
	fseek(finfo.file, 0, SEEK_SET);
	finfo.done_sz = 0;
	finfo.buf = new char[client->_buf_sz];
	finfo.buf_sz = client->_buf_sz;

	unsigned int file_sz;
	// file_sz[0] = 0;
	// file_sz[1] = finfo.totalsz_hi;
	file_sz = finfo.total_sz;
	int res = send(client->sock(), (char *)&file_sz, sizeof(file_sz), 0);
	client->_call_event(EV_SENT_INFO, &finfo);

	client->_call_event(EV_SENDING_DATA, &finfo);
	while(!feof(finfo.file)) {
		res = (int)fread(finfo.buf, sizeof(char), finfo.buf_sz, finfo.file);
		finfo.done_sz += res;
		res = send(client->sock(), finfo.buf, res, 0);
		if (SOCKET_ERROR == res) {
			client->_wsa_error();
			client->_call_event(EV_KILLING, TO_PVOID(ERC_CLNT_ERSEND));
			break;
		}
		client->_call_event(EV_SENT_DATA, &finfo);
	}

	if (SOCKET_ERROR != res) {
		client->_call_event(EV_SEND_END, &finfo);
	}
	cfx_fblk fblk;
	fblk.ublock(finfo.filename);
	fclose(finfo.file);
	closesocket(client->sock());
	delete finfo.buf;

	client->_call_event(EV_KILLED, NULL);
	return OK_OK;
}



};	// end of namespace "fx_ns"
Соседние файлы в папке fexchange