Send integer from TCP socket in C to Qt TCP socket - c++

I'm currently trying to implement basic client-server file transfer program using TCP sockets. Client is being written in C++/Qt and the server in C.
I encountered great difficulty when trying to send the file size from server to client (integer value). Below are the code samples.
C server:
if(fileExists == '1')
{
fp = fopen(filename, "r");
fseek(fp, 0L, SEEK_END);
fileSize = ftell(fp);
printf("%d\n", fileSize);
send(client_socket, &fileSize, sizeof(fileSize), 0);
}
Qt Client:
void Client::receiveFile(QString filename)
{
qint32 fileSize;
clientSocket->waitForReadyRead(1000);
clientSocket->read(fileSize);
qDebug() << fileSize;
}
The problem is that C calculates the file size properly as 40435408 and sends it over to the client which says the size is 32767. It's obvious that the problem lies on the client side. I tried to figure out the problem for almost whole day and failed.
I realize that this is some simple and stupid mistake I made and I apologize for asking such dumb question. I'm a complete begginner. Can anyone help?

When you call clientSocket->read(fileSize);, you are in fact calling QByteArray QIODevice::read(qint64 maxSize). Which means you are reading up to an undefined (as fileSize is not initialized) amount of bytes from the TCP socket and discard it immediatly, as you don't use the return value.
I think you are trying to use qint64 QIODevice::read(char *data, qint64 maxSize), so your code should look like this:
qint32 fileSize = 0;
clientSocket->waitForReadyRead(1000);
if (clientSocket->bytesAvailable() >= sizeof(fileSize)) {
clientSocket->read(&fileSize, sizeof(fileSize));
} else {
qWarning() < "Only received" << clientSocket->bytesAvailable() << "bytes: " << clientSocket->readAll().toHex(' ');
}
qDebug() << fileSize;
Note that I would not use this code in any software that is more than a proof of concept.

Related

Does TCPdump strip any headers when receiving packets?

So i am attempting to send an already constructed packet over a RAW socket interface (these are packets that have been previously captured and i want to resend them without changing the packet integrity) and am using TCPdump to check that the packets are going over correctly (surprise they are not).
The packets are physically being sent but are always 24 bytes short of what my "sent" returns.
In wireshark my eth headers seem to be erased as my source and dest MAC addresses are "00:00:00:00:00
sock setup is as follows
sock = socket(AF_PACKET,SOCK_RAW,IPPROTO_RAW);
if(sock==-1)
{
qDebug() << "sock error";
}
int reuse = 1;
if(setsockopt(sock, IPPROTO_RAW, IP_HDRINCL, (char *)&reuse, sizeof(reuse)) < 0)
{
qDebug() << "error setting reuse"
}
else
{
"setting reuse"
}
struct sockaddr_ll sll;
struct ifreq ifr;
bzero(&sll, sizeof(sll));
bzero(&ifr, sizeof(ifr));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons(IPPROTO_RAW);
sll.sll_halen = ETH_ALEN;
strncpy((char*)ifr.ifr_ifrn.ifrn_name,interface.toUtf8.constData(),IFNAMSIZ);
if(ioctl(sock,SIOCGIFINDEX,&ifr) == -1)
{
qDebug() << "error getting interface name";
}
strncpy((char*)ifr.ifr_ifrn.ifrn_name,interface.toUtf8.constData(),IFNAMSIZ);
if(ioctl(sock,SIOCGIFHWADDR,&ifr) == -1)
{
qDebug() << "error getting interface name";
}
if(bind(sock,(struct sockaddr *)&sll,sizeof(sll))==-1)
{
qDebug() << "error binding sock";
}
after this im using
int size = write(sock,(const void*)&packet,hdr.caplen);
i've tried sendto in the past but it would always reconfigure things so this was my next solution which also isnt working as i would like.
I'm not the most savy with TCP/IP stuff so any help would be greatly appreciated!
okay so after just trying a bunch of different stuff i landed on what seems to be my solution.
i created a second pointer that will point to the top of the packet and send that instead.
(char *)sendingPacket;
struct ethhdr *ethh = (struct ethhdr*)packet;
sendingPacket = (char*) ethh;
i don't really understand why this works but sending the other packet doesn't so if anyone has insight please share!

my C++ code cannot handle (a little bit) fast data traffic

