Странное поведение recv()
От: s3dworld Россия  
Дата: 20.11.11 10:59
Оценка:
Всем доброго дня!

Проблема моя связанная с тем, что когда сервер отправляет клиенту сообщение через send(), клиент через select() определяет что пора вызвать recv() и этот самый recv() возвращает 0, то есть сервер разорвал соединение с клиентом. Но я ведь не разрывал, а просто прислал сообщение. Почему так? Сам уже два дня не могу понять причину. Помогите! Ниже всё подробно...

Есть сервер, со следующим алгоритмом работы:

1. WSAStartup()
2. socket()
3. bind()
4. listen()
5. accept()
6. send()
7. recv()
8. goto 7

Сервер работает нормально. Если что, вот полный код:

#pragma comment(lib,"ws2_32.lib")

#include <iostream>
#include <WinSock2.h>

using namespace std;

SOCKET listener=0;
SOCKET client=0;

void release(void);

int main(void)
{
    WSADATA sockData;

    cout<<"Init... ";

    if(WSAStartup(MAKEWORD(2,2),&sockData))
    {
        cout<<"[ERROR]"<<endl;
        release();
        return -1;
    }

    cout<<"[OK]"<<endl;
    cout<<"Socket... ";

    listener=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    if(listener==INVALID_SOCKET)
    {
        cout<<"[ERROR]"<<endl;
        release();
        return -1;
    }

    sockaddr_in sockAddr;

    memset(&sockAddr,0,sizeof(sockaddr_in));

    sockAddr.sin_family                = AF_INET;
    sockAddr.sin_addr.S_un.S_addr    = ADDR_ANY;
    sockAddr.sin_port                = 30002;

    cout<<"[OK]"<<endl;
    cout<<"Bind... ";

    if(bind(listener,(const sockaddr*)&sockAddr,sizeof(sockaddr_in))==SOCKET_ERROR)
    {
        cout<<"[ERROR]"<<endl;
        release();
        return -1;
    }

    cout<<"[OK]"<<endl;
    cout<<"Listen... ";

    if(listen(listener,20)==SOCKET_ERROR)
    {
        cout<<"[ERROR]"<<endl;
        release();
        return -1;
    }

    int size=sizeof(sockaddr_in);

    cout<<"[OK]"<<endl;
    cout<<"Accept... ";

    client=accept(listener,(sockaddr*)&sockAddr,&size);

    if(client==INVALID_SOCKET)
    {
        cout<<"[ERROR]"<<endl;
        release();
        return -1;
    }

    cout<<"[OK]"<<endl;

    char buf[256];

    memset(buf,0,256);

    buf[0]='S';
    buf[1]='e';
    buf[2]='r';
    buf[3]='g';
    buf[4]='e';
    buf[5]='i';

    int res=0;

    cout<<"Send... ";

    int index=0;

    while(index!=6)
    {
        int snd=send(client,buf+index,6-index,0);

        if(snd==SOCKET_ERROR)
        {
            cout<<"[ERROR]"<<endl;
            release();
            return -1;
        }

        index+=snd;
    }

    cout<<"[OK]"<<endl;

    bool isExit=false;

    while(!isExit)
    {
        cout<<"Wait... ";
        res=recv(client,buf,256,0);

        if(res==0)
        {
            cout<<"[DISCONNECT]"<<endl;
            isExit=true;
        }
        else if(res==SOCKET_ERROR)
        {
            if(WSAGetLastError()==WSAECONNRESET)
                cout<<"[DISCONNECT]"<<endl;
            else
                cout<<"[ERROR]"<<endl;
            isExit=true;
        }
        else
        {
            cout<<"[MESSAGE]"<<endl;
        }
    }

    release();
    return 0;
}

void release(void)
{
    if(listener) closesocket(listener);
    if(client) closesocket(client);
    WSACleanup();
    system("pause");
}


Есть клиент, алгоритм работы которого следующий:

1. WSAStartup()
2. socket()
3. bind()
4. connect()

А в отдельном потоке (проверку на разрешение записи я не делаю):

1. select()
2. recv()

Если более подробно, то так происходит создание клиента:

