Forum: PC-Programmierung c++ sockets recv


von c++ (Gast)


Lesenswert?

Hallo,
ich möchte/muss unter C++ mit Sockets arbeiten. Das sieht jetzt so aus 
und funktioniert:
1
char echo_buffer[RCVBUFSIZE];
2
int recv_size;
3
4
recv_size = recv(sock, echo_buffer, RCVBUFSIZE,0);
5
    if(recv_size < 0)
6
    error_exit("Fehler bei recv()");
7
8
echo_buffer[recv_size] = '\0';
9
leer[recv_size] = '\0';

Bei recv gibt er mir alle epfangenen Daten seit dem letzten Mal. Wenn 
aber keine Daten kommen, wartet er, bis Daten kommen. Das darf nicht 
sein.
Mit
1
recv_size > 0 //Daten sind da
wird das auch nichts.

Meine Frage:
Wie kann ich gucken, ob überhaupt in diesem Moment neue Daten verfügbar 
sind, damit recv auf gar keinen Fall wartet?

von Karl H. (kbuchegg)


Lesenswert?

schau dir mal die Funktion 'select()' an

von Georg B. (diereinegier)


Lesenswert?

Auf Linux "man recv" tippen oder ein Buch lesen?

Man kann den Socket Non-Blocking machen (mit fcntl) oder (üblicher) mit 
select abfragen, ob Daten anliegen.

Ich würde zum Erlernen der Socket-Programmierung das Buch Unix Network 
Programming von Ruchard W. Stevens empfehlen. Es enthält sehr gut 
dokumentierte Beispiele in C.

von c++ (Gast)


Lesenswert?

Danke für die Antworten!!!

Select scheint schonmal das richtige zu sein. Bisher suche ich noch nach 
einer Möglichkeit select() zu benutzen.

Hat da jemand einen Gedankenanstoß?

von CC (Gast)


Lesenswert?

Die man-Page hat (bei mir) ein Beispiel drin. Muss natürlich angepasst 
werden, aber das schaffst du schon ;-)

von c++ (Gast)


Lesenswert?

[c]
select(sock_max+1, &sock, NULL, NULL, NULL);
[\c]

ock ist mein Socket. Aber was soll sock_max sein?

von c++ (Gast)


Lesenswert?

CC schrieb:
> Die man-Page hat (bei mir) ein Beispiel drin. Muss natürlich angepasst
> werden, aber das schaffst du schon ;-)

Ich guck' mal...

von CC (Gast)


Lesenswert?

Der socket mit dem höchsten Wert in rdfs.

von CC (Gast)


Lesenswert?

Ich seh gerade, &sock ist falsch. Muss der fd set sein, in dem die 
Sockets zum Lesen sind.

von c++ (Gast)


Lesenswert?

CC schrieb:
> Der socket mit dem höchsten Wert in rdfs.

In man steht leider bei mir nichts.

Und vor allem:
Was ist rdfs?

von CC (Gast)


Lesenswert?

http://linux.die.net/man/2/select
Nach unten scrollen. Oder eins der zig-tausend socket-Tutorials lesen 
(und verstehen)...

von c++ (Gast)


Lesenswert?

sock wird übrigens so erzeugt;

sock = socket( AF_INET, SOCK_STREAM, 0 );

von c++ (Gast)


Lesenswert?

Irgendwie hat das alles nichts mit meinem TCP zu tun.

