1

私は次のようなクライアントを構築しています:

  1. サーバーと標準入力の両方から情報を受信できる必要があります
  2. たとえば、別のクライアントがメッセージを送信した場合など、サーバーから情報を要求せずに受信できる必要があります。

そうするために、選択を使用して、可能な両方の入力を監視しようとしました。

何が起こるかというと、キーボード入力が監視されているときにクライアントにメッセージを送信し、メッセージが返されることを期待しているので、問題はありません。しかし、サーバーが予期しないメッセージを送信しても何も起こらず、その理由がわかりません。select() を使用するのは適切な方法ですか? ingselect()なしでも使用できますか?listen()

これが私のコードです(コンパイル可能):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <cstring>
#include <arpa/inet.h>
#include <iostream>
#include <fstream>

#define MAX_CLIENT_NAME 30
#define MAX_TWIT_SIZE 140
#define NUM_OF_ARG 4
#define ERROR -1
#define GREAT_SUCCESS 0
#define OK "OK"
#define EXIT "EXIT"


using std::string;
using std::cerr;
using std::endl;
using std::cout;

string clientName;

int srverfd, numbytes, status, maxSock ;

fd_set inputFdSet;        /* Socket file descriptors we want to wake
                       up for, using select() */

int establishConnection(char * serverAddress,char * port){
    if ((srverfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        return ERROR;
    }
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    inet_aton(serverAddress, &server.sin_addr);
    server.sin_port = htons(atoi(port));
    memset(&(server.sin_zero), '\0', 8);

    if (connect(srverfd,(const struct sockaddr *)&server,sizeof(struct sockaddr)) == -1) {
        perror("connect");
        close(srverfd);
        return ERROR;
    }
    maxSock = srverfd;
    return GREAT_SUCCESS;
}

const char * getUserTweet(){
    string temp;
getline(std::cin,temp);
    return temp.c_str();
}

void sendMessage(string message){
    if ((numbytes = send(srverfd, message.c_str(), message.length(), 0)) == -1) {
        perror("sendMessage");
        close(srverfd);
    }
    cout<<"Message sent: "<< message << endl;
    return;
}

const char * getMessage(){
    char buf[MAX_TWIT_SIZE];
    memset(buf,'\0',MAX_TWIT_SIZE);
    if ((numbytes = recv(srverfd, buf, 140, 0)) == -1) {
        perror("getMessage");
        close(srverfd);
    }
    string temp = buf;
    return temp.c_str();
}

void build_select_list() {
    FD_ZERO(&inputFdSet);
    FD_SET(srverfd,&inputFdSet);
    FD_SET(STDIN_FILENO,&inputFdSet);
    if (STDIN_FILENO > maxSock)
        maxSock = STDIN_FILENO;
    return;
}

void readSocket(fd_set tempfd) {
    const char * tweet, * inMessage;
    if (FD_ISSET(srverfd,&tempfd)) {
        inMessage = getMessage();
        cout << inMessage << endl;
    }

    if (FD_ISSET(STDIN_FILENO,&tempfd)) {
        tweet = getUserTweet();
        sendMessage(tweet);
        inMessage = getMessage();
        if (strcmp(inMessage,OK) != 0) {
            cout << inMessage << endl;
        }
        if (strcmp(inMessage,EXIT) == 0) {
            return;
        }
    }
    return;
}

int main (int argc, char *argv[] ){
    int value;
    bool clientON = false;
    if(establishConnection(argv[2],argv[3])){
        cerr << "usage: failed to make connection" << endl << "exiting..." << endl;
        exit(EXIT_FAILURE);
    }

    cout << "Connected successfully" << endl;
    sendMessage("CONNECT "+clientName); //Connect
    if(strcmp(getMessage(),OK) == 0){
        clientON = true;
    }
    while(clientON){
        build_select_list();
        value = select(maxSock, &inputFdSet, NULL, NULL, NULL);
        if (value < 0) {
            perror("select");
            exit(EXIT_FAILURE);
        }
        if (value == 0) {
            continue;
        }
        else {
            readSocket(inputFdSet);
        }
    }
    sendMessage("DISCONNECT");
    if(strcmp(getMessage(),OK) == 0){
        // do nothing
    }
    close(srverfd);
    return 0;
}
4

1 に答える 1

2

あなたのselect電話は無効です。最初のパラメーターは、任意のセットの中で最上位のファイル記述子に1 を加えたものでなければなりません。
あなたが持っているように、上のイベントは呼び出しsrverfdを「目覚めさせません」 (何らかの理由で が より小さかった場合を除きます。その場合、イベントはロック解除されませんが、実際には発生しません)。selectSTDIN_FILENOsrverfdstdinselect


コードには他にもかなりの問題があります。(実際には C++ のようには見えません。)

getUserTweet信頼性がありません (未定義の動作 -temp関数が戻るとすぐに破棄されるため、char*呼び出し元が使用しようとするまでに戻り値が消えてしまいます)。についても同じですgetMessage。これを改善するには、どこでも使用し、C ライブラリ関数を呼び出すときにstd::stringのみ抽出します)。char*

readSocket不必要に FD セットをコピーします (コストがかかる可能性があります)。

これらすべてのグローバルを本当に取り除く必要があります。1 つまたは 2 つのクラスを構築して、その状態とネットワーク機能などをカプセル化します。

于 2012-06-16T13:19:14.827 に答える