简体   繁体   English

套接字编程 - 客户端和服务器 - 收到损坏的图像

[英]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++: 用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: 客户端用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. 我感谢你的帮助。

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: 在发送方,在python中,您首先发送文件的名称:

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: 首先是PNG标题 ,它是0x89后跟三个字母PNG ,后跟二进制数据:

在此输入图像描述

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. 在接收方,您收到所有数据,将其写入文件,然后您认为512首先是文件名。 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. 当您将其余数据写为png文件时,生成的文件不再具有有效的png标头。

By the way, you use an intermediary file (is this really needed ?). 顺便说一句,你使用中间文件(这真的需要吗?)。 You open it correctly in "wb" binary mode. 您可以在“wb”二进制模式下正确打开它。 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. 您必须考虑套接字的streamng特性。 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 用空格填充文件的名称,直到它填充512字节,或
  • 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. 但如果它是二进制的,你必须考虑网上机器的潜在endian问题。
  • or send a special separator after the filename, and on the receiver side, search for this special separator instead of reading a fixed length. 或者在文件名后发送一个特殊的分隔符,并在接收方,搜索这个特殊的分隔符,而不是读取固定的长度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM