PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : TCP Server - Socket Verbindung



rxenon
25-06-2007, 15:15
Hallo!

Ich habe angefangen in C einen TCP Server zu implementieren. Später mal soll das ganze als Chat Server dienen oder auch für anderes.
Was bisher funktioniert, ist, dass der Server einen Socket erstellt und auf diesem auf einen Client wartet. Dann wird ein weiterer Socket erstellt zu dem Clienten. Das ganze, damit sich später auch mehrere Clienten anmelden können.
Zu dem angemeldeten Client kann ich auch problemlos Bytes senden (der client ist erstmal telnet von linux).

Jetzt kommen die Probleme:
1. Wenn ich Daten vom Client empfangen will, kommen immer nur 3 Zeichen an. Am Buffer zum einlesen kann das aber nicht liegen, da er String 1024 Zeichen lang ist.

2. Benutze ich telnet.exe (von Windows XP) um auf den Server zuzugreifen, dann wird jedes Zeichen direkt gesendet. Bei Linux wird immer gewartet, bis der Benutzer Enter drückt, bevor gesendet wird. Bei Windows hingegen wird jedes Zeichen direkt gesendet. Gibt es eine Möglichkeit für den Server erst aufzuhören mit empfangen, bis der Telnet Benutzer Enter gedrückt hat? Vlt. hat jemand Erfahrung mit den Besonderheiten der telnet.exe .

Das hier ist noch mein Code, falls jemand den braucht:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> //declaration of 'sockaddr_in'
#include <sys/time.h>
#include <unistd.h>
#include <string.h>


int tcp_server_send(int fd, char buffer[]) {
int result;
result = send(fd, buffer, strlen(buffer), 0);
if(result==-1 ) perror("send failure");
return result;
}

int tcp_server_receive(int fd, char buffer[]) {
int bytes;
bytes = recv(fd, buffer, sizeof(buffer)-1, 0);
if(bytes==-1) perror("receive failure");
buffer[bytes] = '\0';
return bytes;
}

int tcp_server_init(int port) {
struct sockaddr_in addr;
int fd;

fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd==-1) perror("Could not create socket");

addr.sin_port = htons(port);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);

if( bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1 ) perror("Could not bind socket");

if( listen(fd, 3) == -1 ) perror("listen to socket failed");

return fd;
}

int main(int argc, char *argv[]) {
struct sockaddr_in cli_info;
int listen_sock, client, cli_size;
char name[1024], buffer[1024];

if(argc!=2) {
printf("usage: %s port\n", argv[0]);
return 1;
}


listen_sock = tcp_server_init(atoi(argv[1]));

cli_size = sizeof(cli_info);
for(;;) {
client = accept(listen_sock, (struct sockaddr *) &cli_info, &cli_size);

tcp_server_send(client, "Welcome!\n\tusername: ");

tcp_server_receive(client, name);

//sprintf(buffer, "\nYour name is: %s\n", name);
//tcp_server_send(client, buffer);

close(client);

printf("username: %s\n", name);
break;
}

}

Gsus
27-06-2007, 11:10
Hey rxenon,

versuch es doch mal so mit dem Empfangen:



char mesg[1024];

while(recv(fd, buffer, sizeof(buffer)-1, 0))
{
strcat(mesg, buffer);
}


ist keine schoene loesung aber mit hat es mal geholfen ! Es kann sein das bei TCP nich alles in einem Rutsch ankommt. Nomalerweisse sollte das egal sein weil TCP es schon puffert. Aber ich kanns nicht wirklich erklaeren bei mir hats dann immer geklapt.

rxenon
27-06-2007, 12:51
ja das kann sehr gut sein! Ich werde es nachher mal testen.

panzi
28-06-2007, 15:13
Die Funktion tcp_server_receive kann so nicht funktionieren. char buffer[] ist nur eine andere Syntax für char * buffer. Ein sizeof(buffer) liefert dann auf einer 32bit Plattform 4 (4Bytes = 32bit), weil ein Pointer 4Bytes gros ist (bzw. auf 64bit wären es 8Byte). Du musst die Größe des buffers manuell mitgeben. Das ist halt so in C. Ist extrem lowlevel und in einen Array steht nunmal nicht wie groß es ist.


int tcp_server_receive(int fd, char buffer[], size_t n) {
int bytes = 0;
if(n > 0) { /* wenn n == 0 würe n-1 bei unsigned einen overflow bewirken und die größt mögliche unsigned int Zahl würde rauskommen */
bytes = recv(fd, buffer, n-1, 0);
}
if(bytes==-1) perror("receive failure");
buffer[bytes] = '\0';
return bytes;
}

Der Aufruf muss dann entsprechend so aussehn:

tcp_server_receive(client, name, 1024);

Wobei an der Stelle der Compiler weiß wie groß name ist (da es ja mit char name[1024] und nicht char name[] angelegt wurde) also würde auch das gehn:

tcp_server_receive(client, name, sizeof(name));

Wobei ich würde das ja so machen, dass ich am Anfang oder in einer Header-Datei folgendes Makro definiere:

#define BUFFER_SIZE 1024
Und dann das so verwende (damit wenn ich name mit char * name = malloc(BUFFER_SIZE) anlege es auch noch funktioniert):

char name[BUFFER_SIZE];
/* ... */
tcp_server_receive(client, name, BUFFER_SIZE);
Aber das ist Geschmackssache.

