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

时间:2012-01-06 18:41:10

标签: c select client chat

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

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

由于

1 个答案:

答案 0 :(得分:0)

一个疯狂的猜测:当你使用硬编码的ip-address:port tuple来访问服务器时,你确定它是正确的吗?


客户端2不“......必须写一些东西......”来接收,但在gets_s()等待用户输入被阻止,因此{{1}不能因此不能select()

要解决此阻止问题,您可以选择两种方法。

1多线程解决方案(更便携)

更改客户端设计以异步处理服务器连接(写入并读取它),例如在主线程的不同线程中。

2单线程解决方案(非便携式)

2.1 Windows

您可以使用recv()从控制台实现非阻塞读取。

例如,可以这样做:

ReadConsoleEvent()

2.2 UNIX

您可以使用/* * 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; } 的结果来检索标准输入的文件描述符,并将其添加到传递给fileno(stdin)的文件描述符集中,以便在通过控制台输入内容时收到通知。 / p>