Multiuser-Server??
-
Alooaaaaaaaaa!
Ich habe ein Problem, und zwar folgendes:
Habe jetzt mich mal ein wenig mit der Socketprogrammierung auseinandergesetzt und gleich einen kleinen Server geschrieben, mit dem man sich verbinden und mit ihm Daten austauschen kann. Jetzt habe ich aber ein kleines Problem, und zwar kann sich nur ein einziger Client da anmelden. Wie geht das aber, wenn sich mehrere Clients anmelden können??
Ich habe mir das mal so gedacht: Einen Array machen, z.B.SOCKET connectedSocket[10];
soll dann ermöglichen, 10 User dort anzumelden. Ok dann noch einen Integer der die aktuelle anzahl der Clients hat.
Jetzt habe ich aber das Problem, wenn sich nun einer angemeldet hat, wie kann der Server dann gleichzeitig listen, senden und empfangen???
In einer Schleife denke ich irgendwie, aber wie???Gruss, code_pilot
-
du solltest deinen server asynchron laufen lassen ... sprich WSAAsynchSelect() verwenden und events wie FD_READ und FD_ACCEPT über eine CALLBACK abfangen, dann läufts ... listen() läuft dann auch im hintergrund weiter ...
ach ja, in der CALLBACK steckt der SOCKET des jeweiligen clients im WPARAM ... nur so als hinweis noch.
so far ... rocknix ///
PS: WERBUNG IN EIGENER SACHE - DEMNÄCHST (CA. 2-3 WOCHEN NOCH - IST IN ARBEIT) GIBTS GENAU ZU DIESER PROBLEMSTELLUNG EINE DEMO-DLL AUF MEINER WEBPAGE ... WATCH OUT !
-
Original erstellt von RockNix:
**WSAAsynchSelect() verwenden und events wie FD_READ und FD_ACCEPT über eine CALLBACK abfangen ...
**Hi!
Danke, kannst du mir mal vielleicht ein kleines Beispiel geben???Grüsse, code_pilot :p
-
Hmmm ich sehe grad das das nur für Fensteranwendungen geht, ich wills aber in einer DOS-Box haben ... habe mir mal select() angeguckt, aber es funktioniert nicht. Hier mal der Code meines kleinen Servers:
#include <windows.h> #include <winsock2.h> #include <stdio.h> #define MAXCLIENTS 10 struct clients { char name[256]; int socket; }; int main() { long rc; int clients = 0; int selectres; SOCKET acceptSocket; SOCKET connectedSocket[MAXCLIENTS]; SOCKADDR_IN addr; char buf[256]; char buf2[300]; WSADATA wsa; fd_set fds; //Timeval für select struct timeval tv; tv.tv_sec=5; tv.tv_usec=5000; printf("Loading winsock..."); rc = WSAStartup(MAKEWORD(2,0),&wsa); if(rc!=0) { printf("Failed: %d\n",rc); return 1; } else { printf("Done\n"); } printf("Loading socket..."); acceptSocket=socket(AF_INET,SOCK_STREAM,0); //IPPROTO_TCP ?? if(acceptSocket==INVALID_SOCKET) { printf("Failed\n"); return 1; } else { printf("Done\n"); } printf("Starting server..."); memset(&addr,0,sizeof(SOCKADDR_IN)); addr.sin_family=AF_INET; addr.sin_port=htons(12345); //Port addr.sin_addr.s_addr=ADDR_ANY; rc = bind(acceptSocket,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN)); if(rc==SOCKET_ERROR) { printf("Failed\n"); return 1; } else { printf("Done\n"); } listen(acceptSocket,MAXCLIENTS); connectedSocket[0] = accept(acceptSocket,NULL,NULL); while(1) { FD_ZERO(&fds); selectres = select(MAXCLIENTS+1,&fds,NULL,NULL,&tv); printf("Select result: %d\n", selectres); printf("Listening..."); //listen(acceptSocket,0) } for(int i = 0;i<MAXCLIENTS;i++) { closesocket(connectedSocket[i]); } WSACleanup(); return 0; }
was mache ich falsch???
code_pilot
[ Dieser Beitrag wurde am 29.01.2003 um 12:53 Uhr von code_pilot editiert. ]
-
hm, hier ein älterer QUICK HACK von mir ... so in der art muss du das machen ...
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HINSTANCE hInst; static PAINTSTRUCT ps; // variables for tcp/ip #define WM_TCP_EVENT (WM_USER+1) #define TCP_PORT 1234 static WSADATA WSAData; //static SOCKADDR sa; static SOCKADDR_IN sa; static SOCKADDR connection_add; static SOCKET tcp_socket = 0; int iError = 0; int bytes = 0; WORD wEvent=0, wError=0; static SOCKET sock_client; switch(message) { case WM_CREATE: hInst = (HINSTANCE) (((LPCREATESTRUCT) lParam)->hInstance); iError = WSAStartup (MAKEWORD(2,0), &WSAData); tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(tcp_socket==INVALID_SOCKET) exit(0); iError = WSAAsyncSelect(tcp_socket, hwnd, WM_TCP_EVENT, FD_READ|FD_ACCEPT); sa.sin_addr.s_addr = INADDR_ANY; sa.sin_family=AF_INET; sa.sin_port=htons(TCP_PORT); iError = bind(tcp_socket, (LPSOCKADDR) &sa, sizeof(sa)); listen(tcp_socket,SOMAXCONN); if(iError == SOCKET_ERROR) { closesocket( tcp_socket ); return FALSE; } return 0; case WM_DESTROY: closesocket (tcp_socket) ; tcp_socket = 0 ; WSACleanup () ; PostQuitMessage(0); return 0; case WM_TCP_EVENT: wEvent = WSAGETSELECTEVENT(lParam); wError = WSAGETSELECTERROR(lParam); switch(wEvent) { case FD_READ: return 0; case FD_ACCEPT: sock_client=accept(tcp_socket, &connection_add, NULL); return 0; } return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
wie gesagt ein QUICK HACK --- keine gewähr für leaks oder ähnliches, war halt nur zum testen
hope it helps .... rocknix ///
[ Dieser Beitrag wurde am 29.01.2003 um 12:54 Uhr von RockNix editiert. ]
-
hmm, du meinst, weil du ein HWND brauchst, oder ?
mal ein tip:
ich habe den ganzen kram in einer klasse gekapselt, die sich für diesen fall beim instanzieren ein eigenes (nicht sichtbares) fenster erzeugt, von dem der user nichts bemerkt ... dann kann man das auch in einer konsole verwenden.
übrigens, etwaige diskussion von wegen "nimm statt dummy-window lieber WSAEventSelect" sind nicht angebracht, da das handling nicht dasselbe ist !!!
rocknix ///
-
Hmmmm...
Habe jetzt mal folgendes probiert:
FD_ZERO(&fds); FD_SET(connectedSocket[0],&fds); selectres = select(MAXCLIENTS+1,&fds,NULL,NULL,&tv); printf("Select result: %d\n", selectres); printf("Listening...\n"); switch(selectres) { case 0: break; case 1: rc = recv(connectedSocket[0],buf,256,0); if(rc > 0) { buf[rc]='\0'; printf("Client: %s\n",buf); sprintf(buf2,"Answering your request: %s", buf); printf("Sending...\n",buf); rc = send(connectedSocket[0],buf2,strlen(buf2),0); } break; } }
das steht jetzt in meiner Schleife. Funkt auch, solange ich mit nur einem client angemeldet bin. Schalte ich jtzt aber einen 2ten CLient hinzu, dann kriege ich weder einen accept-request noch sonst irgend etwas von select().
Meine Fragen:
1. Was macht FD_SET(connectedSocket[0],&fds); bzw. was muss ich da reinkopieren??
2. Wie kriege ich herraus von welchem Client eine request, welches select() zurückgibt, kommt??Ich hab mal versucht das mit einer for-schleife zu machen, die dann bei jedem Client prüft ob was von ihm kommt aber das ging irgendwie nich :(:(:(:(
Vielen Dank schonmal im vorraus,
code_pilot
-
hmm, habe es nie ohne den asynchronen mechnismuss probiert, war mir zu umständlich und auch nicht so ganz windows alike
was spricht dagegen ?
rocknix ///
-
OK was ich nun wissen will ist: Was ist dieses fd_set??
Ich habe ein fd_set mit dem Namen fds. Ich habe das jetzt so verstanden das dieses fd_set eine Liste an sockets enthält, die select() dann prüfen soll.Ich mache zuerst:
for(int i = 0;i < MAXCLIENTS;i++) { FD_SET(connectedSocket[i],&fds); }
um den fd_set fds zu füllen. Dann tu ich select() ausführen:
selectres = select(MAXCLIENTS+1,&fds,&fds,NULL,&tv);
und dann mache ich noch
for(int i=0;i < MAXCLIENTS;i++) { if(FD_ISSET(i,&fds)) { switch(selectres) { case 0: break; case 1: ... . . .
nun ja und das alles endet mit einem Programm, welches überhaupt nichts mehr tut, als ständig -1 bei select() zurückzuliefern.
Meine Frage: WAS MACHE ICH FALSCH?????
code_pilot
-
eigentlich ist FD_SET nichts weiter wie eine STRUCT ... es gibt da aber ein paar macros, die diese STRUCT "umbiegen" können
FD_SET
typedef struct fd_set {
u_int fd_count; /* how many are SET? /
SOCKET fd_array[FD_SETSIZE]; / an array of SOCKETs */
} fd_set;Element Usage
fd_count The number of sockets that are set.
fd_array An array of the sockets in the set.hier ein auszug aus dem msdn:
Four macros are defined in the header file WINSOCK2.H for manipulating and checking the descriptor sets. The variable FD_SETSIZE determines the maximum number of descriptors in a set. (The default value of FD_SETSIZE is 64, which can be modified by defining FD_SETSIZE to another value before including WINSOCK2.H.) Internally, socket handles in an FD_SET structure are not represented as bit flags as in Berkeley Unix. Their data representation is opaque. Use of these macros will maintain software portability between different socket environments. The macros to manipulate and check FD_SET contents are:
FD_CLR(s, *set)
Removes the descriptor s from set.
FD_ISSET(s, *set)
Nonzero if s is a member of the set. Otherwise, zero.
FD_SET(s, *set)
Adds descriptor s to set.
FD_ZERO(*set)
Initializes the set to the NULL set.rocknix ///
[ Dieser Beitrag wurde am 29.01.2003 um 16:23 Uhr von RockNix editiert. ]
-
Jaaaa aber es ist doch richtig, das ich bei select() ein fd_set übergeben muss der die Sockets die ich verwende enthält. WIE stelle ich aber diese Liste ZUSAMMEN??? Wie schreibe ich die Sockets die er prüfen soll hinein??? Ich will doch nur prüfen, wenn ich einen client habe, und sich ein zweiter (oder dritter oder vierter...) einloggt dann soll der server prüfen ob ein request vorliegt und von welchem client er kommt! Aber irgendwie findet man weder im web, noch in der Hilfe noch im Forum brauchbares Material *heul*.
Der traurige code_pilot :(:(:(