rxenon
29-06-2007, 00:36
Ah ja super, dass du dir das alles sehr genau angeschaut hast! Ich kenne mich zwar recht gut aus in C aber mache einfach noch zu viele Fehler, da keine Routine....
Also das war auf jeden Fall der ausschlaggebende Fehler und es funktioniert jetzt auch soweit. Vielen Dank!

Jetzt kommt nur noch das kleine Problem mit dem telnet client von Windows. Hat da jemand eine Idee?

panzi
29-06-2007, 01:12
Ah ja super, dass du dir das alles sehr genau angeschaut hast! Ich kenne mich zwar recht gut aus in C aber mache einfach noch zu viele Fehler, da keine Routine....
Jo das sind halt die typischen Anfängerfehler. Eins muss man wissen: C kennt die größe eines Arrys nicht! Also kannst du auch das machen:

char a[5];
a[200] = 0;
Das ist auch das häufigste und schlimmst Sicherheitsloch das man so findet. Auch heute noch. Das lässt einen echte Schauer über den Rücken laufen das immer noch so programmiert wird, schnell mal einen Buffer am Stack anlegen. *brrr*

Warum ist das so ein schweres Sicherheitsloch? Bei so einen BUfferoverflow wird Speicher überschrieben der nicht überschrieben werden soll. Dazu muss man wissen das der Stack auf x86 (und vielen anderen Systemen) von oben nach unten wächst. Also ein positiver bufferoverflow überschreibt somit den Stackframe von der aktuellen Funktion (der ja vor den lokalen Variablen am stack steht) und somit auch die Rücksprungsadresse. Wenn ich nun in die Daten meinen Schadcode reinpacke und ich aus Experimenten auf meinen Rechner rausfinde wo genau der im Speicher landet und ich somit auch gezielt die Rücksprungadresse mit der Adresse des Schadcodes überschreiben kann, kann ich meinen Code ausführen. Wenn der bufferoverflow z.B. in einer Bilddarstellungs lib ist, die ein Browser verwendet, muss ich nur noch ein entsprechend manipuliertes Bild ins netz stellen und warten. Der Schadecode wird beim Anzeigen des Bildes ausgeführt. Das bringt einen schon zu schaudern. Und das nur weil Leute schlampig programmieren. Wenn doch nur sowas wie eine ordentlich programmierte Buffer-"Klasse" in der C Standard lib wäre, dann hätte man diese Probleme nicht.


Also das war auf jeden Fall der ausschlaggebende Fehler und es funktioniert jetzt auch soweit. Vielen Dank!
Bitte! :)


Jetzt kommt nur noch das kleine Problem mit dem telnet client von Windows. Hat da jemand eine Idee?[ignoranz-modus]Linux installieren? So hab ichs gmacht. ;)[/ignoranz-modus]

Gsus
29-06-2007, 07:04
Hey panzi,

das war mir gar nicht aufgefallen und ich habe in letzter Zeit routine hmmm... Gibt mir zu denken.

Ich wollte eigentlich nur sagen, dass das eine der besten Erklaerungen war die ich bisher zum Thema Bufferoverflow gesehen habe. Ich wollte einfachmal beweihreuchern ;)

Aber zum dem Thema wollte ich noch sagen das wenn man unter OpenBSD programmiert man vom gcc sehr gute warnungen zu Bufferoverflows bekommt (natuerlich muss man die Warnung auch dafuer anschalten). Ausserdem hat OpenBSD sehr schoene Standard libs. Aber okay das hat mich auch nicht davor geschuetzt in meinem ersten groessern Projekt jede menge Mist zu bauen :)

Bye

gsus

rxenon
29-06-2007, 11:28
@panzi: Ja vielen Dank für die ausführliche Erklärung. Ich kannte den Bufferoverflow Effekt zwar bereits, wusste aber nicht so genau Bescheid.

Mit dem Windows telnet muss ich jedoch schon klarkommen, da in der Schule zB nur Windows PCs sind. Ich selber habe natürlich auch Linux, keine Frage ;-)
Also lasse ich die Frage mal noch offen, vlt. sieht es ja jemand, der etwas ähnliches schonmal angegangen ist.

MfG rXenon

Gsus
29-06-2007, 13:23
wenn ich nochmal darf:

ich wuerde dir denn putty.exe client empfehlen zu verwenden. Ist nur ne exe kannst du downloaden und simple verwenden

http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html

Nicht viel aber fuer deine zwecke denke ich sollte er ausreichen und besonders gut da du ja an einem Schulpc bis kannst es nach der benutzung wieder loeschen!

rxenon
29-06-2007, 13:43
Ja, Putty kenn ich natürlich schon. Aber da die telnet.exe von Windows XP sogar ganz anders ist, als die von Win 2k etc. ist es wohl einfacher halt putty zu benutzen..
Na gut.

rxenon
29-06-2007, 15:38
Hallo!

Ich hätte noch eine andere Frage:
Gibt es eine Möglichkeit einem telnet client ein Zeichen zu senden, sodass sich dessen Zeiger eine Zeile weiter höher befindet? Oder kann man Zeichen beim Client löschen?

panzi
29-06-2007, 16:37
Eventuell hilft dir das: http://www.hamiltonlabs.com/UserGuide/30-AnsiEscapeSequences.htm
bzw das: http://en.wikipedia.org/wiki/ANSI_escape_code
Erfahrung hat mir gezeigt, dass das meiste davon auch die Windows Shell kann. Aja, und wenn du den Cursor einfach nur zum Zeilenanfang bewegen willst kannst du natürlich einfach "\r" (carriage return (http://en.wikipedia.org/wiki/Carriage_return)) ausgeben.