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 来说却是致命的。也就是说,只需记录您写入的每个字节和您收到的每个字节,然后区分两者。您可以使用hd
或hexdump
和diff
等工具来有效地比较这些。
【参考方案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 编程——客户端和服务器——收到损坏的图像的主要内容,如果未能解决你的问题,请参考以下文章