Also mein Code bisher sieht so aus:
1
#include "Tcp.h"
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <string.h>
5
#include <errno.h>
6
7
#include <iostream>
8
#include <string.h>
9
#include <cstring>
10
11
#include <sys/types.h>
12
#include <sys/socket.h>
13
#include <netinet/in.h>
14
#include <netdb.h>
15
#include <arpa/inet.h>
16
#include <unistd.h>
17
18
#define RCVBUFSIZE 8192
19
20
static void eingeben();
21
static void error_exit(char *errorMessage);
22
static void send(char *text);
23
24
int _port = 13050;
25
26
struct sockaddr_in server;
27
struct hostent *host_info;
28
unsigned long addr;
29
30
int sock;
31
32
unsigned int _len;
33
34
35
int Tcp::begin( int argc, char *argv[])
36
{
37
    //printf("" + argc);
38
    
39
    if (argc < 2)
40
        error_exit("usage: client server-ip echo_word\n");
41
42
    sock = socket( AF_INET, SOCK_STREAM, 0 );
43
44
    if (sock < 0)
45
        error_exit( "Fehler beim Anlegen eines Sockets");
46
47
    
48
    if ((addr = inet_addr( argv[1])) != INADDR_NONE)
49
    {
50
        //argv[1] sollte eine numerische IP-Adresse sein
51
        memcpy( (char *)&server.sin_addr, &addr, sizeof(addr));
52
    }
53
    else
54
    {
55
        //Computernamen oder "localhost" in IP-Adressen umwandeln
56
        host_info = gethostbyname(argv[1]);
57
        if (NULL == host_info)
58
        {
59
            addr = inet_addr( "127.0.0.1");
60
            memcpy( (char *)&server.sin_addr, &addr, sizeof(addr));
61
        }
62
        else
63
        {
64
            //Server-IP-Adresse
65
            memcpy( (char *)&server.sin_addr,
66
                    host_info->h_addr, host_info->h_length );
67
        }
68
    }
69
70
    //Eine IPv4-Verbindung solls werden
71
    server.sin_family = AF_INET;
72
73
    //Setze Portnummer
74
    server.sin_port = htons(_port);
75
76
    //Baue die Verbindung zum Server auf
77
    if(connect(sock,(struct sockaddr*)&server,sizeof(server)) <0)
78
        error_exit("Kann keine Verbindung zum "
79
                   "Server herstellen");
80
    
81
    
82
    return 0;
83
}
84
85
string Tcp::_all = "";
86
87
/**
88
 *  TCP-Eingabe überprüfen und gegebenenfalls verarbeiten lassen.
89
**/
90
bool Tcp::eingeben()
91
{
92
    sleep(10);
93
    
94
    char echo_buffer[RCVBUFSIZE];
95
    char leer[RCVBUFSIZE];
96
    int recv_size;
97
98
    recv_size = recv(sock, echo_buffer, RCVBUFSIZE,0);
99
    
100
    if(recv_size < 0)
101
        error_exit("Fehler bei recv()");
102
103
    echo_buffer[recv_size] = '\0';
104
    leer[recv_size] = '\0';
105
106
    std::string test1 = std::string(echo_buffer);
107
108
    int ready=select(sock_max+1, &sock, NULL, NULL, NULL);   //!! Das Problem!!
109
    
110
    if(recv_size > 0)
111
    {
112
      _all+=test1;
113
        
114
        //return 0;
115
116
        printf(echo_buffer);
117
      printf("\n");
118
      printf("=================================\n");
119
        return 1;
120
    }
121
    
122
    cout << _all << endl;
123
    
124
    _all = "";
125
    
126
    printf("\n");
127
    printf("=================================\n");
128
    
129
    return 1;
130
}

von CC (Gast)


Lesenswert?

Der ist falsch. Wie es richtig geht steht in der man-Page, zu der ich 
Dir den Link gegeben habe. Passt ja schon allein mit den Datentypen 
nicht.

Ein Tipp: Im Link fragen sie Eingaben vom Keyboard ab (entspricht etwa 
einem Socket mit dem Wert 0).


Für den Rest: Tutorials sind dein Freund.

von CC (Gast)


Lesenswert?

(...oder auch Bücher, welche geeignet sind, weiß ich spontan nicht. Wird 
aber viele geben.)

von CC (Gast)


Lesenswert?

