[VC++/WinSock] Obsługiwanie kilku połączeń przez serwer

0

Witam :). Problem jest czysto teoretyczny :P. Otóż mam program typu client/server. Rozpatrując program od strony serwera... otóż użyłem funkcji WSAAsyncSelect () ; aby otrzymywać informacje o połączeniu jako komunikaty. No i zaŁÓżmy, że otrzymuję taki komunikat FD_ACCEPT, ktory to jest wysyłany gdy klient próbuje nawiązać połączenie. Akceptowanie połączenia wygląda mniej więcej tak:

client = accept ( listening_sock , ( sockaddr * ) &client_address , &size ) ;

Ok, otrzymałem identyfikator gniazda klienta i mogę się teraz z nim komunikować, ale co mam zrobić gdy tych połączeń będzie więcej? Jak obsłużyć wszystkie połączenia i komunikować się z każdym klientem równolegle/jednocześnie? Jak to jest rozwiązywane w typowych programach client/server? Nie proszę o gotowy kod tylko o małe wyjaśnienie tego problemu...

0

nie wiem czy to jest najlepsze rozwiazanie ale ja zrobilem liste gniazd i za kazdym razem kiedy przychodzi nowe polaczenie dodaje nowe gniazdo do tej listy, potem w wysylaniu wysylam do odpowiednich/wszystkich gniazd z listy.

0

Hmmm... w sumie to chyba nie najgorsze rozwiązanie... muszę to przetestować, tymczasem może jeszcze jakieś inne sugestie?

0

proste rozwiazanie: dla kazdego nowego polaczenia serwer tworzy oddzielny watek...

0

Zawsze to się robi na wątkach. Do wątku podajesz socket klienta i przez niego czytasz/wysyłasz, a w tym czasie wątek główny wraca do czekania na nowych klientów.
To fragment wycięty z mojego obecnego programu - zabawki. Program główny zmienia Message oraz MessageTime, a wątek w razie wykrycia zmian wysyła to do klientów, czekając jednocześnie na jakieś komunikaty (pojedyncze litery) od nich:

const DWORD MsgLen = 1024;
const DWORD LagTime = 40;
const DWORD dwTimeOut = 5000;
const DWORD dwPause = 1000;

WORD Port = 10002;
bool End;
SOCKET server;

struct CThreadInfo
{
	HANDLE Handle;
	DWORD ID;
};

typedef CThreadInfo* VecList;
std::vector<VecList> ThreadList;

char Message[MsgLen];
DWORD MessageTime = 0;
CRITICAL_SECTION CriticalSection;

