うーん、これは昨年やった覚えがあります。私は基本的に、独自の telnet クローンを使用してインターネットと通信するプログラムでした。問題は、telnet が stdin と stdout を使用して動作することです。
あなたも同じ状況のようです!これを解決するために私がしたことは、プロセスをフォークし、stdin と stdout を取得してパイプに入れ、フォークされたプロセス イメージを telnet の呼び出しで上書きすることでした (現在は、stdin と stdout の代わりにこれらのパイプを使用しています)。
テキストをbcに送信するには1つのパイプが必要で、bcから受信するには別のパイプが必要です。すべてに 1 つのパイプを使用すると、bc に送信したデータと混合データを読み取ってしまう可能性があります。
警告: 大量のコードが受信されています。スレッドを使用して同時に書き込みと読み取りを行い、select() を使用してパイプに読み取るものがあるかどうかを確認しているため、すべてを理解する必要はないと思います。非常に重要です!!! 通信が切断されると、プロセスは正常に終了しない SIGPIPE を受け取ります (動的メモリなどを使用している場合)。そして、fflush(outpipe) しなければなりません。そうしないと、bc はそれを受け取りません。(これは、正しく思い出せば、システムが「\ n」などを見つけたときにのみフラッシュするためです)。X の機能を読みたい場合に備えて、すべてのコードを記載します。しかし、必要なのは、コメント「LOCAL FUNCTIONS END HERE」の直後の小さなフォークだけです。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include "irc.h"
#include "coloring.h"
#include "rtsp.h"
#define BUFFERSIZE 255
int main (int argc, char *argv[]) {
/* XXX: When kid dies, program doesn't exit */
char *serverName, *port, *nick, *channel;
int ptelnetin[2];
int ptelnetout[2];
FILE *fpipes[2];
bool running = true;
pid_t kid;
pthread_t pthread_input, pthread_output;
/************************************************
LOCAL FUNCTIONS START HERE
***********************************************/
void *inputprocess(void *pipes) {
bool bracket;
int i;
fd_set rfds;
struct timeval tv;
tv.tv_sec = 0.2;
tv.tv_usec = 0;
char buffer[BUFFERSIZE];
FILE *out = ((FILE **) pipes)[1];
while (running){
FD_ZERO(&rfds);
FD_SET(fileno(stdin), &rfds);
switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
case -1:
fprintf(stderr, "Error reading data at select() Line %d, File %s\n", __LINE__, __FILE__);
running = false;
break;
case 0:
/* There's no data avaiable just yet.
Do nothing and keep checking */
break;
default:
/* This check needs to be done;
select isn't completely reliable */
if(!fgets(buffer, BUFFERSIZE, stdin)) {
running = false;
break;
}
/* Check message not to contain brackets*/
for (i = 0, bracket = false; running && !bracket && buffer[i] && i < BUFFERSIZE; i++) {
if (buffer[i] == '[' || buffer[i] == ']') {
PRINT_YELLOW;
printf("Use of brackets not allowed\n");
RESET_COLOR;
fflush(stdout);
bracket = true;
break;
}
}
if (running && !bracket) ircInputWrite(out, buffer);
fflush(out);
}
}
}
void *outputprocess(void *pipes){
fd_set rfds;
struct timeval tv;
tv.tv_sec = 0.2;
tv.tv_usec = 0;
char buffer[BUFFERSIZE];
char from[100];
FILE *in = ((FILE **) pipes)[0];
FILE *out = ((FILE **) pipes)[1];
while (running){
FD_ZERO(&rfds);
FD_SET(fileno(in), &rfds);
switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
case -1:
fprintf(stderr, "Error reading data at select() Line %d, File %s\n", __LINE__, __FILE__);
running = false;
break;
case 0:
/* There's no data avaiable just yet. */
/* Select sometimes returns 0 when there IS
data to read so we'll read anyway */
default:
/* This check needs to be done;
select isn't completely reliable */
if(!fgets(buffer, BUFFERSIZE, in)) {
running = false;
break;
}
switch(ircWhatsthis(buffer)) {
case iPING:
PRINT_BLUE;
printf("PingPong!\n");
RESET_COLOR;
ircPingPong(out, buffer); fflush(out);
fflush(stdout);
break;
case iROOMMSG:
if (ircUnpackPRIVMSG(from, buffer, buffer)) {
PRINT_BRIGHT_RED;
fprintf(stdout, "Malformed private message received\n");
RESET_COLOR;
}
else {
PRINT_CYAN;
printf("<%s>: ", from);
puts(buffer);
RESET_COLOR;
}
fflush(stdout);
break;
case iPRIVMSG:
fflush(stdout);
if (ircUnpackPRIVMSG(from, buffer, buffer)) {
PRINT_BRIGHT_RED;
fprintf(stdout, "Malformed private message received\n");
RESET_COLOR;
fflush(stdout);
}
else {
if (rtspExecBrackets(out, from, buffer)) {
PRINT_BRIGHT_MAGENTA;
printf("[%s]: ", from);
puts(buffer);
RESET_COLOR;
fflush(stdout);
}
}
break;
case iERROR:
PRINT_BRIGHT_RED;
fwrite(buffer, sizeof(char), strlen(buffer)+1, stdout);
RESET_COLOR;
break;
case iOK:
PRINT_BRIGHT_CYAN;
fwrite(buffer, sizeof(char), strlen(buffer)+1, stdout);
RESET_COLOR;
break;
default:
PRINT_BRIGHT_BLACK;
fwrite(buffer, sizeof(char), strlen(buffer)+1, stdout);
RESET_COLOR;
}
fflush(stdout);
}
}
}
void terminate(int signum) {
/* XXX irc.c calls rtsp.c which uses threads.
These threads never clean up if exiting via ^C
*/
RESET_COLOR;
running = false;
/*Close IO*/
fclose(fpipes[0]);
fclose(fpipes[1]);
/* Call child */
kill(kid, SIGINT);
wait(NULL);
exit(0);
}
/************************************************
LOCAL FUNCTIONS END HERE
***********************************************/
signal(SIGPIPE, terminate);
signal(SIGINT, terminate);
/* Get parameters */
if (argc != 5) {
fprintf(stderr, "Usage:\n %s <server> <port> <nick> <channel>\n", argv[0]);
return -1;
}
serverName = argv[1];
port = argv[2];
nick = argv[3];
channel = argv[4];
/* Startup pipes */
pipe(ptelnetin);
pipe(ptelnetout);
/* Launch telnete */
switch (kid = fork()) {
case -1:
perror("OMG ABORTION at main");
exit(-2);
case 0: /* CHILD */
/*Overwrite stdin with pipein and discard pipe*/
dup2(ptelnetin[0], 0);
close(ptelnetin[0]);
close(ptelnetin[1]);
/*Overwrite stdout with pipeout and discard pipe*/
dup2(ptelnetout[1], 1);
close(ptelnetout[0]);
close(ptelnetout[1]);
/*Overwrite process image with telnete*/
execlp("./telnete", "./telnete", argv[1], argv[2], (char *) NULL);
perror("Call to exec failed at main");
exit(-3);
default: /* PARENT */
/* Close reading end of pipein */
close(ptelnetin[0]);
/* Close writing end on pipeout */
close(ptelnetout[1]);
}
/* Turn (fileno) into (FILE *) */
fpipes[1] = fdopen(ptelnetin[1],"w");
if(!fpipes[1]) {
perror("Error at fdopen(in) at main");
kill(kid, SIGINT);
abort();
}
fpipes[0] = fdopen(ptelnetout[0],"r");
if(!fpipes[0]) {
perror("Error at fdopen(out) at main");
kill(kid, SIGINT);
abort();
}
/* Sleep for a few seconds so server doesn't ignore it */
PRINT_YELLOW;
printf("Logging in IRC...\n");
RESET_COLOR;
fflush(stdout);
if (ircRegister(argv[3], fpipes[1], fpipes[0])) {
fprintf(stderr, "Error registering in IRC.\n");
terminate(-1);
}
PRINT_YELLOW;
printf("Joining room %s\n", argv[4]);
RESET_COLOR;
ircJOIN(fpipes[1], argv[4]);
fflush(fpipes[1]);
/* Launch threads */
if (pthread_create(&pthread_input, NULL, inputprocess, fpipes)){
fprintf(stderr,"Couldn't launch input thread");
kill(kid, SIGINT);
abort();
}
if (pthread_create(&pthread_output, NULL, outputprocess, fpipes)){
fprintf(stderr,"Couldn't launch output thread");
kill(kid, SIGINT);
abort();
}
/* Wait for threads */
if (pthread_join(pthread_input,NULL)){
fprintf(stderr, "Error joining thread.\n");
}
if (pthread_join(pthread_output,NULL)){
fprintf(stderr,"Error joining thread.\n");
}
terminate(0);
}
より明確にするために、ここにキーフラグメントを配置します。
/* Startup pipes */
pipe(ptelnetin);
pipe(ptelnetout);
/* Launch telnete */
switch (kid = fork()) {
case -1:
perror("OMG ABORTION at main");
exit(-2);
case 0: /* CHILD */
/*Overwrite stdin with pipein and discard pipe*/
dup2(ptelnetin[0], 0);
close(ptelnetin[0]);
close(ptelnetin[1]);
/*Overwrite stdout with pipeout and discard pipe*/
dup2(ptelnetout[1], 1);
close(ptelnetout[0]);
close(ptelnetout[1]);
/*Overwrite process image with telnete*/
execlp("./telnete", "./telnete", argv[1], argv[2], (char *) NULL);
perror("Call to exec failed at main");
exit(-3);
default: /* PARENT */
/* Close reading end of pipein */
close(ptelnetin[0]);
/* Close writing end on pipeout */
close(ptelnetout[1]);
}
/* Turn (fileno) into (FILE *) */
fpipes[1] = fdopen(ptelnetin[1],"w");
if(!fpipes[1]) {
perror("Error at fdopen(in) at main");
kill(kid, SIGINT);
abort();
}
fpipes[0] = fdopen(ptelnetout[0],"r");
if(!fpipes[0]) {
perror("Error at fdopen(out) at main");
kill(kid, SIGINT);
abort();
}
これを行った後、(FILE *) fpipes[0] から bc の結果を読み取り、fpipes[1] に書き込むことができます。各書き込みの後に忘れずに fflush(fpipes[1]) してください。この 2 つは、他のファイルと同じように扱ってください。