Ah, es macht auch wenig sinn, recv() VOR select() aufzurufen. Letzteres 
teilt nämlich mit, ob recv() blockieren wird oder nicht.

von c++ (Gast)


Lesenswert?

CC schrieb:
> Ah, es macht auch wenig sinn, recv() VOR select() aufzurufen.
> Letzteres
> teilt nämlich mit, ob recv() blockieren wird oder nicht.

Mir fehlt der erste Parameter von select() Das ist das große Problem.

von Dr. Sommer (Gast)


Lesenswert?

c++ schrieb:
> #include <stdio.h>
In C++ heißt diese Datei <cstdio>
> #include <stdlib.h>
und die <cstdlib>
> #include <string.h>
und die <cstring>
> #include <errno.h>
und die <cerrno>
> #include <string.h>
und die <cstring> - hoppla, die haben wir ja schon includet
> #include <cstring>
Uund gleich noch einmal includen.

c++ schrieb:
> #define RCVBUFSIZE 8192
In C++ muss man sich keiner Textersetzung bedienen, sondern kann echte 
Konstanten verwenden um die diversen Makro-Probleme zu vermeiden:
1
const size_t RCVBUFSIZE = 8192;

Wie wäre es mit Boost.Asio, das ist genau dafür gedacht was du machen 
willst:
http://www.boost.org/doc/libs/1_56_0/doc/html/boost_asio.html

von CC (Gast)


Lesenswert?

c++ schrieb:
> Mir fehlt der erste Parameter von select() Das ist das große Problem.

Bei dir:

sock+1

Aber das ist nicht (nur) "das" große Problem.

von Rolf M. (rmagnus)


Lesenswert?

Warum eigentlich überhaupt select()? Das ergibt doch hier überhaupt 
keinen Sinn. Das Haupt-Ziel von select() ist es, sich um mehrere fds 
gleichzeitig zu kümmern. Die gibt es hier aber gar nicht.
Es reicht, einfach den Socket non-blocking zu machen. Dann kehrt recv() 
immer ohne Wartezeit zurück, und wenn keine Daten verfügbar sind, 
liefert es -1 zurück, und errno wird auf den Wert EAGAIN gesetzt.

Dr. Sommer schrieb:
> In C++ muss man sich keiner Textersetzung bedienen, sondern kann echte
> Konstanten verwenden um die diversen Makro-Probleme zu vermeiden:
> const size_t RCVBUFSIZE = 8192;

Dann aber bitte auch nicht komplett groß scheiben, da das eigentlich das 
Erkennungszeichen für Makros ist.

c++ schrieb:
> Mir fehlt der erste Parameter von select() Das ist das große Problem.

Das große Problem ist eher, daß du noch gar nicht verstanden hast, was 
select() überhaupt tut.

