Unlike tcpSocket, this socket works as tcp socket in the different thread.
However, checking if the memberlist contains sender is not available. This will be fixed in next commit.
#The implementation files
set(network_src
network/tcpnetwork.cxx
+ network/server.cxx
+ network/client.cxx
)
set(ui_src
ui/about.cxx
validator/ipaddressvalidator.cxx \
structures/header.cxx \
network/tcpnetwork.cxx \
+ network/server.cxx \
+ network/client.cxx \
file/qtlockedfile.cxx \
settings.cxx \
main.cxx
#include <QtGui>
#include <QCoreApplication>
+#include <iostream>
#include "ui/jasmine_mainwindow.h"
#include "app_version.h"
#include "definition.h"
--- /dev/null
+#include "tcpnetwork.h"
+#include <cstring>
+#include "../structures/header.h"
+
+using namespace network;
+using namespace enc_hash;
+using namespace structures;
+using namespace std;
+
+//Tcp socket(for client) constructor
+tcpSocket::tcpSocket(const QString &senderName,const quint64 buffersize,QObject *parent):QTcpSocket(parent){
+ this->buffer_size=buffersize;
+ this->senderName=senderName;
+ this->event=tcpSocket::Size;
+ connect(this,SIGNAL(readyRead()),SLOT(client_receive_flag()));
+}
+//Moving event
+void tcpSocket::client_move_section(){
+ switch(this->event){
+ case tcpSocket::Size:
+ this->event=tcpSocket::Header;
+ break;
+ case tcpSocket::Header:
+ this->event=(this->head_data.fileName().isNull()?tcpSocket::Msg:tcpSocket::File);
+ break;
+ default:
+ this->event=tcpSocket::End;
+ break;
+ }
+ this->client_process_event();
+}
+//Dispatch events
+bool tcpSocket::client_process_event(){
+ bool result;
+#ifdef DEBUG
+ qDebug()<<"Client Event:"<<this->event;
+#endif
+ switch(this->event){
+ case tcpSocket::Size:
+ result=this->client_send_size();
+ break;
+ case tcpSocket::Header:
+ result=this->client_send_header();
+ break;
+ case tcpSocket::Msg:
+ result=this->client_send_msg();
+ break;
+ case tcpSocket::File:
+ result=this->client_send_file();
+ case tcpSocket::End:
+ emit this->sentData();
+ result=true;
+ break;
+ }
+ return result;
+}
+bool tcpSocket::client_flush(){
+ bool result=this->flush();
+ if(!result){
+ this->setErrorString(tr("The data couldn't be sent"));
+ emit this->error(QAbstractSocket::UnknownSocketError);
+ }
+ return result;
+}
+
+bool tcpSocket::client_send_size(){
+ this->head_data=header(this->senderName,this->msg);
+ QDataStream datastream(&this->tmp_buffer,QIODevice::WriteOnly);
+ datastream<<this->head_data;
+ quint16 size=(quint16)this->tmp_buffer.size();
+ this->write((char*)&size,sizeof(size));
+ if(!this->client_flush()) return false;
+#ifdef DEBUG
+ qDebug()<<"Client:size of header has been sent successfully. The size is:"<<size;
+#endif
+ this->client_move_section();
+ return true;
+}
+
+bool tcpSocket::client_send_header(){
+ this->write(this->tmp_buffer);
+ if(!this->client_flush()) return false;
+#ifdef DEBUG
+ qDebug()<<"Client:header has been sent successfully.";
+#endif
+ this->client_move_section();
+ return true;
+}
+
+bool tcpSocket::client_send_msg(){
+ QBuffer memoryStream(&this->msg);
+ if(!memoryStream.open(QIODevice::ReadOnly)){
+ this->setErrorString(tr("Memory Stream couldn't open."));
+ emit this->error(QAbstractSocket::UnknownSocketError);
+ return false;
+ }
+ while(this->write(memoryStream.read(this->buffer_size))>0)
+ if(!this->client_flush()){
+ memoryStream.close();
+ return false;
+ }
+ memoryStream.close();
+#ifdef DEBUG
+ qDebug()<<"Client:data has been sent successfully.";
+#endif
+ this->client_move_section();
+ return true;
+}
+
+bool tcpSocket::client_send_file(){
+return false;
+}
+
+void tcpSocket::client_receive_flag(){
+ if(this->bytesAvailable()<4) return;
+ QByteArray data=this->readAll();
+#ifdef DEBUG
+ qDebug()<<"Client:received flag:"<<(Flag)data.toInt();
+#endif
+ switch((Flag)data.toInt()){
+ case tcpSocket::accepted:
+ break;
+ case tcpSocket::refused:
+ this->setErrorString(tr("Access Refused"));
+ emit this->error(QAbstractSocket::UnknownSocketError);
+ return;
+ }
+}
+
+tcpSocket &tcpSocket::operator<<(const QString &msg){
+ if(this->state()!=QAbstractSocket::ConnectedState){
+ this->setErrorString(tr("Haven't been connecting yet."));
+ emit this->error(QAbstractSocket::NetworkError);
+ return (*this);
+ }
+ this->msg=msg.toUtf8();
+ this->event=tcpSocket::Size;
+ this->client_process_event();
+ return (*this);
+}
+
+tcpSocket &tcpSocket::operator<<(const QFileInfo &src_file){
+ this->src_file=src_file;
+ QtLockedFile file(src_file.fileName());
+ if(!this->state()!=QAbstractSocket::ConnectedState)return (*this);
+ this->head_data=header(this->senderName,QFileInfo(file));
+ QByteArray tmp_buffer;
+ QDataStream datastream(tmp_buffer);
+ quint16 size=(quint16)tmp_buffer.size();
+ datastream<<this->head_data;
+ this->write((char*)&size,sizeof(size)/sizeof(char));
+ if(!this->flush())return (*this);
+ this->write(tmp_buffer);
+ if(!this->flush())return (*this);
+ tmp_buffer.clear();
+ emit this->file_header_sent();
+ if(!file.open(QIODevice::ReadOnly)){
+ this->setErrorString(tr("The file stream couldn't open."));
+ emit this->error(QAbstractSocket::UnknownSocketError);
+ return (*this);
+ }
+ file.lock(QtLockedFile::ReadLock);
+ while(this->write(file.read(this->buffer_size))>0&&!this->check_canceled_then_abort()){
+ if(!this->flush())return (*this);
+ emit this->sending_file_progress(file.pos());
+ }
+ file.unlock();
+ file.close();
+ emit this->sentData();
+ return (*this);
+}
+
+//Threaded TCP socket (for client) implementation
+threadedTcpSocket::threadedTcpSocket(const AddressAndPort &to,const QString &senderName, const quint64 buffersize, QObject *parent):QThread(parent){
+ connect(this,SIGNAL(finished()),SLOT(deleteLater()));
+ connect(this,SIGNAL(terminated()),SLOT(deleteLater()));
+ this->locks[threadedTcpSocket::Mode].lockForWrite();
+ this->locks[threadedTcpSocket::SenderName].lockForWrite();
+ this->locks[threadedTcpSocket::BufferSize].lockForWrite();
+ this->locks[threadedTcpSocket::RWMode].lockForWrite();
+ this->locks[threadedTcpSocket::AddrAndPort].lockForWrite();
+ this->_addrPort=to;
+ this->mode=threadedTcpSocket::Client;
+ this->_senderName=senderName;
+ this->_buffersize=buffersize;
+ this->open_mode=QAbstractSocket::ReadWrite;
+ this->locks[threadedTcpSocket::Mode].unlock();
+ this->locks[threadedTcpSocket::SenderName].unlock();
+ this->locks[threadedTcpSocket::BufferSize].unlock();
+ this->locks[threadedTcpSocket::RWMode].unlock();
+ this->locks[threadedTcpSocket::AddrAndPort].unlock();
+}
+//Implementation of threadedTcpSocket
+threadedTcpSocket &threadedTcpSocket::operator<<(const QString &msg){
+ this->locks[threadedTcpSocket::Msg].lockForWrite();
+ this->_msg=msg;
+ this->locks[threadedTcpSocket::Msg].unlock();
+ this->start();
+ return (*this);
+}
+threadedTcpSocket &threadedTcpSocket::operator<<(const QFileInfo &file){
+ this->locks[threadedTcpSocket::File].lockForWrite();
+ this->_file=file;
+ this->locks[threadedTcpSocket::File].unlock();
+ this->start();
+ return (*this);
+}
--- /dev/null
+#include "tcpnetwork.h"
+#include <cstring>
+#include "../structures/header.h"
+
+using namespace network;
+using namespace enc_hash;
+using namespace structures;
+using namespace std;
+
+//Tcp server implementation
+tcpServer::tcpServer(quint64 buffersize, QObject *parent):QTcpServer(parent){this->buffersize=buffersize;}
+
+void tcpServer::incomingConnection(int handle){
+ threadedTcpSocket *socket=new threadedTcpSocket(this->buffersize,this);
+ socket->socketDescriptor(handle);
+ if(! emit this->pending(*socket)){
+ socket->exit(1);
+ return;
+ }
+ socket->start();
+ emit this->newConnection();
+}
+
+//Tcp socket implementation
+tcpSocket::tcpSocket(const quint64 buffersize, QObject *parent):QTcpSocket(parent){
+ this->buffer_size=buffersize;
+ this->canceled=false;
+ this->event=tcpSocket::Size;
+ connect(this,SIGNAL(readyRead()),SLOT(read_data()));
+ connect(this,SIGNAL(bytesWritten(qint64)),SLOT(move_next_section(const qint64)));
+#ifdef DEBUG
+ qDebug()<<"Server:tcpSocket is constructed.";
+ qDebug()<<"Server:blocked:"<<this->signalsBlocked();
+#endif
+}
+void tcpSocket::read_data(){
+ /*TODO: remove this loop*/
+ while(this->bytesAvailable()>=0){
+#ifdef DEBUG
+ qDebug()<<"Server:server Event Mode:"<<this->event;
+ qDebug()<<"Server:server Available bytes:"<<this->bytesAvailable();
+#endif
+ switch(this->event){
+ case tcpSocket::Size: this->size_event(); break;
+ case tcpSocket::Header: this->header_event(); break;
+ case tcpSocket::Msg: this->msg_event(); break;
+ case tcpSocket::File: this->file_event(); break;
+ default: this->disconnectFromHost(); return;
+ }
+ }
+}
+void tcpSocket::move_next_section(const qint64 size){
+ Q_UNUSED(size);
+ switch(this->event){
+ case tcpSocket::Size: this->event=tcpSocket::Header; break;
+ case tcpSocket::Header:
+ this->event=(this->head_data.fileName().isEmpty()?tcpSocket::Msg:tcpSocket::File);
+ break;
+ default:
+ this->event=tcpSocket::End;
+ break;
+ }
+}
+void tcpSocket::send_flag(const Flag flag){
+ QByteArray send_data;
+ send_data.append((int)flag);
+ this->write(send_data);
+ if(!this->flush()){
+ this->setErrorString(tr("Accept signal couldn't be sent."));
+ emit this->error(QAbstractSocket::UnknownSocketError);
+ return;
+ }
+#ifdef DEBUG
+ qDebug()<<"Server:the flag:"<<flag<<" has been sent.";
+#endif
+}
+
+void tcpSocket::size_event(){
+ if(this->bytesAvailable()<2) return;
+ char size_buf[2];
+ this->read(size_buf,sizeof(size_buf));
+ memcpy(&this->header_size,size_buf,
+ (sizeof(quint16)>sizeof(size_buf))?sizeof(quint16):sizeof(size_buf)
+ );
+ //this->send_flag(tcpSocket::accepted);
+ this->move_next_section(0);
+}
+void tcpSocket::header_event(){
+ if(this->bytesAvailable()<this->header_size) return;
+ else{
+ QByteArray data=this->read(this->header_size);
+ QDataStream datastream(data);
+ datastream>>this->head_data;
+ if(this->head_data==structures::header()){
+ this->setErrorString(tr("The header is empty."));
+ emit this->error(QAbstractSocket::UnknownSocketError);
+ this->disconnectFromHost();
+ return;
+ }
+ if(!this->head_data.fileName().isEmpty()){
+ this->where_to_save=(emit this->file_pending());
+ if(this->where_to_save.isEmpty()){
+ this->write(QByteArray(1,0x00));
+
+ this->setErrorString(tr("The filename is empty."));
+ emit this->error(QAbstractSocket::UnknownSocketError);
+
+ this->disconnectFromHost();
+ return;
+ }
+ }
+ emit this->header_received();
+ }
+ //this->send_flag(tcpSocket::accepted);
+ this->move_next_section(0);
+}
+void tcpSocket::msg_event(){
+ if((quint64)this->bytesAvailable()<this->header_data().datasize()) return;
+ quint64 final_readsize=this->head_data.datasize()%this->buffer_size,
+ read_count=(this->head_data.datasize()-final_readsize)/this->buffer_size;
+ QByteArray msg;
+ for(quint64 count=0;count<read_count;count++){
+ if(this->check_canceled_then_abort())return;
+ msg+=this->read(this->buffer_size);
+ }
+ if(this->check_canceled_then_abort())return;
+ msg+=this->read(final_readsize);
+
+ rmd6 generator;
+ if(this->head_data.ripemd160()==generator.compute_hash(msg)){
+ emit this->msg_received(QString::fromUtf8(msg.data()));
+ }
+ else{
+#ifdef DEBUG
+ qDebug()<<"Hash digest error. the received message may be broken or modified.";
+ qDebug()<<"this->head_date.ripemd160():"<<this->head_data.ripemd160();
+ qDebug()<<"RIPEMD160-Hash"<<generator.compute_hash(msg);
+#endif
+ this->setErrorString(tr("The data has been broken."));
+ emit this->error(QAbstractSocket::UnknownSocketError);
+ }
+ //this->send_flag(tcpSocket::accepted);
+ this->move_next_section(0);
+}
+void tcpSocket::file_event(){
+ if((quint64)this->bytesAvailable()<this->header_data().datasize()) return;
+ quint64 final_readsize=this->head_data.datasize()%this->buffer_size,
+ read_count=(this->head_data.datasize()-final_readsize)/this->buffer_size;
+ streamopen:
+ if(this->where_to_save.isEmpty()){
+ this->setErrorString(tr("The filename is empty."));
+ emit this->error(QAbstractSocket::UnknownSocketError);
+ this->disconnectFromHost();
+ return;
+ }
+ QFile file(this->where_to_save,this);
+ if(!file.open(QIODevice::Truncate|QIODevice::WriteOnly)){
+ this->where_to_save=emit this->fileStream_openFailed(file.error(),file.errorString());
+ goto streamopen;
+ }
+ for(quint64 count=0;count<read_count&&!this->check_canceled_then_abort();count++){
+ if(this->check_canceled_then_abort())return;
+ file.write(this->read(this->buffer_size));
+ emit this->file_receive_progress(file.pos());
+ }
+ if(this->check_canceled_then_abort())return;
+ file.write(this->read(final_readsize));
+ emit this->file_receive_progress(file.pos());
+ file.close();
+
+ rmd6 generator;
+ if(this->head_data.ripemd160()==generator.compute_hash(file))
+ emit this->file_saved();
+ else{
+ this->setErrorString(tr("The file has been broken."));
+ emit this->error(QAbstractSocket::UnknownSocketError);
+ }
+ //this->send_flag(tcpSocket::accepted);
+ this->move_next_section(0);
+}
+void threadedTcpSocket::header_received(){
+ tcpSocket *socket=qobject_cast<tcpSocket *>(this->sender());
+ this->locks[threadedTcpSocket::HeaderData].lockForWrite();
+ this->_header=socket->header_data();
+ this->locks[threadedTcpSocket::HeaderData].unlock();
+}
+
+//Threaded TCP socket (for server) implemantation
+threadedTcpSocket::threadedTcpSocket(const quint64 buffersize, QObject *parent):QThread(parent){
+ connect(this,SIGNAL(finished()),SLOT(deleteLater()));
+ connect(this,SIGNAL(terminated()),SLOT(deleteLater()));
+ this->locks[threadedTcpSocket::Mode].lockForWrite();
+ this->locks[threadedTcpSocket::BufferSize].lockForWrite();
+ this->locks[threadedTcpSocket::RWMode].lockForWrite();
+ this->mode=threadedTcpSocket::Session;
+ this->_buffersize=buffersize;
+ this->open_mode=QAbstractSocket::ReadWrite;
+ this->locks[threadedTcpSocket::Mode].unlock();
+ this->locks[threadedTcpSocket::BufferSize].unlock();
+ this->locks[threadedTcpSocket::RWMode].unlock();;
+}
using namespace structures;
using namespace std;
-//Tpc server implementation
-tcpServer::tcpServer(quint64 buffersize, QObject *parent):QTcpServer(parent){this->buffersize=buffersize;}
-void tcpServer::incomingConnection(int handle){
- tcpSocket *socket=new tcpSocket(this->buffersize,this);
- if(!socket->setSocketDescriptor(handle)){
- emit this->socket_error(*socket);
- return;
+void tcpSocket::disconnectFromHostImplementation(){
+ this->canceled=true;
+ QTcpSocket::disconnectFromHost();
+}
+bool tcpSocket::check_canceled_then_abort(){
+ if(this->canceled){
+ this->setErrorString(tr("The process has been canceled by user."));
+ emit this->error(QAbstractSocket::UnknownSocketError);
+ this->abort();
+ return true;
}
- if(emit this->pending(*socket)) this->newConnection();
- else socket->abort();
+ return false;
}
+void tcpSocket::cancel(){this->canceled=true;}
+QString tcpSocket::path_to_save() const{return this->where_to_save;}
+header tcpSocket::header_data() const{return this->head_data;}
-//Tcp socket implementation
-tcpSocket::tcpSocket(const quint64 buffersize, QObject *parent):QTcpSocket(parent){
- this->buffer_size=buffersize;
- this->canceled=false;
- this->event=tcpSocket::headsize;
- connect(this,SIGNAL(disconnected()),SLOT(deleteLater()));
- connect(this,SIGNAL(readyRead()),SLOT(read_data()));
-#ifdef DEBUG
- qDebug()<<"TcpSocket is constructed.";
- qDebug()<<"Status:";
- qDebug()<<"Blocked:"<<this->signalsBlocked();
-#endif
+/* Threaded TCP socket implementation. */
+QString threadedTcpSocket::senderName(){
+ QString senderName;
+ this->locks[threadedTcpSocket::SenderName].lockForRead();;
+ senderName=this->_senderName;
+ this->locks[threadedTcpSocket::SenderName].unlock();
+ return senderName;
}
-tcpSocket::tcpSocket(const QString &senderName,const quint64 buffersize,QObject *parent):QTcpSocket(parent){
- this->buffer_size=buffersize;
- this->senderName=senderName;
+quint64 threadedTcpSocket::buffersize(){
+ quint64 size;
+ this->locks[threadedTcpSocket::BufferSize].lockForRead();
+ size=this->_buffersize;
+ this->locks[threadedTcpSocket::BufferSize].unlock();
+ return size;
+}
+AddressAndPort threadedTcpSocket::peerAddr(){
+ this->locks[threadedTcpSocket::AddrAndPort].lockForRead();
+ AddressAndPort r=this->_addrPort;
+ this->locks[threadedTcpSocket::AddrAndPort].unlock();
+ return r;
+}
+void threadedTcpSocket::to(const AddressAndPort &to){
+ this->locks[threadedTcpSocket::AddrAndPort].lockForWrite();
+ this->_addrPort=to;
+ this->locks[threadedTcpSocket::AddrAndPort].unlock();
+}
+
+int threadedTcpSocket::socketDescriptor(){
+ int desc;
+ this->locks[threadedTcpSocket::Descriptor].lockForRead();
+ desc=this->_descriptor;
+ this->locks[threadedTcpSocket::Descriptor].unlock();
+ return desc;
+}
+
+void threadedTcpSocket::senderName(const QString &name){
+ this->locks[threadedTcpSocket::SenderName].lockForWrite();
+ this->_senderName=name;
+ this->locks[threadedTcpSocket::SenderName].unlock();
+}
+void threadedTcpSocket::buffersize(const quint64 size){
+ this->locks[threadedTcpSocket::BufferSize].lockForWrite();;
+ this->_buffersize=size;
+ this->locks[threadedTcpSocket::BufferSize].unlock();
+}
+void threadedTcpSocket::socketDescriptor(const int descriptor){
+ this->locks[threadedTcpSocket::Descriptor].lockForWrite();
+ this->_descriptor=descriptor;
+ this->locks[threadedTcpSocket::Descriptor].unlock();
+}
+QIODevice::OpenMode threadedTcpSocket::readWriteMode(){
+ QIODevice::OpenMode mode;
+ this->locks[threadedTcpSocket::RWMode].lockForRead();
+ mode=this->open_mode;
+ this->locks[threadedTcpSocket::RWMode].unlock();
+ return mode;
+}
+void threadedTcpSocket::readWriteMode(const QIODevice::OpenMode openMode){
+ this->locks[threadedTcpSocket::RWMode].lockForWrite();
+ this->open_mode=openMode;
+ this->locks[threadedTcpSocket::RWMode].unlock();
+}
+header threadedTcpSocket::head_data(){
+ this->locks[threadedTcpSocket::HeaderData].lockForRead();
+ header head=this->_header;
+ this->locks[threadedTcpSocket::HeaderData].unlock();
+ return head;
}
-void tcpSocket::read_data(){
- while(this->bytesAvailable()>0){
+void threadedTcpSocket::run(){
#ifdef DEBUG
- qDebug()<<"Server Event Mode:"<<this->event<<"Server Available bytes:"<<this->bytesAvailable();
+ qDebug()<<"threadedTcpSocket has been created and running.";
#endif
- switch(this->event){
- case tcpSocket::headsize: this->size_event(); break;
- case tcpSocket::header_receive: this->header_event(); break;
- case tcpSocket::msg: this->msg_event(); break;
- case tcpSocket::file: this->file_event(); break;
- }
- }
-}
-void tcpSocket::size_event(){
- if(this->bytesAvailable()<2) return;
- char size_buf[2];
- this->read(size_buf,sizeof(size_buf));
- memcpy(&this->header_size,size_buf,
- (sizeof(quint16)>sizeof(size_buf))?sizeof(quint16):sizeof(size_buf)
- );
- this->event=tcpSocket::header_receive;
-}
-void tcpSocket::header_event(){
- if(this->bytesAvailable()<this->header_size) return;
- else{
- QByteArray data=this->read(this->header_size);
- QDataStream datastream(data);
- datastream>>this->head_data;
- if(this->head_data==structures::header()){
- this->setErrorString(tr("The header is empty."));
- emit this->error(QAbstractSocket::UnknownSocketError);
- this->disconnectFromHost();
- return;
- }
- if(!this->head_data.fileName().isEmpty()){
- this->where_to_save=(emit this->file_pending());
- if(this->where_to_save.isEmpty()){
- this->write(QByteArray(1,0x00));
+ this->locks[threadedTcpSocket::BufferSize].lockForRead();
+ this->locks[threadedTcpSocket::SenderName].lockForRead();
+ tcpSocket *socket=
+ (this->mode==threadedTcpSocket::Client)?
+ new tcpSocket(this->_senderName,this->_buffersize):
+ new tcpSocket(this->_buffersize);
+ this->locks[threadedTcpSocket::BufferSize].unlock();
+ this->locks[threadedTcpSocket::SenderName].unlock();
+ /*Common signals*/
+ connect(socket,SIGNAL(error(QAbstractSocket::SocketError)),
+ SLOT(error_occured(QAbstractSocket::SocketError)),
+ Qt::BlockingQueuedConnection);
+ connect(socket,SIGNAL(connected()),SLOT(host_connected()),Qt::BlockingQueuedConnection);
+ connect(socket,SIGNAL(connected()),SIGNAL(connected()));
+ connect(socket,SIGNAL(disconnected()),SIGNAL(disconnected()));
+ connect(socket,SIGNAL(disconnected()),SLOT(host_disconnected()),
+ Qt::BlockingQueuedConnection);
+ connect(socket,SIGNAL(hostFound()),SIGNAL(hostFound()));
+ connect(socket,SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &,QAuthenticator*)),
+ SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &,QAuthenticator*)));
+ connect(socket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
+ SIGNAL(stateChanged(QAbstractSocket::SocketState)));
- this->setErrorString(tr("The filename is empty."));
- emit this->error(QAbstractSocket::UnknownSocketError);
+ int descriptor;
+ this->locks[threadedTcpSocket::Mode].lockForRead();
+ switch(this->mode){
+ case threadedTcpSocket::Client:
+ connect(socket,SIGNAL(sentData()),SIGNAL(sentData()));
+ connect(socket,SIGNAL(sentData()),SLOT(quit()));
+ connect(socket,SIGNAL(file_header_sent()),SIGNAL(file_header_sent()));
+ connect(socket,SIGNAL(sending_file_progress(quint64)),SIGNAL(sending_file_progress(quint64)));
+ this->locks[threadedTcpSocket::AddrAndPort].lockForRead();
+ socket->connectToHost(this->_addrPort.first,this->_addrPort.second);
+ this->locks[threadedTcpSocket::AddrAndPort].unlock();
+ break;
+ case threadedTcpSocket::Session:
+ this->locks[threadedTcpSocket::Descriptor].lockForRead();
+ descriptor=this->_descriptor;
+ this->locks[threadedTcpSocket::Descriptor].unlock();
+ connect(socket,SIGNAL(msg_received(const QString &)),SIGNAL(msg_received(const QString &)));
+ connect(socket,SIGNAL(msg_received(const QString &)),SLOT(quit()));
- this->disconnectFromHost();
- return;
- }
+ connect(socket,SIGNAL(file_receive_progress(const quint64)),SIGNAL(file_receive_progress(const quint64)));
+ connect(socket,SIGNAL(file_saved()),SIGNAL(file_saved()));
+ connect(socket,SIGNAL(file_saved()),SLOT(quit()));
+ connect(socket,SIGNAL(header_received()),SLOT(header_received()));
+ if(!socket->setSocketDescriptor(descriptor)){
+ this->_error=socket->errorString();
+ emit this->error(socket->error());
}
+ this->_addrPort.first=socket->peerAddress();
+ this->_addrPort.second=socket->peerPort();
+ break;
}
- if(this->head_data.fileName().isEmpty()) this->event=tcpSocket::msg;
- else this->event=tcpSocket::file;
-}
-void tcpSocket::msg_event(){
- if((quint64)this->bytesAvailable()<this->header_data().datasize()) return;
- quint64 final_readsize=this->head_data.datasize()%this->buffer_size,
- read_count=(this->head_data.datasize()-final_readsize)/this->buffer_size;
- QByteArray msg;
- for(quint64 count=0;count<read_count;count++){
- if(this->check_canceled_then_abort())return;
- msg+=this->read(this->buffer_size);
- }
- if(this->check_canceled_then_abort())return;
- msg+=this->read(final_readsize);
-
- rmd6 generator;
- if(this->head_data.ripemd160()==generator.compute_hash(msg))
- emit this->msg_received(QString::fromUtf8(msg.data()));
- else{
- this->setErrorString(tr("The data has been broken."));
- emit this->error(QAbstractSocket::UnknownSocketError);
- }
- this->disconnectFromHost();
-}
-void tcpSocket::file_event(){
- if((quint64)this->bytesAvailable()<this->header_data().datasize()) return;
- quint64 final_readsize=this->head_data.datasize()%this->buffer_size,
- read_count=(this->head_data.datasize()-final_readsize)/this->buffer_size;
- streamopen:
- if(this->where_to_save.isEmpty()){
- this->setErrorString(tr("The filename is empty."));
- emit this->error(QAbstractSocket::UnknownSocketError);
- this->disconnectFromHost();
+ this->locks[threadedTcpSocket::Mode].unlock();
+ if(this->exec()==0){
+ socket->disconnectFromHost();
return;
}
- QFile file(this->where_to_save,this);
- if(!file.open(QIODevice::Truncate|QIODevice::WriteOnly)){
- this->where_to_save=emit this->fileStream_openFailed(file.error(),file.errorString());
- goto streamopen;
- }
- for(quint64 count=0;count<read_count&&!this->check_canceled_then_abort();count++){
- if(this->check_canceled_then_abort())return;
- file.write(this->read(this->buffer_size));
- emit this->file_receive_progress(file.pos());
- }
- if(this->check_canceled_then_abort())return;
- file.write(this->read(final_readsize));
- emit this->file_receive_progress(file.pos());
- file.close();
-
- rmd6 generator;
- if(this->head_data.ripemd160()==generator.compute_hash(file))
- emit this->file_saved();
- else{
- this->setErrorString(tr("The file has been broken."));
- emit this->error(QAbstractSocket::UnknownSocketError);
- }
- this->disconnectFromHost();
+ //Cleanup
+ socket->abort();
}
-
-bool tcpSocket::check_canceled_then_abort(){
- if(this->canceled){
- this->setErrorString(tr("The process has been canceled by user."));
- emit this->error(QAbstractSocket::UnknownSocketError);
- this->abort();
- return true;
+void threadedTcpSocket::host_connected(){
+ tcpSocket *socket=qobject_cast<tcpSocket *>(this->sender());
+#ifdef DEBUG
+ qDebug()<<"Client:"<<socket->peerAddress().toString()<<"has been connected properly.";
+#endif
+ this->locks[threadedTcpSocket::Msg].lockForRead();
+ bool r=this->_msg.isNull();
+ this->locks[threadedTcpSocket::Msg].unlock();
+ if(r){
+ this->locks[threadedTcpSocket::File].lockForRead();
+ (*socket)<<this->_file;
+ this->locks[threadedTcpSocket::File].unlock();
+ }else{
+ this->locks[threadedTcpSocket::Msg].lockForRead();
+ (*socket)<<this->_msg;
+ this->locks[threadedTcpSocket::Msg].unlock();
}
- return false;
}
-void tcpSocket::disconnectFromHostImplementation(){
- this->canceled=true;
- QTcpSocket::disconnectFromHost();
+void threadedTcpSocket::host_disconnected(){
+ tcpSocket *socket=qobject_cast<tcpSocket *>(this->sender());
+ socket->close();
+#ifdef DEBUG
+ qDebug()<<"Socket("<<((this->mode==threadedTcpSocket::Session)?"Session":"Client")<<"):Connection closed successful.";
+#endif
+ socket->deleteLater();
}
-tcpSocket &tcpSocket::operator<<(const QString &msg){
- if(this->state()!=QAbstractSocket::ConnectedState) return (*this);
- this->head_data=header(this->senderName,msg);
- QByteArray tmp_buffer;
- QDataStream datastream(&tmp_buffer,QIODevice::WriteOnly);
- datastream<<this->head_data;
- quint16 size=(quint16)tmp_buffer.size();
- this->write((char*)&size,sizeof(size)/sizeof(char));
- if(!this->flush())return (*this);
- this->write(tmp_buffer);
- if(!this->flush())return (*this);
- tmp_buffer.clear();
- tmp_buffer=msg.toUtf8();
- QBuffer memoryStream(&tmp_buffer,this);
- if(!memoryStream.open(QIODevice::ReadOnly)){
- this->setErrorString(tr("Memory Stream couldn't open."));
- emit this->error(QAbstractSocket::UnknownSocketError);
- return (*this);
- }
- while(this->write(memoryStream.read(this->buffer_size))>0)
- if(!this->flush())return (*this);
- memoryStream.close();
- emit this->sentData();
- return (*this);
-}
-tcpSocket &tcpSocket::operator<<(const QFileInfo &src_file){
- QtLockedFile file(src_file.fileName());
- if(!this->state()!=QAbstractSocket::ConnectedState)return (*this);
- this->head_data=header(this->senderName,QFileInfo(file));
- QByteArray tmp_buffer;
- QDataStream datastream(tmp_buffer);
- quint16 size=(quint16)tmp_buffer.size();
- datastream<<this->head_data;
- this->write((char*)&size,sizeof(size)/sizeof(char));
- if(!this->flush())return (*this);
- this->write(tmp_buffer);
- if(!this->flush())return (*this);
- tmp_buffer.clear();
- emit this->file_header_sent();
- if(!file.open(QIODevice::ReadOnly)){
- this->setErrorString(tr("The file stream couldn't open."));
- emit this->error(QAbstractSocket::UnknownSocketError);
- return (*this);
- }
- file.lock(QtLockedFile::ReadLock);
- while(this->write(file.read(this->buffer_size))>0&&!this->check_canceled_then_abort()){
- if(!this->flush())return (*this);
- emit this->sending_file_progress(file.pos());
- }
- file.unlock();
- file.close();
- emit this->sentData();
- return (*this);
+void threadedTcpSocket::error_occured(const QAbstractSocket::SocketError error){
+ tcpSocket *socket=(tcpSocket*)this->sender();
+ this->locks[threadedTcpSocket::Error].lockForWrite();
+ this->_error=socket->errorString();
+ this->locks[threadedTcpSocket::Error].unlock();
+ this->exit(1);
+ emit this->error(error);
+}
+QString threadedTcpSocket::errorString(){
+ this->locks[threadedTcpSocket::Error].lockForRead();
+ QString str=this->_error;
+ this->locks[threadedTcpSocket::Error].unlock();
+ return str;
}
-void tcpSocket::cancel(){this->canceled=true;}
-QString tcpSocket::path_to_save() const{return this->where_to_save;}
-header tcpSocket::header_data() const{return this->head_data;}
#include "../definition.h"
#include "../ported_rmd6/rmd6.h"
-#define default_bandwidth 0x400
+const quint64 default_bandwidth = 0x400;
+using namespace structures;
namespace network{
class tcpSocket;
class threadedTcpSocket;
public:
tcpServer(quint64 buffersize=default_bandwidth,QObject *parent=NULL);
signals:
- bool pending(const tcpSocket &) const;
- bool pending(const threadedTcpSocket &) const;
- void socket_error(const tcpSocket &) const;
+ bool pending(threadedTcpSocket &) const;
+ void socket_error(const threadedTcpSocket &) const;
protected:
void incomingConnection(int handle);
private:
quint64 buffersize;
};
+
+ class threadedTcpSocket:public QThread{
+ Q_OBJECT
+ public:
+ //This constructor is for server. it works as a session.
+ threadedTcpSocket(const quint64 buffersize=default_bandwidth,QObject *parent=NULL);
+ //This constructor is for client. it works as a client.
+ threadedTcpSocket(const AddressAndPort &,const QString &senderName,const quint64 buffersize=default_buffer_size,QObject *parent=NULL);
+ /*
+ To access these member, accessing critical sections must be needed.
+ Hence, these member uses QReadWriteLock to lock them; Making constant functions is not possible.
+ */
+ quint64 buffersize();
+ QString senderName();
+ AddressAndPort peerAddr();
+ header head_data();
+ int socketDescriptor();
+ void socketDescriptor(const int);
+ void buffersize(const quint64);
+ void senderName(const QString &);
+ void readWriteMode(const QIODevice::OpenMode);
+ void to(const AddressAndPort &);
+ QIODevice::OpenMode readWriteMode();
+ QString errorString();
+ threadedTcpSocket &operator<<(const QString &);
+ threadedTcpSocket &operator<<(const QFileInfo &);
+ private:
+ enum Mode{Session,Client} mode;
+ enum lockID{
+ Mode =0,
+ BufferSize =1,
+ SenderName =2,
+ Msg =3,
+ File =4,
+ Descriptor =5,
+ RWMode =6,
+ Error =7,
+ AddrAndPort =8,
+ HeaderData =9
+ };
+ void run();
+ QIODevice::OpenMode open_mode;
+ QReadWriteLock locks[10];
+ int _descriptor;
+ quint64 _buffersize;
+ QString _senderName,_msg,_error;
+ QFileInfo _file;
+ AddressAndPort _addrPort;
+ header _header;
+ private slots:
+ void header_received();
+ void error_occured(const QAbstractSocket::SocketError);
+ void host_connected();
+ void host_disconnected();
+ signals:
+ QString file_pending() const;
+ QString fileStream_openFailed(const QFile::FileError &,const QString &) const;
+
+ void msg_received(const QString &) const;
+
+ void file_receive_progress(const quint64 streamPos) const;
+ void file_saved() const;
+
+ void sentData();
+ void file_header_sent();
+ void sending_file_progress(const quint64 pos);
+
+ void connected();
+ void disconnected();
+ void error(const QAbstractSocket::SocketError);
+ void hostFound();
+ void proxyAuthenticationRequired(const QNetworkProxy &,QAuthenticator *);
+ void stateChanged(QAbstractSocket::SocketState);
+ };
+
class tcpSocket:public QTcpSocket{
Q_OBJECT
public:
tcpSocket(const QString &senderName,const quint64 buffersize=default_buffer_size,QObject *parent=NULL);
QString path_to_save() const;
structures::header header_data() const;
- //Calling operator<<(QFile), the specified file stream will be copied.
+
tcpSocket &operator<<(const QString &),
&operator<<(const QFileInfo &);
signals:
QString file_pending() const;
QString fileStream_openFailed(const QFile::FileError &,const QString &) const;
+ void header_received() const;
void msg_received(const QString &) const;
void file_receive_progress(const quint64 streamPos) const;
void file_saved() const;
- void sentData();
- void file_header_sent();
- void sending_file_progress(const quint64 pos);
- private slots:
- void read_data();
- void cancel();
+ void sentData() const;
+ void file_header_sent() const;
+ void sending_file_progress(const quint64 pos) const;
protected:
void disconnectFromHostImplementation();
private:
- enum mode{headsize,header_receive,msg,file} event;
+ enum section {
+ Size,
+ Header,
+ Msg,
+ File,
+ End
+ } event;
+
+ enum Flag {accepted=0,refused=1};
void size_event();
void header_event();
void msg_event();
void file_event();
bool check_canceled_then_abort();
+
+ void client_move_section();
+ bool client_process_event();
+ bool client_flush();
+ bool client_send_size();
+ bool client_send_header();
+ bool client_send_msg();
+ bool client_send_file();
+
//The size of header needs to be larger than 0, and smaller than or equal to 0xFFFF.
quint16 header_size,buffer_size,timeout_time;
structures::header head_data;
bool canceled;
QString where_to_save, senderName;
+ //These classes are only used for client to store serialized header data.
+ QByteArray tmp_buffer,msg;
+ QFileInfo src_file;
+ private slots:
+ void read_data();
+ void move_next_section(const qint64);
+ void send_flag(const Flag flag);
+ void cancel();
+
+ void client_receive_flag();
};
}
This document is written about what this software is, the warnings, and how to build.
What this software?
-This software will provide simple P2P(like bittorrent) messenger.
+This software will provide very very simple P2P messenger.
Warnings:
-Please don't build and/or use this version unless you have knowledge about computer; It could do unsafe behavior because it is under development.
+Please don't build and/or use this version unless you have knowledge about computer; It could do unsafe behavior because it is still under development.
How to build:
Before building, please read warnings section million times.
using namespace structures;
using namespace enc_hash;
header::header():rmd6_hash(5){}
-header::header(const QString &senderName,const QString &msg): sendername(senderName),data_size(msg.size()){
+header::header(const QString &senderName,const QByteArray &msg): sendername(senderName),data_size(msg.size()){
rmd6 gen;
- this->rmd6_hash=gen.compute_hash(msg.toUtf8());
+ this->rmd6_hash=gen.compute_hash(msg);
}
header::header(const QString &senderName,const QFileInfo &fileinfo):
filename(fileinfo.completeBaseName()),sendername(senderName),
void header::senderName(const QString &sendername){this->sendername=sendername;}
void header::fileName(const QString &filename){this->filename=filename;}
void header::datasize(const quint64 size){this->data_size=size;}
+void header::operator=(const header &other){
+ this->senderName(other.senderName());
+ this->fileName(other.fileName());
+ this->datasize(other.datasize());
+ this->rmd6_hash=other.ripemd160();
+}
bool header::operator==(const header &head) const{
return (this->sendername==head.senderName()&&
this->filename==head.fileName()&&
public:
header();
header(const QString &senderName,const QFileInfo &fileinfo);
- header(const QString &senderName,const QString &msg);
+ header(const QString &senderName,const QByteArray &msg);
QString fileName()const,
senderName()const;
quint64 datasize()const;
QVector<quint32> ripemd160() const;
bool operator==(const header &) const;
bool operator!=(const header &) const;
+ void operator=(const header &);
friend QDataStream &operator<<(QDataStream &out,const header &value){
out<<value.data_size<<value.rmd6_hash<<value.sendername<<value.filename;
return out;
connect(this->sendTextEditor,SIGNAL(invalidLink(const QString &)),SLOT(invalidLink(const QString &)));
connect(this->sendTextEditor,SIGNAL(sendTriggered()),this->sendButton,SLOT(click()));
- connect(this->mainServer,SIGNAL(pending(const tcpSocket &)),SLOT(tcpserver_pending(const tcpSocket &)));
+ connect(this->mainServer,SIGNAL(pending(threadedTcpSocket &)),
+ SLOT(tcpserver_pending(threadedTcpSocket &)));
}
mainWindow::~mainWindow(){}
void mainWindow::closeEvent(QCloseEvent *event){
}
void mainWindow::showEvent(QShowEvent *event){
if(rev==0)
- this->status->showMessage("<font color=\"#ff0000\">Don't forget almost all features are not implemented!</font>",default_status_interval);
+ this->status->showMessage("Don't forget almost all features are not implemented!",default_status_interval);
event->accept();
}
#pragma omp parallel for
#endif
for(int index=0;index<addressList.size();index++){
- tcpSocket *client=new tcpSocket(this->setting.name(),default_buffer_size,this);
- connect(client,SIGNAL(connected()),SLOT(tcpclient_connected()));
+ threadedTcpSocket *client=new threadedTcpSocket(
+ addressList[index],this->setting.name(),default_buffer_size,this);
connect(client,SIGNAL(error(const QAbstractSocket::SocketError &)),SLOT(tcpclient_error(const QAbstractSocket::SocketError &)));
connect(client,SIGNAL(sentData()),SLOT(sentData()));
- client->connectToHost(addressList[index].first,addressList[index].second);
+ (*client)<<this->sendTextEditor->html();
}
}
void mainWindow::tcpclient_error(const QAbstractSocket::SocketError &error){
Q_UNUSED(error);
- tcpSocket *client=qobject_cast<tcpSocket *>(this->sender());
+ threadedTcpSocket *client=qobject_cast<threadedTcpSocket *>(this->sender());
#ifdef DEBUG
qDebug()<<"Error:"<<client->errorString();
#endif
- client->close();
-}
-
-void mainWindow::tcpclient_connected(){
- tcpSocket *client=qobject_cast<tcpSocket *>(this->sender());
- (*client)<<this->sendTextEditor->html();
- client->disconnectFromHost();
- client->close();
+ client->exit(1);
}
void mainWindow::on_sendFileAction_triggered(){
void mainWindow::openMember(const QString &file){
/*
Does QFileDialog have a bug??
- If you select file by double clicking, fileSelected signal will be emitted twice.
+ Selecting file by double clicking, fileSelected signal will be emitted twice.
*/
if(this->sender()!=NULL&&this->sender()==this->beforesender) return;
this->beforesender=this->sender();
}
settings mainWindow::app_setting() const{return this->setting;}
-//Implementations for main server.
-bool mainWindow::tcpserver_pending(const tcpSocket &socket){
+//Main server
+bool mainWindow::tcpserver_pending(threadedTcpSocket &socket){
#ifdef DEBUG
- qDebug()<<"Pending:"<<socket.peerAddress().toString()<<" Port:"<<socket.peerPort();
+ qDebug()<<"Pending:"<<socket.peerAddr().first.toString()<<" Port:"<<socket.peerAddr().second;
#endif
- if(this->memberList->isInMember(AddressAndPort(socket.peerAddress(),0),true)<0) return false;
+ if(this->memberList->isInMember(socket.peerAddr(),true)<0) return false;
connect(&socket,
SIGNAL(msg_received(const QString &)),
SLOT(tcpserver_msg_received(const QString &)));
}
void mainWindow::tcpserver_error(const QAbstractSocket::SocketError &error){
Q_UNUSED(error);
- tcpSocket *sender=qobject_cast<tcpSocket *>(this->sender());
+ threadedTcpSocket *sender=qobject_cast<threadedTcpSocket *>(this->sender());
#ifdef DEBUG
qDebug()<<"Receive aborted:"<<sender->errorString();
#endif
- sender->close();
+ sender->exit(1);
}
void mainWindow::tcpserver_msg_received(const QString &msg){
- tcpSocket *sender=qobject_cast<tcpSocket *>(this->sender());
- this->receiveText->append(sender->header_data().senderName()+"("+this->memberList->name(AddressAndPort(sender->peerAddress(),0),true)+", "+sender->peerAddress().toString()+")"+tr(" says:"));
+ threadedTcpSocket *sender=qobject_cast<threadedTcpSocket *>(this->sender());
+ this->receiveText->append(sender->head_data().senderName()+
+ "("+this->memberList->name(AddressAndPort(sender->peerAddr().first,0),true)+", "+sender->peerAddr().first.toString()+")"+tr(" says:"));
this->receiveText->append(msg);
this->receiveText->append("<br />");
+ sender->quit();
}
void invalidLink(const QString &);
void selectedLink(const QUrl &);
//These functions are for tcpclient.
- void tcpclient_connected();
void tcpclient_error(const QAbstractSocket::SocketError &error);
void sentData();
//These functions are for tcpserver.
- bool tcpserver_pending(const tcpSocket &);
+ bool tcpserver_pending(threadedTcpSocket &);
void tcpserver_msg_received(const QString &);
void tcpserver_error(const QAbstractSocket::SocketError &);
public slots: