假设我需要通过套接字将五个图像从客户端发送到服务器,并且我想立即执行此操作(不发送一个并等待ACK)。
问题:
我想知道是否有一些最佳做法或指南来划定每一个的结尾。
在服务器中检测分隔符并处理每个图像最安全的方法是什么? (如果可能,在 C / C ++ 中)
提前致谢!
答案 0 :(得分:1)
由于图像是二进制数据,因此很难找到图像中不能包含的分隔符。 (并最终混淆接收方)
我建议你创建一个标题,放在传输开始时或每个图像的开头。
一个例子:
struct Header
{
uint32_t ImageLength;
// char ImageName[128];
} __attribute__(packed);
发件人应在每张图片前添加此内容并正确填写长度。接收器然后将知道图像何时结束并且期望该位置处的另一个Header结构。
属性(打包)是一种安全措施,即使您使用不同的GCC版本编译服务器和客户端,也可确保标头具有相同的对齐方式。在结构由不同过程解释的情况下建议使用。
数据流: 头 图像数据 头 图像数据 头 图像数据 ...
答案 1 :(得分:1)
您可以使用这些函数将文件(从java中的客户端)发送到服务器(在C中)。想法是发送4个字节,表示文件的大小,后跟文件内容,当所有文件都已发送时,发送4个字节(全部设置为0)表示传输结束。
// Compile with Microsoft Visual Studio 2008
// path, if not empty, must be ended with a path separator '/'
// for example: "C:/MyImages/"
int receiveFiles(SOCKET sck, const char *pathDir)
{
int fd;
long fSize=0;
char buffer[8 * 1024];
char filename[MAX_PATH];
int count=0;
// keep on receiving until we get the appropiate signal
// or the socket has an error
while (true)
{
if (recv(sck, buffer, 4, 0) != 4)
{
// socket is closed or has an error
// return what we've received so far
return count;
}
fSize = (int) ((buffer[0] & 0xff) << 24) |
(int) ((buffer[1] & 0xff) << 16) |
(int) ((buffer[2] & 0xff) << 8) |
(int) (buffer[3] & 0xff);
if (fSize == 0)
{
// received final signal
return count;
}
sprintf(filename, "%sIMAGE_%d.img", pathDir, count+1);
fd = _creat(filename, _S_IREAD | _S_IWRITE);
int iReads;
int iRet;
int iLeft=fSize;
while (iLeft > 0)
{
if (iLeft > sizeof(buffer)) iReads = sizeof(buffer);
else iReads=iLeft;
if ((iRet=recv(sck, buffer, iReads, 0)) <= 0)
{
_close(fd);
// you may delete the file or leave it to inspect
// _unlink(filename);
return count; // socket is closed or has an error
}
iLeft-=iRet;
_write(fd, buffer, iRet);
}
count++;
_close(fd);
}
}
客户端部分
/**
* Send a file to a connected socket.
* <p>
* First it send the file size if 4 bytes then the file's content.
* </p>
* <p>
* Note: File size is limited to a 32bit signed integer, 2GB
* </p>
*
* @param os
* OutputStream of the connected socket
* @param fileName
* The complete file's path of the image to send
* @throws Exception
* @see {@link receiveFile} for an example on how to receive the file from the other side.
*
*/
public void sendFile(OutputStream os, String fileName) throws Exception
{
// File to send
File myFile = new File(fileName);
int fSize = (int) myFile.length();
if (fSize == 0) return; // No empty files
if (fSize < myFile.length())
{
System.out.println("File is too big'");
throw new IOException("File is too big.");
}
// Send the file's size
byte[] bSize = new byte[4];
bSize[0] = (byte) ((fSize & 0xff000000) >> 24);
bSize[1] = (byte) ((fSize & 0x00ff0000) >> 16);
bSize[2] = (byte) ((fSize & 0x0000ff00) >> 8);
bSize[3] = (byte) (fSize & 0x000000ff);
// 4 bytes containing the file size
os.write(bSize, 0, 4);
// In case of memory limitations set this to false
boolean noMemoryLimitation = true;
FileInputStream fis = new FileInputStream(myFile);
BufferedInputStream bis = new BufferedInputStream(fis);
try
{
if (noMemoryLimitation)
{
// Use to send the whole file in one chunk
byte[] outBuffer = new byte[fSize];
int bRead = bis.read(outBuffer, 0, outBuffer.length);
os.write(outBuffer, 0, bRead);
}
else
{
// Use to send in a small buffer, several chunks
int bRead = 0;
byte[] outBuffer = new byte[8 * 1024];
while ((bRead = bis.read(outBuffer, 0, outBuffer.length)) > 0)
{
os.write(outBuffer, 0, bRead);
}
}
os.flush();
}
finally
{
bis.close();
}
}
从客户端发送文件:
try
{
// The file name must be a fully qualified path
sendFile(mySocket.getOutputStream(), "C:/MyImages/orange.png");
sendFile(mySocket.getOutputStream(), "C:/MyImages/lemmon.png");
sendFile(mySocket.getOutputStream(), "C:/MyImages/apple.png");
sendFile(mySocket.getOutputStream(), "C:/MyImages/papaya.png");
// send the end of the transmition
byte[] buff = new byte[4];
buff[0]=0x00;
buff[1]=0x00;
buff[2]=0x00;
buff[3]=0x00;
mySocket.getOutputStream().write(buff, 0, 4);
}
catch (Exception e)
{
e.printStackTrace();
}
答案 2 :(得分:0)
如果您无法轻松发送包含长度的标头,请使用一些可能的分隔符。如果图像没有压缩并且由位图 - stype数据组成,可能是0xFF / 0XFFFF / 0xFFFFFFF,因为完全饱和的亮度值通常很少?
使用转义序列来消除在数据中出现的分隔符的任何实例。
这意味着迭代两端的所有数据,但取决于您的数据流,以及正在进行的操作,它可能是一个有用的解决方案:(