【问题标题】:c TCP chat program, client don't know when to recvc TCP聊天程序,客户端不知道什么时候recv
【发布时间】:2017-04-05 02:48:08
【问题描述】:

我想建立一个聊天程序,但问题是一旦客户端向服务器发送消息,所有其他客户端都会收到来自服务器的这条消息,但客户端不知道消息何时到来。在客户端的主循环中,它将阻塞 fgets() 并等待用户输入命令或消息。我需要客户端程序接收消息并在等待用户输入时打印它。我该怎么做?

代码如下: 我还没有为 message 写 recv,因为我不知道该放在哪里。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <time.h>

#define LOGIN 1
#define LO_ACK 2
#define LO_NACK 3
#define EXIT 4
#define JOIN 5
#define JN_ACK 6
#define JN_NACK 7
#define LEAVE_SESS 8
#define NEW_SESS 9
#define NS_ACK 10
#define MESSAGE 11
#define QUERY 12
#define QU_ACK 13

struct packet {
    unsigned int type;
    unsigned int size;
    char source[20];
    char data[500];
};

int encode(struct packet temp, char *data) {

    sprintf(data, "%d:%d:%s:", temp.type, temp.size,
            temp.source);

    int length = strlen(data);

    int i;
    for (i = 0; i < temp.size; i++) {
        data[length + i] = temp.data[i];
    }
    data[length + i] = '\0';

    return length;
}

struct packet decode(char *data) {
    int i, j;
    struct packet message;
    char temp[100];
    char source[20];

    sscanf(data, "%d:%d", &message.type, &message.size);

    sprintf(temp, "%d:%d", message.type, message.size);

    int length = strlen(temp);

    for (i = length + 1; data[i] != ':'; i++) {
        message.source[i - length - 1] = data[i];
    }

    for (j = 0; j < message.size; j++) {
        message.data[j] = data[j + i + 1];
    }

    return message;

}

int main(void) {
    int sockfd, numbytes;
    struct addrinfo hints, *servinfo, *p;
    int rv;
    int login = 0;
    char me[20];


    while (1) {
        char buf[500];
        char input[100];
        char *command;
        char arg1[20], arg2[20], arg3[20], arg4[20], arg5[20];
        int i, j, k, l, m;

        fgets(input, 100, stdin);

        if (strlen(input) < 3)
            continue;

        if (input[0] == '/') {//command
            command = &input[1];

            //get first argument
            for (i = 0; command[i] != '\0' && command[i] != ' '; i++) {
                arg1[i] = command[i];
            }
            //arg1[i] = '\0';

            if (strcmp(arg1, "login") == 0) {//login
                //get id,password,ip,port
                if (login == 1) {
                    printf("error: already login\n");
                    continue;
                }

                for (j = i + 1; command[j] != '\0' && command[j] != ' '; j++) {//id
                    arg2[j - i - 1] = command[j];
                }
                //arg1[j-i+1] = '\0';
                for (k = j + 1; command[k] != '\0' && command[k] != ' '; k++) {//password
                    arg3[k - j - 1] = command[k];
                }

                for (l = k + 1; command[l] != '\0' && command[l] != ' '; l++) {//ip
                    arg4[l - k - 1] = command[l];
                }

                for (m = l + 1; command[m] != '\0'; m++) {//port
                    arg5[m - l - 1] = command[m];
                }

                memset(&hints, 0, sizeof hints);
                hints.ai_family = AF_UNSPEC;
                hints.ai_socktype = SOCK_STREAM;

                if ((rv = getaddrinfo(arg4, arg5, &hints, &servinfo)) != 0) {
                    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
                    return 1;
                }

                for (p = servinfo; p != NULL; p = p->ai_next) {
                    if ((sockfd = socket(p->ai_family, p->ai_socktype,
                            p->ai_protocol)) == -1) {
                        perror("client: socket");
                        continue;
                    }
                    if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
                        close(sockfd);
                        perror("client: connect");
                        continue;
                    }
                    break;
                }

                if (p == NULL) {
                    fprintf(stderr, "client: failed to connect\n");
                    return 2;
                }

                freeaddrinfo(servinfo);

                struct packet tosend;
                tosend.type = LOGIN;
                sprintf(tosend.data, "%s %s", arg2, arg3);
                strcpy(tosend.source, arg2);
                tosend.size = strlen(tosend.data);

                char message[500];
                encode(tosend, message);

                send(sockfd, message, strlen(message), 0);

                usleep(100);
                recv(sockfd, buf, strlen(buf), 0);
                struct packet reply;
                reply = decode(buf);

                if (reply.type == LO_ACK) {
                    printf("login successful\n");
                    strcpy(me, arg2);
                    login = 1;
                    continue;
                } else if (reply.type == LO_NACK) {
                    printf("login failed: %s\n", reply.data);
                    continue;
                }

            } else if (strcmp(arg1, "createsession") == 0) {
                if (login == 0) {
                    printf("error: not login\n");
                    continue;
                }

                for (j = i + 1; command[j] != '\0'; j++) {//session name
                    arg2[j - i - 1] = command[j];
                }

                struct packet tosend;
                tosend.type = NEW_SESS;
                strcpy(tosend.data, arg2);
                strcpy(tosend.source, me);
                tosend.size = strlen(tosend.data);

                char message[500];
                encode(tosend, message);

                send(sockfd, message, strlen(message), 0);

                usleep(100);
                recv(sockfd, buf, strlen(buf), 0);
                struct packet reply;
                reply = decode(buf);

                if (reply.type == NS_ACK) {
                    printf("create session successful\n");
                    continue;
                } else if (reply.type == JN_ACK) {
                    printf("session already exist, join session successful\n");
                    continue;
                }
            } else if (strcmp(arg1, "joinsession") == 0) {
                if (login == 0) {
                    printf("error: not login\n");
                    continue;
                }

                for (j = i + 1; command[j] != '\0'; j++) {//session name
                    arg2[j - i - 1] = command[j];
                }

                struct packet tosend;
                tosend.type = JOIN;
                strcpy(tosend.data, arg2);
                strcpy(tosend.source, me);
                tosend.size = strlen(tosend.data);

                char message[500];
                encode(tosend, message);

                send(sockfd, message, strlen(message), 0);

                usleep(100);
                recv(sockfd, buf, strlen(buf), 0);
                struct packet reply;
                reply = decode(buf);

                if (reply.type == JN_ACK) {
                    printf("join session successful\n");
                    continue;
                }
            } else if (strcmp(arg1, "leavesession") == 0) {
                if (login == 0) {
                    printf("error: not login\n");
                    continue;
                }

                struct packet tosend;
                tosend.type = LEAVE_SESS;
                strcpy(tosend.data, "none");
                strcpy(tosend.source, me);
                tosend.size = strlen(tosend.data);

                char message[500];
                encode(tosend, message);

                send(sockfd, message, strlen(message), 0);

                printf("leave session successful\n");

                continue;
            } else if (strcmp(arg1, "list") == 0) {
                if (login == 0) {
                    printf("error: not login\n");
                    continue;
                }

                struct packet tosend;
                tosend.type = QUERY;
                strcpy(tosend.data, "none");
                strcpy(tosend.source, me);
                tosend.size = strlen(tosend.data);

                char message[500];
                encode(tosend, message);

                send(sockfd, message, strlen(message), 0);

                usleep(100);
                recv(sockfd, buf, strlen(buf), 0);
                struct packet reply;
                reply = decode(buf);

                printf("%s", reply.data);

                continue;
            } else {
                printf("invalid command\n");
                continue;
            }

        } else {//message
            if (login == 0) {
                printf("error: not login\n");
                continue;
            }

            struct packet tosend;
            tosend.type = MESSAGE;
            strcpy(tosend.data, input);
            strcpy(tosend.source, me);
            tosend.size = strlen(tosend.data);

            char message[500];
            encode(tosend, message);

            send(sockfd, message, strlen(message), 0);

            continue;
        }
    }
}

