Linux x86_64
のサーバーアプリケーションを現在<sys/socket.h>
を使用して書いています。 accept()
で接続を受け入れると、fdopen()
を使用して、取得したソケットをFILE*
ストリームにラップします。空でない読み取りバッファーでソケットストリームを処理するときに「不正なシーク」エラーが発生しました
通常、そのFILE*
ストリームに書き込んだり読み込んだりすると、かなりうまく動作しますが、空の読み取りバッファがないうちに書き込むとすぐに使用できなくなります。
デモンストレーションの目的で、接続をリッスンし、fgetc()
を使用して入力を1行ずつ読み取りバッファに読み込むコードを作成しました。行が長すぎてバッファに収まらない場合は、完全には読み込まれませんが、次の繰り返しで読み込まれます。
#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()
の両方がコメントアウトされている場合は、すべてはとなります。
残念ながら、私はこの問題に関する適切な文書やインターネットディスカッションを見つけることができませんでしたので、あなたの助けに感謝します。確実r+
モードで使用することができますFILE*
にソケットのfd変換のないクリーンな方法はないように思えるので、
編集
私は最終的にのために定住ソリューションは、ない使用fdopen()
とFILE*
にあります。 代わりに、自分自身の置換コードをfputs
とfprintf
と書いて、ソケットfdで直接作業しました。
誰でも必要ならば、here is the code。
したがって、 'FILE * streams'、特に** single **' FILE * stream'を使用するには、きれいな方法がありませんか?ソケットディスクリプタを 'FILE * stream'にラップする前に、' printf() 'と' puts() 'のための[私自身の](http://pastebin.com/LYZGn9c2)ラッパーを持っていましたが、醜い。結局のところ、最もクリーンなソリューションだとします。 –
はい。おそらく皮肉なことに、あなたの「基本的なasprintf」コードは、まさに私がやったように 'snprintf'の戻り値を定義したときの私の心にありました。 :-) – torek