C ++のHTTPサーバーがすべてのファイルを正しく送信していない
質問
私はc ++でHTTPサーバーに取り組んでおり、現在はテキストファイルのリクエストに対して機能していますが、jpegなどを取得しようとすると、ファイルの一部のみが送信されます。問題は、fgets(buffer、2000、requested_file)を使用すると、実際にバッファーに入れるよりもはるかに多くのファイル位置インジケーターをインクリメントするように見えることです。なぜこれが起こるのですか?以下にすべてのコードを配置します。この問題は、応答コードが200の場合に発生するwhile(true)ループで発生します。ご返信いただきありがとうございます。
// Interpret the command line arguments
unsigned short port = 8080;
if ( (argc != 1) && (argc != 3) && (argc != 5) ) {
cerr << "Usage: " << argv[0];
cerr << " -p <port number> -d <base directory>" << endl;
return 1;
}
else {
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "-p") == 0)
port = (unsigned short) atoi(argv[++i]);
else if (strcmp(argv[i], "-d") == 0)
base_directory = argv[++i];
}
}
// if base_directory was not given, set it to current working directory
if ( !base_directory ) {
base_directory = getcwd(base_directory, 100);
}
// Create TCP socket
int tcp_sock = socket(AF_INET, SOCK_STREAM, 0);
if (tcp_sock < 0) {
cerr << "Unable to create TCP socket." << endl;
return 2;
}
// Create server socket
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons( port );
server.sin_addr.s_addr = INADDR_ANY;
// Bind the socket
if (bind(tcp_sock, (sockaddr*)&server, sizeof(server)) < 0) {
cerr << "Unable to bind TCP socket." << endl;
return 3;
}
// Listen for a connection request on TCP port
listen(tcp_sock, 5);
// Create HTTP_Request object and start a while loop of accepting connections
char buffer[2000];
int bytes_recv = 0;
int recv_len = 0;
string error_reply;
HTTP_Response* response;
while (true) {
int acc_tcp_sock = accept(tcp_sock, NULL, NULL);
if (acc_tcp_sock == -1) {
cerr << "Unable to open TCP connection with client." << endl;
}
do {
// may want to do just one recv
recv_len = recv( acc_tcp_sock, buffer + bytes_recv,
2000 - bytes_recv, 0 );
bytes_recv += recv_len;
} while (false);
bytes_recv = 0;
// may want to see if this causes a memory leak
HTTP_Request* request = HTTP_Request::Parse(buffer, 2000);
response = handle_request(request); // function to handle the request
// Put response header into buffer
response->Print( buffer, 2000 );
// if 200 and GET then send header with file
if ( response->Get_code() == 200 ) {
// send response header
if ( send( acc_tcp_sock, buffer, strlen(buffer), 0 ) < 0 ) {
cerr << "Unable to send response header to client." << endl;
}
if ( method == "GET" ) {
// send file
while ( true ) {
fgets( buffer, 2000, returned_file );
if ( feof( returned_file ) ) break;
if ( send( acc_tcp_sock, buffer, strlen(buffer), 0 ) < 0 ) {
cerr << "Unable to send file in response to client." << endl;
}
}
}
fclose( returned_file ); // close file
}
else {
if ( method == "GET" ) {
error_reply = buffer + error_page;
if ( send( acc_tcp_sock, error_reply.c_str(), error_reply.length(), 0 ) < 0 ) {
cerr << "Unable to send response to client." << endl;
}
}
else {
if ( send( acc_tcp_sock, buffer, strlen(buffer), 0 ) < 0 ) {
cerr << "Unable to send respone header to client." << endl;
}
}
}
close( acc_tcp_sock ); // close the connection
}
return 0;
解決
ビット単位で生き残るために必要なバイナリデータを読み取るために fgets()
を使用しないでください。レコード区切り文字の翻訳は必要ありません。一部のシステムでは、そのように読むとテキストであると見なされる場合があります。さらに言えば、改行とレコード区切り文字はまったく意味がないので、それらをスキャンするfgets() `機能はせいぜい混乱させる非効率であり、最悪の場合はまったくバイナリ対応ではありません。
fread(3)
を使用するか、またはそれ以上で、rawシステムコール(とにかく非UnixではPosix API)read(2)を使用します。これにより、一定量のビットごとのデータが読み取られ、読み取り量がわかります。 (使用する which バイナリ対応APIについて:通常、データをバッファリングすることをお勧めします。これは通常、行のような小さな単位でデータを処理するためです。この場合、単に read()
を使用する方が簡単かつ高速です。)
バイナリデータを strlen()
することもできません。 API呼び出しのバイトカウントを使用する必要があります。
他のヒント
バイナリファイルでstrlen breakを使用しませんか?
send( acc_tcp_sock, buffer, strlen(buffer), 0 )
バイナリファイルにNULLバイト( '\ 0'
)が含まれている可能性が非常に高いです。 fgets
でデータを読み取ると、バッファに配置される場合がありますが、 \ 0
が失われた後にすべてを送信すると( strlen
呼び出しこれを保証します)。
したがって、データを読み取るには fread
を使用する必要があります。実際に読み取られたバイト数が返されるため、strlenをまったく使用する必要はありません。また、バイナリモードでファイルを開くことを忘れないでください!
より良いのは、ファイルをメモリにマッピングすることについて読んでください。そうすれば、ファイルの内容を読み込むためのバッファを管理する必要がなくなり、そのバッファを send
に渡すことができます。 。
ファイルからバイトを本当に読みたい場合は、バイナリファイルの読み取り(正しいMIMEタイプまたは application / octet-stream
を使用して、読み取りバイト数をバッファに保存する)とファイルを開くことを区別する必要がありますテキスト( text / *
MIMEタイプ。strlenを使用できます)。