例如,假设我需要通过套接字将五个图像从客户端发送到服务器,并且我想立即执行此操作(而不是发送一个并等待 ACK)。
问题:
我想知道是否有一些最佳实践或指导方针来界定每个人的结尾。
在服务器中检测分隔符和处理每个图像的最安全方法是什么?(如果可能,使用C/C++)
提前致谢!
由于图像是二进制数据,因此很难想出不能包含在图像中的分隔符。(最终使接收方感到困惑)
我建议您创建一个标题,该标题将放置在传输的开头或每个图像的开头。
一个例子:
struct Header
{
uint32_t ImageLength;
// char ImageName[128];
} __attribute__(packed);
发件人应在每张图片之前添加此内容并正确填写长度。然后,接收者将知道图像何时结束,并期望在该位置有另一个 Header 结构。
属性(打包)是一种安全性,即使您使用不同的 GCC 版本编译服务器和客户端,也可以确保标头具有相同的对齐方式。建议在结构由不同进程解释的情况下使用。
数据流:标题图像数据标题图像数据标题图像数据...
您可以使用这些函数将文件(从 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();
}
如果您无法轻松发送包含长度的标头,请使用一些可能的分隔符。如果图像未压缩并且由位图类型数据组成,那么可能 0xFF/0XFFFF/0xFFFFFFF 因为完全饱和的亮度值通常很少见?
使用转义序列消除出现在数据中的任何分隔符实例。
这确实意味着在两端迭代所有数据,但取决于您的数据流以及正在执行的操作,这可能是一个有用的解决方案:(