2017-04-04 124 views
-1

我想构建一个聊天程序,但问题是,一旦客户端发送消息到服务器,所有其他客户端将收到此消息从服务器,但客户端不不知道什么时候会有消息。在客户端的主循环中,它将阻塞fgets()并等待用户输入命令或消息。我需要客户端程序来接收消息并在等待用户输入时进行打印。我怎样才能做到这一点 ?c TCP聊天程序,客户端不知道何时recv

这里是代码: 我还没有写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; 
     } 
    } 
} 
+1

这不是如何工作。你不能要求我们为你写代码。显示你的努力。如果您提交代码,我们只能提供帮助。 – Arvindsinc2

+0

哪里?...请发布代码 – Arvindsinc2

+1

有两种选择。 1)线程2)选择。我们必须留给您阅读,并尝试为他们编写*代码*。 –

回答

0

'客户不知道何时recv'这很容易:所有的时间。 有多种选择:

1)在fd上选择(),包括标准输入(Google详细信息)。

2)poll/epoll() - 如上所述,但性能提高。

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

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

0

检查这个代码在这里:

#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),你可以使用它作为一个基地,研究如何选择作品和扩大它为您的需要阅读。

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

祝你好运。

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