von c++ (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Es reicht, einfach den Socket non-blocking zu machen. Dann kehrt recv()
> immer ohne Wartezeit zurück, und wenn keine Daten verfügbar sind,
> liefert es -1 zurück,

Genau das will ich!!! Hast da jemand einen Ansatz?

von Manuel (Gast)


Lesenswert?

struct pollfd fd;
int res;

fd.fd = s;
fd.events = POLLIN;
res = poll(&fd, 1, 1000); // 1000 ms timeout

if (res == 0)
{
    // timeout
}
else if (res == -1)
{
    // error
}
else
{
    // implies (fd.revents & POLLIN) != 0
    recv(s,buf,sizeof(buf),0); // we can read ...
}

von Rolf Magnus (Gast)


Lesenswert?

c++ schrieb:
> Rolf Magnus schrieb:
>> Es reicht, einfach den Socket non-blocking zu machen. Dann kehrt recv()
>> immer ohne Wartezeit zurück, und wenn keine Daten verfügbar sind,
>> liefert es -1 zurück,
>
> Genau das will ich!!! Hast da jemand einen Ansatz?

Ich hab sogar mehrere:

- man-Page lesen
- Ein Buch lesen
- Nach dem Thema googeln
- falls das Lesen von Doku untragbar ist: umsatteln auf Briefmarken 
sammeln

von c++ (Gast)


Lesenswert?

Rolf Magnus schrieb:
> c++ schrieb:
>> Rolf Magnus schrieb:
>>> Es reicht, einfach den Socket non-blocking zu machen. Dann kehrt recv()
>>> immer ohne Wartezeit zurück, und wenn keine Daten verfügbar sind,
>>> liefert es -1 zurück,
>>
>> Genau das will ich!!! Hast da jemand einen Ansatz?
>
> Ich hab sogar mehrere:
>
> - man-Page lesen
> - Ein Buch lesen
> - Nach dem Thema googeln
> - falls das Lesen von Doku untragbar ist: umsatteln auf Briefmarken
> sammeln

Danke! Ich hasse nur leider Sockets in C++ und brauche diese für EIN 
Projekt. Und dieser ganze FD-Mist entsprach nicht im Ansatz dem, was ich 
gebraucht habe. Nach langer Suche ohne FD hab ich nun
1
fcntl(sock, F_SETFL, O_NONBLOCK)
gefunden, was mein Problem löst.

Rolf Magnus schrieb:
> Dann aber bitte auch nicht komplett groß scheiben, da das eigentlich das
> Erkennungszeichen für Makros ist.

Stimmt. Der gepostete Code ist Dreck, das weiß ich selbst. Der Rest des 
Projektes sieht deutlich sauberer aus. Wir sind nämlich keine Anfänger. 
Da dieser Teil aber Softwaretechnisch unser größtes Problem darstellte, 
haben wir uns erst um eine Lösung bemüht und kümmern uns nun auch in 
dieser Klasse um einen sauberen Programmierstil.


Rolf Magnus schrieb:
> Ich hab sogar mehrere:
>
> - man-Page lesen
> - Ein Buch lesen
> - Nach dem Thema googeln
> - falls das Lesen von Doku untragbar ist: umsatteln auf Briefmarken
> sammeln

Ich lese oft Doku. Das hat mir in diesem Fall leider nicht besonders 
geholfen. Da C++ keine besonders neue Sprache ist (damit wurden schon 
uralte Großrechner programmiert, lange vor TCP) , ist der ganze TCP-Kram 
nicht so in der Sprache drin, wie in anderen Programmiersprachen.
In C# oder Java ist das schon einfacher. Da tut es "ein wenig" Doku 
lesen. So hardwareferne Sprachen können wir hier aber leider nicht 
gebrauchen.

==========

Vielen Dank an alle, die nach einer Lösung gesucht haben.

von Rolf Magnus (Gast)


Lesenswert?

c++ schrieb:
> Nach langer Suche ohne FD hab ich nunfcntl(sock, F_SETFL, O_NONBLOCK)
> gefunden, was mein Problem löst.

Um ehrlich zu sein wundert mich das. Wenn man bei google nach 
"non-blocking socket" sucht, bekommt man seitenweise Ergebnisse, wo das 
drin steht.

c++ schrieb:
> Da C++ keine besonders neue Sprache ist (damit wurden schon
> uralte Großrechner programmiert, lange vor TCP) , ist der ganze TCP-Kram
> nicht so in der Sprache drin, wie in anderen Programmiersprachen.

Bei boost gibt's ein socket-API für C++. Hab ich aber noch nicht 
ausprobiert.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

c++ schrieb:
> Da C++ keine besonders neue Sprache ist (damit wurden schon uralte
> Großrechner programmiert, lange vor TCP)

hust

C++ gibt es effektiv* erst seit 1985, TCP ist gut zehn Jahre älter ...

Sprachbestandteil ist "der ganze TCP-Kram" aus ganz anderen Gründen 
nicht.

*) Den Namen gibt es seit '83, '85 erschien die erste Ausgabe des Buchs 
von Stroustrup

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.