Задержка при получении сообщения (выбрать) чат-клиент - PullRequest
0 голосов
/ 06 января 2012

Я пишу чат-приложение (клиент и сервер), используя C в VS 2010.

Я закончил писать свой код, как вы можете видеть ниже, но проблема все еще существует. Эта проблема заключается в том, что клиент не получает вовремя сообщения, которые отправляет сервер.

Код сервера:

#include <WinSock2.h>
#include <stdio.h>
#include <time.h>



main()
{

    SOCKET          ListeningSocket;
    SOCKET          AcceptSocket;

    SOCKADDR_IN     ServerAddr;
    SOCKADDR_IN     ClientAddr;

    WSADATA         wsaData;

    const unsigned short PORT = 4444;

    FD_SET          fdread;
    FD_SET          BackUpfdread;
    FD_SET          fdwrite;
    FD_SET          BackUpfdwrite;

    int             maxDescriptor;
    SOCKET          SocketArray[20];
    int             index = 0;
    int             selectResults;
    int             i,k;
    int             clientAddrSize;
    int             RecvBytes;
    int             SentBytes;

    char            SentBuff[500];
    char            RecvBuff[500];

    struct  timeval timeout;


    // Initialize Winsock2.2
    WSAStartup(MAKEWORD(2,2),&wsaData);

    // Initialize Listening Socket
    ListeningSocket = socket(AF_INET,SOCK_STREAM,0);


    // Initialize ServerAddr
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    ServerAddr.sin_port = htons(PORT);

    // Bind the ServerAddr with ListeningSocket
    bind(ListeningSocket,(SOCKADDR *)&ServerAddr,sizeof(ServerAddr));

    // Listening Socket
    listen(ListeningSocket,5);

    FD_ZERO(&fdread);
    FD_ZERO(&BackUpfdread);
    FD_ZERO(&fdwrite);
    FD_ZERO(&BackUpfdwrite);

    // Asign ListeningSocket at fdread
    FD_SET(ListeningSocket,&fdread);

    maxDescriptor = ListeningSocket;

    SocketArray[index] = ListeningSocket;
    index++;

    timeout.tv_sec = 0;
    timeout.tv_usec = 0;

    // Main loop starts here
    for(; ;)
    {

        BackUpfdread = fdread;
        BackUpfdwrite = fdwrite;
        selectResults = select(maxDescriptor+1,&BackUpfdread,&BackUpfdwrite,NULL,&timeout);
        //printf("server select() OK\n");

        if(selectResults == -1)
        {
            printf("Select() error");
            WSACleanup();
            return 0;
        }


        for(i=0;i<=index-1;i++)
        {
            //printf("%d\n",SocketArray[i]);
            if(FD_ISSET(SocketArray[i],&BackUpfdread))
            {
                if(SocketArray[i] == ListeningSocket) // we have a new connection
                {
                    clientAddrSize = sizeof(ClientAddr);
                    AcceptSocket = accept(ListeningSocket,(SOCKADDR *)&ClientAddr,&clientAddrSize);

                    // Add the newest accepted socket to the fdread and fdwrite sets.
                    FD_SET(AcceptSocket,&fdread);
                    FD_SET(AcceptSocket,&fdwrite);

                    // Add the newest accepted socket into SocketArray
                    SocketArray[index] = AcceptSocket;
                    index++;

                    // keep track of the maxDescriptor.
                    if(AcceptSocket > maxDescriptor)
                    {
                        maxDescriptor = AcceptSocket;
                    }

                    printf("New connection from %s on socket %d\n", inet_ntoa(ClientAddr.sin_addr), AcceptSocket);

                }else{ // That means that the socket is not from a new conection and has something sent.

                    memset(RecvBuff,0,sizeof(RecvBuff));
                    RecvBytes = recv(SocketArray[i], RecvBuff, sizeof(RecvBuff)-1, 0);

                    if(RecvBytes > 0) // Some data received.
                    {

                        printf("Message Sent.\n");
                        printf("Message was: %s\n",RecvBuff);

                        for(k=0;k<=index-1;k++)
                        {

                            if(SocketArray[k] != ListeningSocket && SocketArray[k] != SocketArray[i])
                            {
                                memset(SentBuff,0,sizeof(SentBuff));
                                strcpy(SentBuff,RecvBuff);
                                SentBytes = send(SocketArray[k],SentBuff,sizeof(SentBuff),0);

                                if(SentBytes > 0)
                                {
                                    printf("Message Forwarded\n");
                                }else{
                                    printf("Message forward error\n");
                                }

                            }


                        }

                    }

                }

            }
        }

        SleepEx(10, FALSE);

    }// Main loop ends here


}

Код клиента:

#include <WinSock2.h>
#include <stdio.h>
#include <time.h>



