使用消息结构

时间:2016-11-15 10:08:30

标签: c networking server udp chat

我一直在尝试设置一个简单的UDP聊天服务器来学习如何建立自己的聊天室,对于初学者来说,我正忙着与共享相同IP /端口的客户端运行UDP服务器作为服务器。客户端以结构的形式发送数据。 服务器应该有三种可能性,一个新的客户端加入,它通过匹配命令JOIN或通过匹配命令QUIT退出客户端,或者通过匹配这些命令来消息来识别...

这些命令存储在客户端以(ID,COM​​MAND,Domain / Port)形式发送的结构中。 除非它是一条消息,否则它将是(ID,域/端口,消息)。我在我的代码中处理了哪些内容。

客户端100%工作,因为它是作为源提供给我的,但我不能使用它,因为它的可执行文件,所以问题来自我的服务器。我做了一个调试工具来检查出错了什么,好像当一个客户端加入并且我向客户承认我已经接受了他时,客户端永远不会收到确认网,那就是因为我没有正确格式化它。 它应该是Received: <1 JOIN loki>然后Sent: <1 JOIN loki>,其中带括号的3个变量是结构部分..但是它没有正确存储第二个字符串,即域/端口部分...所以当我运行调试工具Received: <1 JOIN>SENT <1 JOIN>。 那么为什么会这样做,就像我将结构解析为我的缓冲区结构然后发送它。为什么它没有正确捕获域名部分?

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define LOCAL_PORT 2317
#define MAX_LEN 255

struct message {

    int   cid;                    /* Connection ID */

    char  str1[MAX_LEN];         /* JOIN, QUIT, or client-domain-name */

    char  str2[MAX_LEN];        /* user-string or client-domain-name */

};



int rcv_cid;

int rc,sd;

struct message my_msg, rcv_msg;

char rcv_str1[MAX_LEN];

char rcv_str2[MAX_LEN];




int main(int argc, char* argv[]) {

    struct sockaddr_in  servAddr;
     int servLen = sizeof(servAddr);
    int IDlist [] = {0,0,0,0,0,0,0,0,0,0} ;
    int i=0;  int port;
    /* Socket Creation */
    if(argc < 3){
        printf("usage : ./chatServer <port#> <debug_option:0 or 1>\n");
        exit(1);
    }
    sd = socket(AF_INET, SOCK_DGRAM , 0);
    if(sd<0) {
        printf("%s , cannot open socket \n",argv[0]);
        exit(1);
    }

     /* bind argv[1] server port */
     servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    /* bind argv[1] which is what the user provides */
        port = atoi(argv[1]);
    servAddr.sin_port = htons(port);

       rc= bind(sd, (struct sockaddr *) &servAddr , sizeof (servAddr));
if(rc <0){
    printf("%s: cannot bind port number %d \n", argv[0], port);
    exit (1);
}
printf("%s: waiting for data on port UDP %d \n", argv[0], port);

while(1) {
    /* grab incoming request */
    rc = recvfrom(sd, &rcv_msg, MAX_LEN, 0,   (struct sockaddr *) &servAddr, &servLen);
   printf(" %s \n" , rcv_msg.str2);
    if(atoi(argv[2]) == 1){
       printf("DEBUG: Receiving <%d %s %s>\n",rcv_msg.cid,rcv_msg.str1,rcv_msg.str2);
   }
    if(strcmp(rcv_msg.str1,"join") == 0 ) {

        for( i =1; i< 11 ; i ++){
            if(IDlist[i] ==  rcv_msg.cid ){
                rcv_msg.cid = i;
                IDlist[i] = rcv_msg.cid;
                break;}
        }



    /*         for(j = 0, j < 10 ; j ++){                     */
    /*          if(IDlist[j]  !=  IDlist [i] ){              */

    my_msg.cid = i;
    strcpy(my_msg.str1, "JOIN");
    strcpy(my_msg.str2, rcv_msg.str2);    

    if(atoi(argv[2]) == 1){
            printf("DEBUG: Sending <%d %s %s>\n",my_msg.cid,my_msg.str1,my_msg.str2);
                                }

    rc = sendto(sd, &my_msg, sizeof(my_msg)+1, 0,
            (struct sockaddr *) &servAddr,
            sizeof(servAddr));
    /* compose a temp struct , then send it to each ID on the list except the one we just added */
    }


if(strcmp(rcv_msg.str1,"QUIT") == 0 ) {

    for( i=0;i<10 ; i++){
        if(rcv_msg.cid == IDlist[i])
            IDlist [i] = 0;
    }
    my_msg.cid =  rcv_msg.cid;
    strcpy(my_msg.str1, "QUIT");
    strcpy(my_msg.str2, rcv_msg.str2);    /*my name Nodname contains domain and port number built into C library */

if(atoi(argv[2]) == 1){
                    printf("DEBUG: Sending <%d %s %s>\n",my_msg.cid,my_msg.str1,my_msg.str2);
                                                        }


    rc = sendto(sd, &my_msg, sizeof(my_msg)+1, 0,
            (struct sockaddr *) &servAddr,
             sizeof(servAddr));
 }
 else {

my_msg.cid =  rcv_msg.cid;
strcpy(my_msg.str1,  rcv_msg.str1);
strcpy(my_msg.str2, rcv_msg.str2);    /*my name Nodname contains domain and port number built into C library */

  if(atoi(argv[2]) == 1){
                printf("DEBUG: Sending <%d %s %s>\n",my_msg.cid,my_msg.str1,my_msg.str2);
                                                    }


rc = sendto(sd, &my_msg, sizeof(my_msg)+1, 0,
        (struct sockaddr *) &servAddr,
        sizeof(servAddr));

                   }

           }

        return 0;
      }

