使用select without listen()ing,可能吗?

时间:2012-06-16 11:33:35

标签: c++ sockets select

我正在建立一个客户:

  1. 应该能够从服务器和标准输入中接收信息
  2. 应该能够在不询问的情况下从服务器接收信息,例如当另一个客户端发送消息时。
  3. 为此,我尝试使用select来监控两种可能的输入。

    当监视键盘输入时,我发送一条消息给客户端,我希望有一个消息,所以没有问题。但是当服务器发送意外消息时没有任何反应,我不知道为什么。是否正确使用select()?是否可以在没有select()的情况下使用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;
    }
    

1 个答案:

答案 0 :(得分:2)

您的select来电无效。第一个参数必须是任何一组中最高的文件描述符,加一个 如果你拥有它,srverfd上的活动就不会“唤醒”select来电(除非STDIN_FILENO小于srverfd,在这种情况下stdin select }事件无法解锁getUserTweet - 但这在实践中不会发生。)


您的代码还存在其他一些问题。 (它看起来并不像C ++。)

temp不可靠(未定义的行为 - char*会在函数返回后立即销毁,因此当调用者尝试使用它时,您返回的getMessage已消失。 std::string也是如此。要解决此问题,请在任何地方使用char*,并在调用C库函数时仅提取readSocket

{{1}}不必要地复制FD集(可能很昂贵)。

你应该真正摆脱所有那些全局变量 - 构建一个或两个类来封装那个状态和网络功能,或类似的东西。