Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Socket Programming -- client and server -- received damaged image

I am able to send an image from client to server and I got the image on server. However, when I am trying to open the image on server side it's damaged even though it has the original size ( not lost anything ). In fact I asked on this website about previous errors and I got help--- see the link below: Socket Programming -- Sending and receiving images

Now I want to fix why image after I received it is damaged. This is my last code --

Server side written in c++:

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> 
#include <netdb.h>
#include <unistd.h>
#include <string>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <fstream>

using namespace std;
using namespace cv;

#define SERVER_PORT htons(50007)

int main() {
  /* First call to socket() function */
    int serverSock=socket(AF_INET, SOCK_STREAM, 0); 

  if (serverSock < 0) 
  {
    perror("ERROR opening socket");
    exit(1);
  }

  sockaddr_in serverAddr;
  serverAddr.sin_family = AF_INET;
  serverAddr.sin_port = SERVER_PORT;
  serverAddr.sin_addr.s_addr = INADDR_ANY;

  /* bind (this socket, local address, address length)
     bind server socket (serverSock) to server address (serverAddr).  
     Necessary so that server can use a specific port */ 

  /* Now bind the host address using bind() call.*/
  if (bind(serverSock, (struct sockaddr *)&serverAddr, sizeof(sockaddr)) < 0) 
  {
    perror("ERROR on binding");
    exit(1);
  }



  /* Now start listening for the clients, here process will
   * go in sleep mode and will wait for the incoming connection
  */
  // wait for a client
  /* listen (this socket, request queue length) */
  listen(serverSock,5);

  sockaddr_in clientAddr;
  socklen_t sin_size=sizeof(struct sockaddr_in);

  int clientSock=accept(serverSock,(struct sockaddr*)&clientAddr, &sin_size);

  if (clientSock < 0) 
  {
    perror("ERROR on accept");
    exit(1);
  }


  //char *buff = (char*)malloc(sizeof(char) * (240*360));    
  FILE* output = fopen("test.jpeg", "wb");

  //this is the total number of bytes ever read from the socket
  unsigned int readBytes = 0;

  while(true)
  {
    std::size_t buffer_size = 1024;
    char buffer[1024];

    int ret = recv(clientSock, buffer, buffer_size, 0);
    if (ret == 0)
        break;
    if (ret < 0)    
    {    
        perror("ERROR reading from socket");
        exit(1);    
    }

    fwrite(buffer, sizeof(char), ret, output);    

    readBytes += ret;    
  }


  fclose( output );

  std::ifstream infile("test.jpeg", std::ios::binary);

  char filepath[512];
  infile.read(filepath, 512);

  if( infile.gcount() != 512 )
    perror("Data did not send filepath");

  std::string filepathstr(filepath);

  std::cout << filepathstr << std::endl;

  std::ofstream ofile(filepathstr);

  //loop until there is nothing left to read or error on the filestream
  while (infile && ofile)
  {
    std::size_t buffer_size = 1024;
    char buffer[1024];

    infile.read(buffer, buffer_size);
    int ret = infile.gcount();

    ofile.write(buffer,ret);
  }
  ofile.close();

  close(serverSock);
  return 0;
}

Client side written in python:

import socket
import sys,os
import cv2
import numpy as np
import time

HOST, PORT = "localhost", 50007

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
#   Create Socket 

client_socket.connect((HOST, PORT)) # Connection 

fpath ='/Users/salemalqahtani/Desktop/NetworkingPart/client/ss.png'

size = os.path.getsize(fpath)
print size

client_socket.send(str.encode("STORE " + fpath))

t = time.time()

pic = open(fpath,'rb')
while True:
    strng = pic.readline(512)
    if not strng:
        break
    client_socket.send(strng)
pic.close()

print("Done sending")
print("Elapsed time = " + str(time.time() - t) + 's')
client_socket.close()

Please I appreciate your help.

like image 649
Salem Alqahtani Avatar asked Mar 31 '26 16:03

Salem Alqahtani


1 Answers

The problem

I could reproduce your error very easily on my PC.

On the sender side, in python, you start with sending the name of the file:

client_socket.send(str.encode("STORE " + fpath))

Every bytes of this string are transfered and only these bytes. But this is immediately followed by the content of the file. First comes the PNG header which is 0x89 followed by the three letters PNG, followed by binary data:

enter image description here

On the receiver side, you receive all the data, write it to a file, and then you consider that the 512 first are the file name. Unfortunately, you take much more than only the file name ! As you write the rest of the data as being the png file, your resulting file does no longer have a valid png header.

By the way, you use an intermediary file (is this really needed ?). You open it correctly in "wb" binary mode. But your end result file is opened as a stream in default text mode. Here you should go the safe way:

std::ofstream ofile(filepathstr, std::ios::binary);

How to solve this ?

You have to take into account the streamng nature of a socket. When you send data, nothing will tell the receiver how long the data is supposed to be (except the count of bytes received, if the comunication is not interrupted).

So three solutions:

  • pad the name of the file with spaces until it fills exactly 512 bytes, or
  • start the sending with the length of the filename. But if it's binary, you have to consider the potential endian issue accross machines on the net.
  • or send a special separator after the filename, and on the receiver side, search for this special separator instead of reading a fixed length.
like image 147
Christophe Avatar answered Apr 03 '26 05:04

Christophe