Background:
I have the below C++ code that listens UDP packets on port26009 then outputs the received packets
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
udpSocket.bind(26009); //the port that GUI listens (26009)
connect(&udpSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
}
void MainWindow::readyRead(){
QByteArray datagram;
do {
datagram.resize(udpSocket.pendingDatagramSize());
udpSocket.readDatagram(datagram.data(), datagram.size());
} while (udpSocket.hasPendingDatagrams());
qDebug() << "Message: " << datagram;
}
On the client side, here is a C code to unicast 20 udp packets: hi0, hi1, ... hi19:
int main(void){
int i;
char msg[5] = {'\0'};
for(i=0; i<20; i++){
snprintf(msg, 4, "hi%d", i);
send_unicast("192.168.5.1", msg, 26009);
memset(msg, 0, sizeof msg);
}
return 0;
}
void send_unicast(char IPaddr[16], char* message, uint16_t destPort){
struct sockaddr_in si_other;
int s, slen = sizeof(si_other);
if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1){
fprintf(stderr, "socket() failed - line817\n");
exit(1);
}
memset((char *) &si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;
si_other.sin_port = htons(destPort);
if (inet_aton(IPaddr, &si_other.sin_addr) == 0) {
fprintf(stderr, "inet_aton() failed - line 825\n");
exit(1);
}
if (sendto(s, message, 512, 0, (struct sockaddr *) &si_other, slen) == -1){
fprintf(stderr, "sendto() failed - line830\n");
exit(1);
}
close(s);
}
I run C++ (Server) and C (client) codes on different Linux devices.
Problem:
C code works fine and generates the packets. I can see on Wireshark that the packets are received on receiving end (C++ ).
However, the above C++ code cannot receive (or process) the packets correctly, it is somehow too fast for the code to handle and I see only couple output, e.g.
hi5
hi14
If the frequency of the transmission is significantly slowed down on the C code (adding sleep(1)) then the C++ code outputs the packets fine.
Question:
I use the received UDP packets to update some information on the GUI (Qt). I guess that the slot causes the slowness, and the code would handle the traffic if I used pthread to listen the packets but then how the thread needs to notify the main class so that the GUI can update accordingly?
All in all, how do you recommend to modify C++ code, so that it can handle the fast data traffic and notify GUI to update?
In your readyRead function of MainWindow, I expect this is going to be a problem: -
QByteArray datagram;
do {
qint64 dSize = udpSocket.pendingDatagramSize();
datagram.resize(dSize);
udpSocket.readDatagram(datagram.data(), dSize);
} while (udpSocket.hasPendingDatagrams());
If the first packet of data is, let's say 8 bytes long, you set the QByteArray to 8 bytes and read the data.
While reading 8 bytes, if 6 bytes are received, the second time around that loop you're going to to call resize on the byte array, but this time it's going to reduce it from 8 bytes to 6 and read 6 bytes, throwing away the data you previously read in.
Rather than resizing the QByteArray, I suggest you append the data to it: -
QByteArray datagram;
do
{ datagram.append(udpSocket.pendingDatagramSize());
udpSocket.readDatagram(datagram.data(), datagram.size());
}while (udpSocket.hasPendingDatagrams());

FTP server file transfer