1 个答案:

答案 0 :(得分:0)

您只允许系统读取最多MAX_LEN个字节:

rc = recvfrom(sd, &rcv_msg, MAX_LEN, 0, 
    (struct sockaddr *)&servAddr, &servLen
);

但是rcv_msg的大小是它的两倍(2 * MAX_LEN + sizeof(int))。正确的是:

rc = recvfrom(sd, &rcv_msg, sizeof(rcv_msg), 0,
    (struct sockaddr *)&servAddr, &servLen
);

同样servLen必须包含socklen_t类型,而不是int;不能保证两种类型相同或可互换。

这段代码也错了:

rc = sendto(sd, &my_msg, sizeof(my_msg)+1, 0,
    (struct sockaddr *)&servAddr, sizeof(servAddr)
);

my_msg正好是sizeof(my_msg),所以为什么要告诉系统发送sizeof(my_msg)+1个字节? +1在那里做什么?这一个字节超过了结构的结尾,它不属于结构,这个内存可能是填充或已经属于另一个变量。您不应该在结构之后读取该字节,如果您运气不好,这甚至可能会导致程序崩溃。

然后你需要小心大写。在您的代码中,命令始终是大写(“JOIN”),但此代码行与小写字符串进行比较:

 if(strcmp(rcv_msg.str1,"join") == 0 ) {

strcmp区分大小写,strcmp("JOIN", "join")不会返回0

您是否认为问题可能不是接收方没有正确接收消息但发送方没有正确发送消息?我只有你的接收者代码,我没有任何发送者代码。你说客户端的工作方式与你在源代码中得到的一样,但这并不意味着它能够正常工作,或者以你认为的方式发送数据。在没有看到客户端如何发送数据的情况下,我无法真正告诉您问题的确切原因,我也没有测试服务器代码的测试用例。

如果我修复了上面指出的所有问题,并让服务器向自己发送测试消息,那么在我的系统上工作正常,你甚至可以直接在线测试代码,并且它也可以正常工作,请看这里:< / p>

http://rextester.com/MSH79937