main()
{

    SOCKET          ConnectSocket;
    SOCKET          SocketArray[20];

    SOCKADDR_IN     ServerAddr;

    WSADATA         wsaData;

    FD_SET          fdwrite;
    FD_SET          fdread;
    FD_SET          BackUpfdread;
    FD_SET          BackUpfdwrite;

    char            server_address[20] = "192.168.1.4";
    char            SentBuff[500];
    char            RecvBuff[500];

    const unsigned short PORT = 4444;

    int             maxDescriptor;
    int             index = 0;
    int             SelectResults;
    int             i;
    int             RecvBytes;
    int             SentBytes;

    BOOL            bOpt = TRUE;

    struct timeval  timeout;




    // Initialize Winsock 2.2
    WSAStartup(MAKEWORD(2,2),&wsaData);

    // Initialize ServerAddr
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_addr.s_addr = inet_addr(server_address);
    ServerAddr.sin_port = htons(PORT);

    // Create a new socket to make a client connection.
    ConnectSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    setsockopt(ConnectSocket, IPPROTO_TCP, TCP_NODELAY,(char*)&bOpt,sizeof(BOOL));

    // Clear the fd sets
    FD_ZERO(&fdread);
    FD_ZERO(&BackUpfdread);
    FD_ZERO(&fdwrite);
    FD_ZERO(&BackUpfdwrite);

    // Asign ConnectSocket into fdread and fdwrite.
    FD_SET(ConnectSocket,&fdread);
    FD_SET(ConnectSocket,&fdwrite);

    // Set timer
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;

    maxDescriptor = ConnectSocket;
    SocketArray[index] = ConnectSocket;
    index++;

    // Make a connection to the server with socket s.
    if(connect(ConnectSocket, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
    {
        printf("Couldn't connect to the server\n");
    }


    // Main loop starts here
    for(; ;)
    {
        BackUpfdread = fdread;
        BackUpfdwrite = fdwrite;

        memset(SentBuff, 0, sizeof(SentBuff));
        printf("Write: ");
        gets_s(SentBuff, sizeof(SentBuff)); 

        SelectResults = select(maxDescriptor+1,&BackUpfdread,&BackUpfdwrite,NULL,&timeout);

        for(i=0;i<=index-1;i++)
        {
            // Something to read from server.
            if(FD_ISSET(SocketArray[i],&BackUpfdread) && SocketArray[i] == ConnectSocket) 
            {
                RecvBytes = recv(SocketArray[i], RecvBuff, sizeof(RecvBuff), 0);

                if(RecvBytes > 0)
                {
                    printf("%s\n",RecvBuff);   
                    // Cleaning the Receive Buffer 
                    memset(RecvBuff,0,sizeof(RecvBuff));
                }

            }

            // Something to write.
            if(FD_ISSET(SocketArray[i],&BackUpfdwrite) && SocketArray[i] == ConnectSocket)
            {
                SentBytes = send(SocketArray[i], SentBuff,sizeof(SentBuff),0);
                // Cleaning the Sent Buffer 
                memset(SentBuff,0,sizeof(SentBuff));

            }


        }

        SleepEx(10, FALSE);

    } // Main menu ends here

}

На мой взгляд, что-то не работает на стороне клиента. Я говорю это, потому что, если я использую приложение telnet в качестве клиентских сообщений, переданных правильно.

У кого-нибудь есть идеи, как это исправить?

Спасибо

1 Ответ

0 голосов
/ 07 января 2012

Просто дикая догадка: поскольку вы используете жестко закодированный IP-адрес: порт кортежа для доступа к серверу, вы уверены, что он правильный?


Клиент 2 не "... должен что-то написать ..." для получения, но заблокирован в gets_s() в ожидании ввода пользователя и поэтому не может select() и, следовательно, не может recv().

Чтобы решить эту проблему блокировки, вы можете использовать два подхода.

1 Многопоточное решение (более переносимое)

Измените дизайн клиента для асинхронной обработки соединения с сервером (записи в него и чтения из него), например, в другом потоке, а не в основном потоке.

2 Однопоточное решение (непереносное)

2,1 Windows

Вы можете использовать ReadConsoleEvent() для реализации неблокирующего чтения с консоли.

Например, это можно сделать так:

/*
 * Does a non-blocking read of characters from the stdin into the
 * buffer referenced by 'pszBuffer' up to a maximum of 'sizeBuffer'.
 *
 * If bEcho is TRUE the characters read are echoed to the console window.
 *
 * Returns the characters read or a negative value on error.
 */
DWORD ReadConsoleNonBlocking(char * pszBuffer, size_t sizeBuffer, BOOL bEcho)
{
  DWORD dwRC = 0;
  static HANDLE stdinHandle = 0;

  stdinHandle = GetStdHandle(STD_INPUT_HANDLE);

  {
    if (!pszBuffer)
      return -1;

    if (!sizeBuffer)
      return -2;

    {
      INPUT_RECORD input = {0};
      DWORD NumberOfEventsRead = 0;

      if (!ReadConsoleInput(stdinHandle, &input, 1, &NumberOfEventsRead)) 
        return -3;

      if (NumberOfEventsRead && (KEY_EVENT == input.EventType) && input.Event.KeyEvent.bKeyDown)
      {
        DWORD dwCharactersRead = 0;

        while (input.Event.KeyEvent.wRepeatCount > dwCharactersRead && dwCharactersRead < sizeBuffer)
        {
          pszBuffer[dwCharactersRead] = input.Event.KeyEvent.uChar.AsciiChar;

          if ('\r' == pszBuffer[dwCharactersRead])
            pszBuffer[dwCharactersRead] = '\n';

          if (bEcho)
            printf("%c", pszBuffer[dwCharactersRead]); /* echo character read to console */

          ++ dwCharactersRead;
        }

        dwRC = dwCharactersRead;
      }
    }
  }

  return dwRC;
}

2.2 UNIX

Вы можете использовать результат fileno(stdin), чтобы получить дескриптор файла для стандартного ввода и добавить его к набору дескрипторов файлов, переданных в select(), чтобы получать уведомления, если что-то было введено через консоль.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...