私は現在、Linux x86_64
を使用してサーバーアプリケーションを作成しています<sys/socket.h>
。を介して接続を受け入れた後、取得したソケットをストリームにラップするためaccept()
に使用します。fdopen()
FILE*
そのストリームへの書き込みとそのストリームからの読み取りはFILE*
通常非常にうまく機能しますが、空でない読み取りバッファーがあるときにソケットに書き込むとすぐにソケットは使用できなくなります。
デモンストレーションの目的で、接続をリッスンし、を使用して入力を1行ずつ読み取りバッファーに読み取るコードを記述しましたfgetc()
。行が長すぎてバッファに収まらない場合は、完全には読み取られませんが、代わりに次の反復中に読み取られます。
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
FILE* listen_on_port(unsigned short port) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in name;
name.sin_family = AF_INET;
name.sin_port = htons(port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sock, (struct sockaddr*) &name, sizeof(name)) < 0)
perror("bind failed");
listen(sock, 5);
int newsock = accept(sock, 0, 0);
return fdopen(newsock, "r+");
}
int main(int argc, char** argv) {
int bufsize = 8;
char buf[9];
buf[8] = 0; //ensure null termination
int data;
int size;
//listen on the port specified in argv[1]
FILE* sock = listen_on_port(atoi(argv[1]));
puts("New connection incoming");
while(1) {
//read a single line
for(size = 0; size < bufsize; size++) {
data = fgetc(sock);
if(data == EOF)
break;
if(data == '\n') {
buf[size] = 0;
break;
}
buf[size] = (char) data;
}
//check if the read failed due to an EOF
if(data == EOF) {
perror("EOF: Connection reset by peer");
break;
} else {
printf("Input line: '%s'\n", buf);
}
//try to write ack
if(fputs("ack\n", sock) == EOF)
perror("sending 'ack' failed");
//try to flush
if(fflush(sock) == EOF)
perror("fflush failed");
}
puts("Connection closed");
}
コードは、特別なパラメーターなしでgccでコンパイルする必要があります。ポート番号を引数として実行し、netcatを使用してローカルに接続します。
これで、8文字より短い文字列を送信しようとすると、これは問題なく実行されます。ただし、10文字を超える文字列を送信すると、プログラムは失敗します。このサンプル入力:
ab
cd
abcdefghij
この出力を作成します:
New connection incoming
Input line: 'ab'
Input line: 'cd'
Input line: 'abcdefgh'
fflush failed: Illegal seek
EOF: Connection reset by peer: Illegal seek
Connection closed
ご覧のとおり、(正しく)abcdefghの最初の8文字のみが読み取られますが、プログラムが「ack」文字列(クライアントが受信することはありません)を送信しようとすると、Illegal seek
エラーが発生し、次の呼び出しfgetc()
はEOFを返します。
fflush()
パーツがコメントアウトされている場合でも、同じエラーが発生しますが、
fflush failed: Illegal seek
サーバー出力に行がありません。
fputs(ack)
パーツがコメントアウトされている場合、すべてが意図したとおりに機能しているように見えますが、gdbから手動で呼び出されたperror()は、「不正なシーク」エラーを報告します。
fputs(ack)
との両方fflush()
がコメントアウトされている場合、すべてが意図したとおりに機能します。
残念ながら、この問題に関する優れたドキュメントやインターネットでの議論は見つかりませんでした。ご協力いただければ幸いです。
編集
私が最終的に解決した解決策は、とを使用しないことです。これは、ソケットfdをモードで確実に使用できるに変換するクリーンな方法がないように思われるためです。代わりに、私はソケットfdに直接取り組み、との独自の置換コードを記述しました。fdopen()
FILE*
FILE*
r+
fputs
fprintf
誰かがそれを必要とするならば、ここにコードがあります。