たとえば、ソケットを介してクライアントからサーバーに 5 つの画像を送信する必要があり、一度に送信したいとします (1 つを送信して ACK を待つのではなく)。
質問:
それぞれの終わりを区切るためのベスト プラクティスやガイドラインがあるかどうかを知りたいです。
サーバーで区切り文字を検出し、各画像を一度処理するための最も安全な方法は何ですか? (可能であればC/C++で)
前もって感謝します!
画像はバイナリデータなので、画像に収まらない区切り文字を考え出すのは難しいでしょう。(そして最終的に受信側を混乱させる)
送信の最初、または各画像の最初に配置されるヘッダーを作成することをお勧めします。
例:
struct Header
{
uint32_t ImageLength;
// char ImageName[128];
} __attribute__(packed);
送信者は、各画像の前にこれを追加し、長さを正しく入力する必要があります。受信者は、画像がいつ終了するかを認識し、その位置で別のヘッダー構造を期待します。
属性(packed)は安全であり、異なる 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 でしょうか?
エスケープ シーケンスを使用して、データ内で現れる区切り文字のインスタンスをすべて排除します。
これは、両端ですべてのデータを反復することを意味しますが、データフローととにかく行われていることによっては、便利な解決策になる可能性があります:(