__ __ __ .-----.--.--.----.| |.--.--.--| |.-----.--| | .-----.----.-----. | -__|_ _| __|| || | | _ || -__| _ |__| _ | _| _ | |_____|__.__|____||__||_____|_____||_____|_____|__|_____|__| |___ | by cc_ip - member of excluded-team |_____| ######################################### #TCP SOCKET PROGRAMMIERUNG UNTER WINDOWS# ######################################### 1. Vorwort 2. Der Client-Sourcecode 3. Erläuterung des Client-Sourcecodes 4. Der Server-Sourcecode 5. Erläuterung des Server-Sourcecodes 6. Schlusswort 7. Grüße 1. Vorwort: Dieses Tutorial behandelt den Einstieg in die Socketprogrammierung in Windows, wobei es bewusst sehr schlicht gehalten wurde damit Leute dennen Netzwerkprogrammierung neu ist, auch etwas damit anfangen können. In diesen Tutorial wird ein kleines Client Server (TCP) Chatprogramm geschrieben. Um dieses Tutorial verstehen zu können sind einige wenige Grundlagen in C, wie z.B. Datenstruckturen erforderlich. Da ich die Rechtschreibung nochnie beachtet habe, bitte ich euch sie doch auser Acht zu lassen.... Natürlich hafte ich für keine aus irgendwelchen Gründen entstandenen Schäden in jeglicher Art. Kopieren erlaubt, verändern nicht ! CLIENT: 2. Code: Ich habe mich entschieden, erst das Programm zu schreiben und danach die einzelen Befehle und Funktion zu entschlüsseln. Zuerst möchte ich den Client behandeln hier der Code dazu: #include #include #include #include int startWinsock(void); int main() { SOCKET s; SOCKADDR_IN addr; int check; char buf[300]; check = startWinsock(); printf("Das ist der Chat Client\nTaste druecken um Client zu starten!\n\n"); getch(); if(check!=0) { printf("Error: startwinsock, Error: %d\n",check); return 1; } else { printf("Winsock gestartet!\n"); } s=socket(AF_INET,SOCK_STREAM,0); if(s==INVALID_SOCKET) { printf("Error: Der socket wurde nicht erstellt, Error: %d\n",WSAGetLastError()); return 1; } else { printf("Socket erfolgreich generiert!\n"); } memset(&addr,0,sizeof(SOCKADDR_IN)); addr.sin_family=AF_INET; addr.sin_port=htons(55555); addr.sin_addr.s_addr=inet_addr("127.0.0.1"); check=connect(s,(SOCKADDR*)&addr,sizeof(SOCKADDR)); if(check==SOCKET_ERROR) { printf("Error: connect gescheitert, Error: %d\n",WSAGetLastError()); } else { printf("Verbindung mit der Server hergestellt.\n\n"); } while(check!=SOCKET_ERROR) { printf("\nZeichenfolge eingeben [max 299]: "); gets(buf); send(s,buf,strlen(buf),0); check=recv(s,buf,300,0); if(check==0) { printf("Server hat die Verbindung getrennt..\n"); break; } if(check==SOCKET_ERROR) { printf("Error: recv, Error: %d\n",WSAGetLastError()); break; } buf[check]='\0'; printf("Server antwortet: %s",buf); } getch(); } int startWinsock(void) { WSADATA wsa; return WSAStartup(MAKEWORD(2,0),&wsa); } 3.Erklärung: Ich gehe bewusst nicht auf jede Zeile des Codes ein, da das Programm so schon sehr schlicht gehalten ist und die Funktionen die ich nicht bearbeite sollte eigentl. jeder kennen der sich mit Netzwerkprogrammierung befassen möchte, auch möchte ich einen Gewissen "Tiefgang" vermeiden um möglichst wenig Verwirrung zu stiften. Wir benötigen folgende Header damit unser Compiler überhaupt die Socketfunktionen findet: #include #include (#include ) Da wir natülich auch was ausgeben wollen benötigen wir noch die Headerdatei für die standart In- Outputs: #include Für die kontrollierten In- und Outputs verwenden wir conio.h: #include --------------------------------------------------------------------------------------------- Zuerst legen wir eine Variable vom Typ SOCKET an: SOCKET s; Werden wir erst später gebrauchen und näher darauf eingehen: SOCKADDR_IN addr; Diese Variable vom Typ Integer verwenden wir um zu Überprüfen ob unser Programm einen Fehler verursacht hat: int check; Zum Schluß legen wir noch einen Array an den wir später mit den zu übertragenden Text füllen werden: char buf[300]; -------------------------------------------------------------------------------------------- Dieser Befehl wartet auf den nächsten Tastenanschlag, danach läuft der Kontrollfluß weiter: getch(); ------------------------------------------------------------------------------------------- Startet Winsock, hierbei wird der Wert check zugewiesen, dabei ist zu beachten, das es den Wert 0 zurück gibt wenn kein fehler aufgetreten ist: check = startWinsock(); Das eigentl. Socket wird erstellt: s=socket(AF_INET,SOCK_STREAM,0); Die Funktion is folgendermaßen definiert: SOCKET socket ( int af, int type, int protocol ); AF_INET = Hier wird die Protokollfamilie übergeben in unseren Fall ist das AF_INET das es sich um ein TCP/IP Socket der IP version 4 handelt. SOCK_STREAM = Hier wird die Art des Sockets über geben SOCK_STREAM steht für TCP Sockets SOCK_DGRAM für ein UDP Socket und SOCK_RAW für raw Sockets. 0 = Hier sollte eine Null stehen, auser man will mit raw Sockets arbeiten, da wir in diesen Tutorial aber keine raw Sockets behandeln möchte ich nicht weiter darauf eingehen. ------------------------------------------------------------------------------------------------ Falls es Probleme bei der Erstellung des Sockets gab wird INVALID_SOCKET zurückgegeben: if(s==INVALID_SOCKET) ------------------------------------------------------------------------------------------------ Mit WSAGetLastError() kann der letze Error festgestellt werden (meist ein Integer Wert), in unseren Fall warum das Socket nicht erstellt werden konnte: printf("Error: Der socket wurde nicht erstellt, Error: %d\n",WSAGetLastError()); ------------------------------------------------------------------------------------------------ Dann wolln wir ma noch n bissi "Freiraum" schaffen: memset(&addr,0,sizeof(SOCKADDR_IN)); void *memset(void *s, int c, size_t n); Die Funktion memset() schreibt n mal die Bytekonstante c in den Speicherbereicht, auf den s zeigt. SOCKADDR_IN ist eine einfache Datenstruktur die folgendermaßen aussieht: struct sockaddr_in{ short sin_family; unsigned short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; ------------------------------------------------------------------------------------------------ So nun wollen wir die Struktur noch ein bisschen mit sinnvollen Daten ausfüllen: addr.sin_family=AF_INET; Bei der addr family sollten wir wieder AF_INET angeben. addr.sin_port=htons(55555); Hier wird der Port angegeben mit dem wir eine connection aufbauen wollen in unsren fall 55555 die Funktion htons hilft nur zur vereinfachung der Schreibweise des Ports. addr.sin_addr.s_addr=inet_addr("127.0.0.1"); Hier muss die IP- Addresse des Ziels angegeben werden, in unsren Fall sind wir das selbst, da Client und Server später auf den Selben (eigenen) Rechner laufen sollen. ------------------------------------------------------------------------------------------------ Damit wir uns noch mit den Serverprogramm verbinden können, verwenden wir die Funktion connect: check=connect(s,(SOCKADDR*)&addr,sizeof(SOCKADDR)); s = entspricht den socket über das wir die Verbindung aufbauen wollen. SOCKADDR* = Hier sollte ein Zeiger auf SOCKADDR gesetzt sein. sizeof(SOCKADDR) = Hier geben wir die größe der Datenstrucktur an (SOCKADDR und SOCKADDR_IN kann man anstelle von SOCKADDR verwenden). ------------------------------------------------------------------------------------------------ Solange die Bedinung wahr ist wird der Schleifenkörper ausgeführ, um was es ihr geht ist hoffentlich offensichtlich: while(check!=SOCKET_ERROR) ------------------------------------------------------------------------------------------------ An dieser Stelle werden wir die eingelesenen Zeichen senden: send(s,buf,strlen(buf),0); s = Hier müssen wir das socket angeben über das gesendet werden soll. buf = Ja genau der soll jetz gesendet werden. strlen(buf) = Hier übergeben wir noch der Funktion die Länge des Strings. 0 = Hier schreibt ihr die alt bekannte Null rein . ------------------------------------------------------------------------------------------------ Damit wir auch Daten vom server empfangen können, verwenden wir die Funktion recv: check=recv(s,buf,300,0); s = Socket über das empfangen wird buf = Hier wird der Kram gespeichert 300 = max 300 Zeichen 0 = 0 wink ------------------------------------------------------------------------------------------------ Damit wir Sockets in Windows verwenden können müssen wir folgendes eintippen: int startWinsock(void) { WSADATA wsa; return WSAStartup(MAKEWORD(2,0),&wsa); } WSADATA wsa = Infos für unsre heiß geliebte winsock version. return WSAStartup(MAKEWORD(2,0),&wsa); MAKEWORD(2,0) = Damit verwenden wir die Winsock Version 2.0, hierbei hilft und MAKEWORD zu dieser einfachen schreibweise. &wsa = Hier werden der Funktion noch die oben genannten Infos übergeben. ------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------ SERVER: 4. Code: So, nun wollen wir uns das Serverprogramm mal etwas näher angucken. (Ich hoffe, die Beziehung zwischen Server und Client ist jedem klar) #include #include #include #include #include #include int startWinsock(void); int main() { long rc; SOCKET wartesocket; SOCKET connectedSocket; SOCKADDR_IN addr; char buf[300]; char buff[300]; rc = startWinsock(); printf("Das ist der Chatserver.\nTaste druecken um Server zu starten!\n"); getch(); if(rc!=0) { printf("\nError: startwinsock, Error code: %d\n",rc); return 1; } else { printf("\nWinsock gestartet!\n"); } wartesocket=socket(AF_INET,SOCK_STREAM,0); if(wartesocket==INVALID_SOCKET) { printf("Error: Der socket konnte nicht erstellt werden, Error code: %d\n",WSAGetLastError()); return 1; } else { printf("Socket erfolgreich generiert!\n"); } memset(&addr,0,sizeof(SOCKADDR_IN)); addr.sin_family=AF_INET; addr.sin_port=htons(55555); addr.sin_addr.s_addr=inet_addr("127.0.0.1"); rc=bind(wartesocket,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN)); if(rc==SOCKET_ERROR) { printf("Error: bind gescheitert, Error code: %d\n",WSAGetLastError()); } else { printf("Socket an Port 55555 gebunden\n"); } rc=listen(wartesocket,1); if(rc==SOCKET_ERROR) { printf("Error: listen, Error code: %d",WSAGetLastError()); } else { printf("wartesocket ist im Listen Modus warte auf Verbindung...\n"); } connectedSocket=accept(wartesocket,NULL,NULL); if(connectedSocket==INVALID_SOCKET) { printf("Error: accept, Error code: %d\n", WSAGetLastError()); } else { printf("Neue Verbindung wurde akzeptiert!\n\n"); } // Daten austauschen while(rc!=SOCKET_ERROR) { rc=recv(connectedSocket,buf,300,0); if(rc==0) { printf("Server hat die Verbindung getrennt..\n"); break; } if(rc==SOCKET_ERROR) { printf("Error: recv, Error code: %d\n",WSAGetLastError()); break; } buf[rc]='\0'; printf("Client sendet: %s\n",buf); printf("To send [Max 299]:"); gets(buff); rc=send(connectedSocket,buff,strlen(buff),0); } closesocket(wartesocket); closesocket(connectedSocket); getch(); } int startWinsock(void) { WSADATA wsa; return WSAStartup(MAKEWORD(2,0),&wsa); } 5. Erklärung: Zur Erklärung des Servers gült das gleiche wie beim Client. Wobei ich hier nicht mehr auf die beim Client erklärten Funktionen/Anweisungen eingehen werde. Da wir das socket nicht auf irgendeinen beliebigen Port haben wollen müssen wir es einen bestimmten Port zuweisen, dies geschieht mit der Funktion bind(). rc=bind(wartesocket,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN)); bind hat folgendes aussehen: int bind ( SOCKET s, const struct sockaddr FAR* name, int namelen ); wartesocket = das Socket dem wir einen Port zuweisen wollen (SOCKADDR*)&addr = hier wird wieder die Adressfamilie die IP und der Port dem wir den Socket zuweisen angegeben. sizeof(SOCKADDR_IN) = Hier müssen wir wieder ein bisschen platz schaffen damit wir alles unterkriegen wink ---------------------------------------------------------------------------------------------------- Damit wir das Socket auf eine einkommende Verbindung warten lassen können verwenden wir listen(): rc=listen(wartesocket,1); wartesocket = das ist der socket der nun auf eine einkommende Verbindung wartet 1 = gibt an wieviele Verbindungen noch ausstehen dürfen. ----------------------------------------------------------------------------------------------------- Damit das Socket auch auf eingehende Verbindungen reagiert und diese annimmt verwenden wir accept(), accept() warte bis eine Verbindung eingeht: connectedSocket=accept(wartesocket,NULL,NULL); accept() hat folgende Struktur: SOCKET accept ( SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen ); wartesocket = das ist unser socket das die Verbindungen annimmt. NULL = Zeiger auf SOCKADDR mit welchen Informationen über den Client gesammelt werden können NULL = hier wird die Addressenlänge gespeichert ----------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------- 6. Schlusswort: Ja soweit so gut, das war jetzt mein kleines Tutorial zu TCP Socketprogrammierung, es wurden wie gesagt nur die Mauern des Gebäudes besprochen, um die Einrichtung noch passend hinzubekommen müsst ihr warscheinlich noch etwas Zeit mit Sockets verbringen. 7. Grüße: -Excluded-Team. -"Ad" Offen****en