【问题讨论】:

  • 这不是 SO 的工作方式。您不能要求我们为您编写代码。展示你的努力。只有您提交代码,我们才能提供帮助。
  • 在哪里?...请张贴代码
  • 有两种选择。 1) 线程 2) 选择。我们将不得不留给您阅读它们并尝试为它们编写代码
  • 还有很多可用的库:libev、libevent、libuv 等等。

标签: c tcpclient


【解决方案1】:

'客户不知道何时接收'这很容易:一直。 有多种选择:

1) fd 上的 select(),包括标准输入(Google 了解详情)。

2) poll/epoll() - 同上,但提高了性能。

3) 有一个线程等待输入并将消息发送到生产者-消费者队列中,然后发送到另一个处理这些消息的“状态机”线程,以及来自处理来自服务器的客户端 recv() 数据的其他线程的消息,并执行所需的操作。

4) 您的特定操作系统可能提供的任何其他机制。

【讨论】:

    【解决方案2】:

    在此处检查此代码:

    #include <stdio.h>
    #include <sys/select.h>
    #include <unistd.h>
    
     int main()
     {
    
        fd_set fds;
    
        while(1){
            FD_ZERO(&fds); // you NEED to zero this every loop
            FD_SET(STDIN_FILENO, &fds);
            // add your socket here
    
            // first argument here will be your socket + 1
            select(STDIN_FILENO + 1, &fds, NULL, NULL, NULL);
            if (FD_ISSET(STDIN_FILENO, &fds)){
                  char buffer[128];
                  fgets(buffer, sizeof(buffer), stdin);
                  printf("User input - stdin: %s", buffer);
            }
            // here check if your socket is set and act accordingly
         }
    
         return 0;
     }
    

    它将使用 select (http://man7.org/linux/man-pages/man2/select.2.html) 从标准输入读取,您可以将其作为基础来研究 select 的工作原理并根据您的需要对其进行扩展。

    您需要在 fds 中添加您的套接字,以便它等待选择,直到有用户输入要处理或您的套接字上有数据(使用 FD_ISSET 进行检查)。

    祝你好运。

    PS:我使用这个问题的代码Using stdin with select() in C 作为基础来生成我发布的那个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-05
      相关资源
      最近更新 更多