class CLink
{
/////////////////////////////////////////////
    private:
/////////////////////////////////////////////
        static void CreateThreads(LPTHREAD_START_ROUTINE pProc, LPVOID pParam)
        {
            CThreadInfo* ListItem = new CThreadInfo;
            ThreadList.push_back(ListItem);
            ListItem->Handle=CreateThread(NULL, 0, pProc, pParam, 0, &ListItem->ID);
            return;
        }

/////////////////////////////////////////////
        static bool CloseExited(VecList Item)
        {
            DWORD ReturnCode;
            GetExitCodeThread(Item->Handle, &ReturnCode);
            if (ReturnCode != STILL_ACTIVE)
            {
                CloseHandle(Item->Handle);
                delete Item;
                return true;
            }
            else
                return false;
        }

/////////////////////////////////////////////
        static void ReleaseThreads()
        {
            ThreadList.erase(remove_if(ThreadList.begin(), ThreadList.end(), CloseExited), ThreadList.end());
        }

/////////////////////////////////////////////
    public:
/////////////////////////////////////////////
        CLink()
        {
            CreateThreads(ServerThread, NULL);
        }

/////////////////////////////////////////////
        ~CLink()
        {
            closesocket(server);
            WSACleanup();
            Sleep(dwPause);
            ReleaseThreads();
        }

/////////////////////////////////////////////
        static DWORD WINAPI ServerThread(void* pParam)
        {
            SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
            WSADATA wsaData;
            sockaddr_in local;
            int wsaret=WSAStartup(0x101,&wsaData);
            if(wsaret!=0)
                return 0;
            local.sin_family=AF_INET;
            local.sin_addr.s_addr=INADDR_ANY;
            local.sin_port=htons((u_short)Port);
            server=socket(AF_INET,SOCK_STREAM,0);
            if(server==INVALID_SOCKET)
                return 0;
            if(bind(server,(sockaddr*)&local,sizeof(local))!=0)
                return 0;
            if(listen(server, 10)!=0)
                return 0;
            SOCKET client;
            sockaddr_in from;
            int fromlen=sizeof(from);
            Message[0]=0;
            MessageTime = GetTickCount();
            while (!End)
            {
                ReleaseThreads();
                client=accept(server, (struct sockaddr*)&from, &fromlen);
                CreateThreads(ClientThread,(LPVOID)client);	
            }	
            return 0;
        }

/////////////////////////////////////////////
        static DWORD WINAPI ClientThread(void* pParam)
        {
            DWORD ThreadTime=MessageTime;
            char ThreadMessage[MsgLen];
            strcpy(ThreadMessage, "Server v2.0\r\n");
            SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
            SOCKET client=(SOCKET)pParam;
            char rbuff[64] = "";
            send(client, ThreadMessage, strlen(ThreadMessage), 0);
            recv(client, rbuff, 63, 0);
            rbuff[63]=0;
            if (strcmp(rbuff, "Password") != 0)
            {
                closesocket(client);
                return 1;
            }
            HANDLE Event = WSACreateEvent();
            WSAEventSelect(client, Event, FD_READ);
            while ((rbuff[0] != 'q') && (rbuff[0] != 'x'))
            {
                if  (WSAWaitForMultipleEvents(1, &Event, FALSE, LagTime/2, TRUE)==WSA_WAIT_EVENT_0)
                {
                    recv(client, rbuff, 1, 0);
                    WSAResetEvent(Event);
                }
                else
                {
                    EnterCriticalSection(&CriticalSection);
                    if (ThreadTime != MessageTime)
                    {
                        ThreadTime = MessageTime;
                        strcpy(ThreadMessage, Message);
                    }
                    else
                        ThreadMessage[0]=0;
                    LeaveCriticalSection(&CriticalSection);
                    if (strlen(ThreadMessage)>0)
                    {
                        send(client, ThreadMessage, strlen(ThreadMessage), 0);
                    }
                }
            }
            if (rbuff[0] == 'x')
                End = true;
            WSACloseEvent(Event);
            closesocket(client);
            return 0;
        }
};

//Dopisane: podkreśliłem granice metod - dla ułatwienia czytania.

0
DWORD WINAPI ClientThread(LPVOID lpParam)
{
    SOCKET sock = (SOCKET)lpParam;
    
     //tutaj piszesz sobie instrukcje, ktore beda wykonywane dla kazdego klienta
     while(1)
    {


     }


}
//deklarujesz zmienne:
HANDLE hThread;
DWORD dwThreadID;

//... inicjujesz gniazdo i przy zaakceptowaniu polaczenia piszesz:
sClient = accept(sListen, (struct sockaddr *)&client, &iAddrSize);
hThread = CreateThread(NULL, 0, ClientThread,
        (LPVOID)sClient, 0, &dwThreadID);
//teraz nowy watek jest utworzony dla nowego klienta
0

ale kiedy uzywasz WSAAsyncSelect() z API to Windows automatycznie tworzy watki i przy odbieraniu i tak z ktorego kolwiek socketu przyjdzie bedzie w FD_READ, a przy wysylaniu mozna uzyc listy i watku :) wiec mysle ze przy korzystaniu z WINSOCK nie trzeba chyba tworzyc dla kazdego klienta nowego watku ?

0

No właśnie, spony ma chyba racje, zastosowanie wątków ma zalety tylko w przypadku aplikacji konsolowych lub przesyłania między serwerem a klientem większej ilości danych np. plików, które mogłoby spowodować nieodpowiadanie serwera na komunikaty klientów... W każdym razie dzieki za pomoc.

Aha a propoS tych komunikatów, do czego kurde służy FD_CONNECT? Próbowałem go jakoś obsłużyć, ale nigdy właściwie nie jest wysyłany ani do serwera, ani do klienta, hmm...

Jednak wybiorę opcję nie komunikatową, a wątkową, ale mam w tej sprawie pytanko. Wprawdzie czytałem dużo o wątkach ale nigdy jeszcze ich nie stosowałem :P. Tak więc, czy lepiej użyć CreateThread czy _beginthread? I jakie "informacje" o wątkach mam przechowywać, czy tylko uchwyty wątków czy także ID? Do czego właściwie te ID może się przydać?

Pozdrawiam.

1 użytkowników online, w tym zalogowanych: 0, gości: 1