I am uncertain about a few things regarding ftp file transfer. I am writing an ftp server and I am trying to figure out how to make the file tranfer work correctly. So far it works somehow but I have certain doubts. Here is my file transfer function (only retrieve so far):
void RETRCommand(int & clie_sock, int & c_data_sock, char buffer[]){
ifstream file; //clie_sock is used for commands and c_data_sock for data transfer
char *file_name, packet[PACKET_SIZE]; //packet size is 2040
int packet_len, pre_pos = 0, file_end;
file_name = new char[strlen(buffer + 5)];
strcpy(file_name, buffer + 5);
sprintf(buffer, "150 Opening BINARY mode data connection for file transfer\r\n");
if (send(clie_sock, buffer, strlen(buffer), 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent: " << buffer << endl;
file_name[strlen(file_name) - 2] = '\0';
file.open(file_name, ios::in | ios::binary);
if (file.is_open()) {
file.seekg(0, file.end);
file_end = (int) file.tellg();
file.seekg(0, file.beg);
while(file.good()){
pre_pos = file.tellg();
file.read(packet, PACKET_SIZE);
if ((int) file.tellg() == -1)
packet_len = file_end - pre_pos;
else
packet_len = PACKET_SIZE;
if (send(c_data_sock, packet, packet_len, 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent some data" << endl;
}
}
else {
sprintf(buffer, "550 Requested action not taken. File unavailable\r\n", packet);
if (send(clie_sock, buffer, packet_len + 2, 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent: " << buffer << endl;
delete(file_name);
return;
}
sprintf(buffer, "226 Transfer complete\r\n");
if (send(clie_sock, buffer, strlen(buffer), 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent: " << buffer << endl;
close(c_data_sock);
delete(file_name);
}
So one problem is the data transfer itself. I am not exactly sure how it is supposed to work. Now it works like this: the server sends all the data to c_data_sock, closes this socket and then the client starts doing something. Shouldn't the client recieve the data while the server is sending them? And the other problem is the abor command. How am I supposed to recieve the abor command? I tried recv with flag set to MSG_OOB but then I get an error saying "Invalid argument". I would be glad if someone could give me a hint or an example of how to do it right as I don't seem to be able to figure it out myself.
Thanks,
John
Ftp use two connections. First - is command connection, in your case it is clie_sock. 'ABOR' command should be received though it. You going to receive it the same way you received 'RETR' command.
To receive file client establishes data connection with your server ( c_data_sock socket ). It will not be opened till client connects, so this is the answer to your second question. You cannot start client after server executes this function. First client sends 'retr' command to your command socket. Then your sever waits new connection from client ( after sending him data ip and port ). Then client connects ( now you have your c_data_sock ready ) and sends all the data to that socket, which are in turn received by the client.
You probably need to read more about networking in general if you feel you don't understand it. I prefer this one: http://beej.us/guide/bgnet/
Also you have a memory leak here, after you allocate an array with the
file_name = new char[strlen(buffer + 5)];
you need to delete it using
delete [] file_name;
Otherwise file_name will be treated as a simple pointer, not an array, so most of array memory will be kept by your application which is bad especially when creating server.

how to send classes defined in .proto (protocol-buffers) over a socket

I am trying to send a proto over a socket, but i am getting segmentation error. Could someone please help and tell me what is wrong with this example?
file.proto
message data{
required string x1 = 1;
required uint32 x2 = 2;
required float x3 = 3;
}
xxx.cpp
...
data data_snd, data_rec;
//sending data to the server
if (send(socket, &data_snd, sizeof(data_snd), 0) < 0) {
cerr << "send() failed" ;
exit(1);
}
//receiving data from the client
if (recv(socket, &data_rec, sizeof(data_rec), 0) < 0) {
cerr << "recv() failed";
exit(1);
}
Thanks for help and replies-
You're not supposed to write the protobuf object itself to the socket. Use the SerializeXXX family of methods to get a sequence of bytes which you can write to the socket.
std::string buf;
data.SerializeToString(&buf);
// now you can write buf.data() to the socket
For one thing, you're assuming that a single call to recv will retrieve all the data. More importantly though, you're not going through the serialization / deserialization code - you're just reading from the network and putting the bytes directly into the object. You should be using the stream-based APIs in Protocol Buffers to write and read the data.
See the Protocol Buffers C++ tutorial for more details - that gives an example of saving to disk, but I expect that the network version would be similar, just with a different stream.
Give more details about where it segfaults and how you manage the connection between server and client.
Use a debugger and print the backtrace, it will help ...

Why would a blocking socket repeatedly return 0-length data?

I'm having a significant problem using a standard BSD-style socket in a C++ program. In the code below, I connect to a local web server, send a request, and simply create a loop waiting for data to return. I actually do receive the data, but then I get an endless stream of 0-length data as if it was a non-blocking socket. The web server presumably didn't kill the connection, because if so I would have received a length of -1.
Please ignore simple typos I make below, as I'm writing the code from memory, not a direct copy/paste. The code produces the same result on OSX and Windows.
int sock = socket(AF_INET, SOCK_STREAM, 0);
//assume serv_addr has been created correctly
connect(sock, (sockaddr*)&serv_addr, sizeof(serv_addr)) < 0);
std::string header = "GET / HTTP/1.1\r\n"
"Host: 127.0.0.1:80\r\n"
"Keep-Alive: 300\r\n"
"Connection: keep-alive\r\n\r\n";
send(sock, header.c_str(), header.length()+1, 0);
for (;;) {
char buffer[1024];
int len = recv(sock, buffer, 1024, 0);
cout << len << endl;
//this outputs two numbers around 200 and 500,
//which are the header and html, and then it
//outputs and endless stream of 0's
}
From the man page of recv
For TCP sockets, the return value 0 means the peer has closed its half
side of the connection.