result client_base::init(const string& _local_ip,port _local_port,const string& _remote_ip,port _remote_port,event_type _type,client_event* _event,time _wait_time,size _length)
{
    if(!_remote_ip.length()) return error_parameter;
    if(!_event) return error_pointer;
    if(!_length) return error_parameter;

    eventType=_type;
    eObject=_event;
    waitTime=_wait_time;

    sClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    if(sClient==INVALID_SOCKET)
    {
        sClient=0;
        return error_socket;
    }

    sockaddr_in sockAddr;

    memset(&sockAddr,0,sizeof(sockaddr_in));

    sockAddr.sin_family    = AF_INET;
    sockAddr.sin_port    = _local_port;

    char* buf=0;

    if(_local_ip.length())
    {
        buf=new (std::nothrow) char[_local_ip.length()+1];
        if(!buf) return error_memory;

        memset(buf,0,_local_ip.length()+1);

        if(!WideCharToMultiByte(CP_ACP,0,_local_ip(),_local_ip.length(),buf,_local_ip.length(),0,0))
        {
            delete[] buf;
            return error_os;
        }

        sockAddr.sin_addr.S_un.S_addr=inet_addr(buf);
        delete[] buf;
    }

    if(bind(sClient,(const sockaddr*)&sockAddr,sizeof(sockaddr_in))==SOCKET_ERROR) return error_socket;

    memset(&sockAddr,0,sizeof(sockaddr_in));

    sockAddr.sin_family    = AF_INET;
    sockAddr.sin_port=_remote_port;

    buf=new (std::nothrow) char[_remote_ip.length()+1];
    if(!buf) return error_memory;

    memset(buf,0,_remote_ip.length()+1);

    if(!WideCharToMultiByte(CP_ACP,0,_remote_ip(),_remote_ip.length(),buf,_remote_ip.length(),0,0))
    {
        delete[] buf;
        return error_os;
    }

    sockAddr.sin_addr.S_un.S_addr=inet_addr(buf);
    delete[] buf;

    if(connect(sClient,(const sockaddr*)&sockAddr,sizeof(sockaddr_in))==SOCKET_ERROR) return error_socket;

    hThread=CreateThread(0,0,winThread,this,0,&hThreadID);

    if(!hThread)
    {
        closesocket(sClient);
        sClient=0;
        return error_os;
    }

    return ok;
}


При этом функция потока:

DWORD WINAPI client_base::winThread(void* _param)
{
    return ((client_base*)_param)->thread();
}


В свою очередь:

DWORD client_base::thread(void)
{
    int sel=0;
    FD_SET fdsRead;
    timeval time;
    client_base* ins=0;

    FD_ZERO(&fdsRead);

    if(eventType==event_receive || eventType==event_both) FD_SET(sClient,&fdsRead);

    time.tv_sec=0;
    time.tv_usec=(long)waitTime*1000;

    eObject->dns_client_connect();

    if(bClose)
    {
        eObject->dns_client_close(close_client);
        EnterCriticalSection(&csRelease);
        ins=instance;
        instance=0;
        LeaveCriticalSection(&csRelease);
        if(ins) delete ins;
        return ok;
    }

    while(true)
    {
        if(waitTime)
            sel=select(0,&fdsRead,0,0,&time);
        else
            sel=select(0,&fdsRead,0,0,0);

        switch(sel)
        {
            case 0:
                eObject->dns_client_close(close_time);
                EnterCriticalSection(&csRelease);
                ins=instance;
                instance=0;
                LeaveCriticalSection(&csRelease);
                if(ins) delete ins;
                return ok;

            case SOCKET_ERROR:
                switch(WSAGetLastError())
                {
                    case WSAENOTSOCK:
                        switch(error)
                        {
                            case cet_ok:
                                break;

                            case cet_netdown:
                            case cet_reset:
                                eObject->dns_client_close(close_error);
                                break;

                            case cet_client:
                                eObject->dns_client_close(close_client);
                                break;

                            case cet_server:
                                eObject->dns_client_close(close_server);
                                break;

                            default:
                                eObject->dns_client_close(close_error);
                                break;
                        }

                        EnterCriticalSection(&csRelease);
                        ins=instance;
                        instance=0;
                        LeaveCriticalSection(&csRelease);
                        if(ins) delete ins;
                        return ok;

                    default:
                        eObject->dns_client_close(close_error);
                        break;
                }

                EnterCriticalSection(&csRelease);
                ins=instance;
                instance=0;
                LeaveCriticalSection(&csRelease);
                if(ins) delete ins;
                return error_socket;

            default:
                break;
        }

        if(FD_ISSET(sClient,&fdsRead))
        {
            binary bin;
            cet err;
            result res;

            res=read(bin,&err);

            switch(res)
            {
                case ok:
                    switch(err)
                    {
                        case cet_ok:
                            eObject->dns_client_receive(bin);
                            if(bClose)
                            {
                                EnterCriticalSection(&csRelease);
                                ins=instance;
                                instance=0;
                                LeaveCriticalSection(&csRelease);
                                if(ins) delete ins;
                                return ok;
                            }
                            break;

                        case cet_client:
                            eObject->dns_client_close(close_client);
                            EnterCriticalSection(&csRelease);
                            ins=instance;
                            instance=0;
                            LeaveCriticalSection(&csRelease);
                            if(ins) delete ins;
                            return ok;

                        case cet_server:
                            eObject->dns_client_close(close_server);
                            EnterCriticalSection(&csRelease);
                            ins=instance;
                            instance=0;
                            LeaveCriticalSection(&csRelease);
                            if(ins) delete ins;
                            return ok;

                        default:
                            eObject->dns_client_close(close_error);
                            EnterCriticalSection(&csRelease);
                            ins=instance;
                            instance=0;
                            LeaveCriticalSection(&csRelease);
                            if(ins) delete ins;
                            return ok;
                    }
                    break;

                case error_memory:
                    eObject->dns_client_close(close_error);
                    EnterCriticalSection(&csRelease);
                    ins=instance;
                    instance=0;
                    LeaveCriticalSection(&csRelease);
                    if(ins) delete ins;
                    return error_memory;

                default:
                    eObject->dns_client_close(close_error);
                    EnterCriticalSection(&csRelease);
                    ins=instance;
                    instance=0;
                    LeaveCriticalSection(&csRelease);
                    if(ins) delete ins;
                    return error_socket;
            }
        }
    }

    EnterCriticalSection(&csRelease);
    ins=instance;
    instance=0;
    LeaveCriticalSection(&csRelease);
    if(ins) delete ins;
    return ok;
}


И сама функция считывания:

result client_base::read(binary& _binary,cet* _error)
{
    int res=0;
    byte* buf=new (std::nothrow) byte[recvLength];

    if(!buf)
    {
        *_error=cet_other;
        return error_memory;
    }

    memset(buf,0,recvLength);

    res=recv(sClient,(char*)buf,recvLength,0);

    switch(res)
    {
        case 0:
            *_error=cet_server;
            closesocket(sClient);
            sClient=0;
            delete[] buf;
            return ok;

        case SOCKET_ERROR:
            switch(WSAGetLastError())
            {
                case WSAENETDOWN:
                    *_error=cet_netdown;
                    closesocket(sClient);
                    sClient=0;
                    delete[] buf;
                    return error_socket;

                case WSAENOTCONN:
                case WSAENOTSOCK:
                    *_error=cet_client;
                    closesocket(sClient);
                    sClient=0;
                    delete[] buf;
                    return ok;

                case WSAENETRESET:
                case WSAECONNABORTED:
                case WSAETIMEDOUT:
                    *_error=cet_reset;
                    closesocket(sClient);
                    sClient=0;
                    delete[] buf;
                    return error_socket;

                case WSAECONNRESET:
                    *_error=cet_server;
                    closesocket(sClient);
                    sClient=0;
                    delete[] buf;
                    return ok;

                default:
                    *_error=cet_other;
                    closesocket(sClient);
                    sClient=0;
                    delete[] buf;
                    return error_socket;
            }
            break;
    }

    if(!_binary.assign(buf,res))
    {
        *_error=cet_other;
        delete[] buf;
        return error_memory;
    }

    *_error=cet_ok;
    delete[] buf;
    return ok;
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.