在(选择)聊天客户端上接收消息延迟

问题描述 投票:0回答:1

我正在 VS 2010 中使用 C 编写一个聊天应用程序(客户端和服务器)。

我已经完成了代码编写,如下所示,但仍然存在问题。 这个问题是客户端没有按时收到服务器发送的消息。

服务器代码:

#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 应用程序作为客户端消息传输正确。

有人知道如何解决这个问题吗?

c client chat posix-select
1个回答
0
投票

只是一个大胆的猜测:当您使用硬编码的 ip-address:port 元组来访问服务器时,您确定它是正确的吗?


客户端 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()
的文件描述符集中,以便在通过控制台输入某些内容时收到通知。

© www.soinside.com 2019 - 2024. All rights reserved.