如何在通过套接字发送之前正确分隔多个图像

时间:2013-09-03 08:24:07

标签: c++ sockets tcp

假设我需要通过套接字将五个图像从客户端发送到服务器,并且我想立即执行此操作(不发送一个并等待ACK)。

问题:

  1. 我想知道是否有一些最佳做法或指南来划定每一个的结尾。

  2. 在服务器中检测分隔符并处理每个图像最安全的方法是什么? (如果可能,在 C / C ++ 中)

  3. 提前致谢!

3 个答案:

答案 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,因为完全饱和的亮度值通常很少?

使用转义序列来消除在数据中出现的分隔符的任何实例。

这意味着迭代两端的所有数据,但取决于您的数据流,以及正在进行的操作,它可能是一个有用的解决方案:(