Socket 编程——客户端和服务器——收到损坏的图像

Posted

技术标签:

【中文标题】Socket 编程——客户端和服务器——收到损坏的图像【英文标题】:Socket Programming -- client and server -- received damaged image 【发布时间】:2016-01-26 20:15:26 【问题描述】:

我能够将图像从客户端发送到服务器,并且我在服务器上获得了图像。但是,当我尝试在服务器端打开图像时,即使它具有原始大小(没有丢失任何东西),它也会损坏。事实上,我在这个网站上询问了以前的错误并得到了帮助——请参见下面的链接: Socket Programming -- Sending and receiving images

现在我想解决为什么我收到图像后损坏的原因。 这是我最后的代码——

用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;

用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()

感谢您的帮助。

【问题讨论】:

尽管将传入的数据写入 .jpg 文件以便稍后将其丢弃的想法很糟糕,但提供的代码中没有任何内容可以保证提供的路径长度为 512 个字符。但是您正在从接收到的缓冲区中完全删除它 - 难怪您损坏了您的图像。 您是否尝试以std::ios::binary 的身份打开文件? 您使用readline 字节有什么原因吗?您可能还需要考虑发送文件大小。 Jason --- 没有理由使用 readLine 而是我应该使用什么。例如,pic.open(512)。 请缩进您的代码。对于 C++ 来说,这对潜在的读者来说只是一种麻烦,但对于 Python 来说却是致命的。也就是说,只需记录您写入的每个字节和您收到的每个字节,然后区分两者。您可以使用hdhexdumpdiff 等工具来有效地比较这些。 【参考方案1】:

问题

我可以很容易地在我的电脑上重现你的错误。

在发送方,在 python 中,您从发送文件名开始:

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

这个字符串的每个字节都被传输并且只有这些字节。但紧随其后的是文件的内容。首先是PNG header,即0x89,后跟三个字母PNG,然后是二进制数据:

在接收端,你接收到所有数据,将其写入一个文件,然后你认为第一个 512 是文件名。不幸的是,您需要的不仅仅是文件名!当您将其余数据写入 png 文件时,生成的文件不再具有有效的 png 标头。

顺便说一句,您使用了一个中间文件(这真的需要吗?)。您以“wb”二进制模式正确打开它。但是您的最终结果文件以默认文本模式作为流打开。在这里你应该走安全的路:

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

如何解决?

您必须考虑套接字的流式性质。当你发送数据时,没有任何东西会告诉接收者数据应该是多长时间(除了接收到的字节数,如果通信没有中断的话)。

所以三个解决方案:

用空格填充文件名,直到它恰好填满 512 个字节,或者 以文件名的长度开始发送。但如果它是二进制文件,则必须考虑网络上跨机器的潜在字节序问题。 或者在文件名之后发送一个特殊的分隔符,在接收方,搜索这个特殊的分隔符而不是读取一个固定的长度。

【讨论】:

以上是关于Socket 编程——客户端和服务器——收到损坏的图像的主要内容,如果未能解决你的问题,请参考以下文章

socket编程中的接收数据丢失问题

Java-UDP Socket编程

Java语言 网络编程之Socket用法

C语言socket高并发网络编程

socket编程中,服务器如何检测到客户端网络连接的断开.比如说客户端的网线断掉了,从服务端如何能检测到呢l

socket编程,简单多线程服务端测试程序