# UDP Socket definieren



## DanielKobzan (17. Februar 2002)

Hab gerade unter V. C++ Das Netzwerkprojekt von Visual C++ 6 in 21 Tagen  nachgebaut und möchte jetzt das Protokoll TCP in UDP ändern, aber hab nicht rausbekommen wie. Habs erst mit m_sConnectSocket.Create(0,Sock_DGRAM); versucht, aber es funktioniert nicht.


----------



## Xeragon (17. Februar 2002)

Was für ein Fehlercode (GetLastError()) tritt auf?


----------



## DanielKobzan (17. Februar 2002)

Komischerweise, tritt gar kein Fehlercode auf. Kompilieren funktioniert ohne Probleme, nur wenn ich verbinden will, tut er das nicht. Hab beide Seiten auf UDP definiert, den Server und den Client.

.
.
.
// Client, einen Standard-Socket erzeugen
m_sConnectSocket.Create(0,SOCK_DGRAM);
// Verbindung zum Server öffnen
m_sConnectSocket.Connect(m_strName, m_iPort);
}
else
{
// Server, einen Socket für den angegebenen Anschluß erzeugen
m_sListenSocket.Create(m_iPort,SOCK_DGRAM);
// Auf Verbindungsgesuche hören
m_sListenSocket.Listen();
.
.
.

In der MSN steht folgendes drin:
--
BOOL Create( UINT nSocketPort = 0, int nSocketType = SOCK_STREAM, long lEvent = FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE, LPCTSTR lpszSocketAddress = NULL );

nSocketType
SOCK_STREAM <--TCP
or 
SOCK_DGRAM  <--UDP
--
was ich jetzt mal auf
m_sConnectSocket.Create und
m_sListenSocket.Create
bezogen hab.


----------



## Xeragon (18. Februar 2002)

Ich meinte Runtime-Fehlercodes... wenn's nicht funktioniert muss immer irgendwo ein Fehlercode auftreten. Die meisten WinAPI-Funktionen geben Erfolg/Misserfolg zurück und per GetLastError() lässt sich der genaue Fehler finden.
Bau mal bei jeder Funktion Fehlerchecks ein.


----------



## DanielKobzan (18. Februar 2002)

Sorry, aber ich hab absolut keine Ahnung, wie ich Fehlercodes einbauen kann. In der KLasse CMySocket steht zB sowas drinnen:

---
void CMySocket::OnConnect(int nErrorCode)
{
// Sind Fehler aufgetreten?
	if (nErrorCode == 0)
// Nein, OnAccept-Funktion des Dialogfelds aufrufen
		((CSockDlg*)m_pWnd)->OnConnect();

}
---
Jetzt muß es doch auch möglich sein den Wert von nErrorCode auszugeben.
Und bitte schreib ein Codebeispiel, ansonsten kapier ichs net;((. Hab doch gerade erst angefangen.:% 

Und dann hab ich noch ne andere Frage, wie ist es möglich dass ein Programm Startparameter, wie "..\test.exe Test" oder "..\test.exe p:Test" oder "..\test.exe /Test" ausliehst. Hab folgendes gefunden, aber irgendwie klappt es nicht, vielleicht weil es für den Borland C++ Compiler ist?

---
Ganz einfach mit der Abfrage
if (LowerCase(ParamStr(1))=="about")
beliebig im Programm.

ParamStr(1) liefert uns den ersten Parameter.
ParamStr(0) liefert uns den Namen des Programms.
Mit ParamStr(2) erhalten wir bei
"C:\Programme\MeinProgramm\MeinProgramm.exe about,fake"
den Parameter "fake".
---


----------



## Xeragon (18. Februar 2002)

Paramete: CWinApp hat die Member-Variable "m_lpCmdLine", das ist die Kommando-Zeile mit der das Programm aufgerufen wurde (gleich wie der entsprechende WinMain()-Parameter)

Was ich wg. Error-Codes meinte (Beispiel):


```
if(!m_sConnectSocket.Create(/*...*/))
{
    // Fehler ist aufgetreten
    int fehlercode = m_sConnectSocket.GetLastError()

    // ... Message-Box mit Fehler ausgeben ...
    // (Mit "FormatMessage()" kannst du aus dem Code auch eine schöne
    // Fehlermeldung machen).
}
```

Alternativ kannst du auch mal im Debugger schaun, ob die vielleicht so was findest...
(in meinem Tutorial is ne kurze Einführung in das Grundlegenste mit dem Debugger)


----------



## DanielKobzan (19. Februar 2002)

So, jetzt hab ich folgendes eingebaut:

---
void CMySocket::OnConnect(int nErrorCode)
{
// Sind Fehler aufgetreten?
	if (nErrorCode == 0)
// Nein, OnAccept-Funktion des Dialogfelds aufrufen
		((CSockDlg*)m_pWnd)->OnConnect();

int fehlercode = CMySocket::GetLastError();
LPVOID lpMsgBuf;
	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |   
      FORMAT_MESSAGE_FROM_SYSTEM | 
      FORMAT_MESSAGE_IGNORE_INSERTS,NULL,fehlercode,MAKELANGID
      (LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0,NULL);
	MessageBox( NULL, (LPCTSTR)lpMsgBuf, "ErrorConnect", MB_OK | 
      MB_ICONINFORMATION );
LocalFree( lpMsgBuf );
}
---

Nur, es wird absolut nichts ausgegebn, nur eine kurze Meldung von OnSend, dass es keine Probleme gab. Hab ich die Fehlerroutine richtig eingegeben oder muß sie wo anders hin?


----------



## Xeragon (19. Februar 2002)

du sollst nicht den Parameter überprüfen, sondern die Rückgabewerte der Funktionen, die du aufrufst (z.b. Create())


----------



## murdi (20. Februar 2002)

@DanielKobzan

genau da ist das probelm an diesem buch , es wird zwar alles gezeigt , vom kleinen süssen malproggi bis hin zu einem chatsystem oder ähnlichen aber verstehen tut es jemand der keine ahnung hat nich, da es is diesem buch einfach lausig erklärt ist !

ps.: wenn ich da sma so anmerken darf  , is in keinster weise böse oder so gemein , einfach nur nen scheiss buch !


----------



## DanielKobzan (20. Februar 2002)

Dass es ein ****** Buch ist hab ich schon bemerkt, aber es war das einzige "Tutorial" im Web, dass ich gefunden habe, dass den Aufbau eines Socketprogrammes, also Server / Client, Schritt für Schritt beschreibt. Wie das funktioniert, dass schaff ich schon weitgehend aus den Schritten rauszulesen. In der Hinsicht erfüllt es genau, zumindest dieses Kapitel, meine Wünsche, nur dass ich UDP brauch. Ich will nähmlich ein Connecttool für CounterStrike schreiben, was prüft ob mein Stammserver voll ist und wenn nicht connectet und wenn doch voll so lange abfragt, bis er leer ist. Es hat mich nämlich immer gestört retry in die console einzugeben. Und Rcon auf HL Servern benötigt nunmal UDP. Ich hab sowas schon in VB geschrieben und will es jetzt als Einstiegsprojekt für C++ verwenden. Dazu brauch ich:

- Eine Clientlösung (mit UDP)
- Startparameter auslesen (wills net nur für einen Server verwenden)
- substr(); (Rückgabe vom Server muß ausgearbeitet werden)

Und von diesen Punkten fehlt mir eigentlich nur noch das UDP. Vielleicht weiß ja einer ein gutes Tutorial über Server / Client, dass auch UDP erwähnt. Ansonsten werd ich mich heute noch mal hinsetzen, den Debugger verwenden, is fast das selbe Prinzip wie in VB, und versuchen den Fehlercode einzubauen. So, ich glaub das war ein erster Einblick, von dem was ich machen will und auch werde, weil das nevt nämlich schon ganz schön mit den retrys.


----------



## DanielKobzan (23. Februar 2002)

*Variable in Winapp an Textfeld*

Ich hab nochmal ne Frage und zwar, wie übergeb ich einen Wert, hier von m_lpCmdLine, aus Klasse Winapp an ein Eingabefeld in einer anderen Klasse?

Nachtrag, geht es zB mit

AfxMessageBox(AfxGetApp()->m_lpCmdLine);

?


----------



## Xeragon (23. Februar 2002)

IIRC sind die Afx*()-Funktionen von überall aufrufbar (sprich "normale" Funktion).
Also müsstest du prinzipiell in der "anderen Klasse" auch AfxGetApp() aufrufen können.


----------



## DanielKobzan (25. Februar 2002)

Also, gut, ich hab es jetzt geschafft die Serverdaten aus m_lpCmdLine auszulesen und hab die weichtigsten Funktionen aus dem Netzwerktutorial eingebaut. Eine Verbindung mit TCP klappt ohne Probleme nur schaff ich es immer noch nicht eine UDP Verbindung zu machen. Bei TCP kommt bei nicht erfolgter Verbindung, wenigstens eine Meldung, bei UDP gar nichts. Auch GetLastError brachte kein Ergebnis und der Debugger ist einfach weitergelaufen. Weiß wirklich keiner genau woran das liegt? Es würde ja wenigstens ein Programmbeispiel für UDP langen.


----------



## DanielKobzan (26. Februar 2002)

So, ich hab es jetzt geschafft mit nem Server Verbindung aufzunehmen,m es lag einfach daran, dass man sich mit UDP Server nicht verbinden kann, sonder einfach nur Daten übertragen oder empfangen. So, jetzt hab ich aber ein anderes Problem und zwar wird nicht die vollen empfangene Nachricht angezeigt, als Code verwende ich:

char *pBuf = new char[10250];
int iBufSize = 10240;
.
.
.
// Nachricht empfangen
iRcvd = m_ConnectSocket.Receive(pBuf, iBufSize);
// Ende der Nachricht abschneiden
pBuf[iRcvd] = NULL;
// Nachricht in einen CString kopieren
strRecvd = pBuf;
Message = strRecvd;

pBuf und iBufSize sind großzügig definiert, es wird aber von 

"D[HoC]Overlord*tm*¡?áCHoolY³0"CaL_termAn?R‰DTequilauÝ4DSchmarkiúˆjDtaugenichts
råˆD[PDS][vF]Joka[ANC]õÝCbin_einkaufen[?C	siQz.rebornšÉ1D
TRULLAj&
[FA]NightmareÛDaL_cApone'ÛC
RaMrOt.ÑCaL_lStaRýè¹C"

immer nur 

"D[HoC]Overlord*tm*" 

angezeigt, hat das vielleicht was mit dem Ascizeichen zu tun und wie kann ich es umgehen. Wenn ich mich noch an VB erinner, dann dürfte es sich bei dem Zeichen , also nach "D[HoC]Overlord*tm*", um ein Zeichen mit dem Ascicode 0 - 5 handeln.


----------



## Xeragon (26. Februar 2002)

Kann es sein, dass das Zeichen nach *tm* eine 0 ist?
Wenn du binäre Daten überträgst, darfst du natürlich nicht davon ausgehen, dass die erste 0 die du findest das String-Ende ist (was CString logischerweise tut, da es eine String-Klasse ist).


----------



## DanielKobzan (26. Februar 2002)

ich glaub scho, dass das ne 0 is, nur was kann ich da machen, wie kann ich des zb in ner MessageBox ganz anzeigen.


----------



## Xeragon (26. Februar 2002)

Du könntest die 0en durch andere Zeichen ersetzen. Die Daten entsprechen dann natürlich nicht mehr dem Original.


----------



## DanielKobzan (26. Februar 2002)

Das ist nicht ganz einfach. Die Daten kommen so vom Server und in pBuf, kann man im Debugger sehen, wird es genauso abgeschnitten. Die Nachricht kommt gar nicht vollständig zum string, da sie vorher schon da abgeschnitten wird.

C++ is scho kompliziert, in VB kam die Nachricht so wie sie is in die Variable.


----------



## Xeragon (26. Februar 2002)

VB verwendet eine String-Representation, die vor dem String die Länge speichert, C-Derivate verwenden 0-terminierte Strings.
Wg. Debugger: Achtung! Du SIEHST den String auch hier schon abgehakt, aber er ist es nicht. Der Debugger zeigt interperpreiter das ganze auch wieder als String und zeigt es auch nur bis zur ersten 0 an!


----------



## DanielKobzan (26. Februar 2002)

Und wie kann ich das jetzt durch ne andere Zahl ersetzen?

Gibt es in C++ auch etwas wie den Befehl in VB chr(...)? Damit hab ich in VB die Daten gefiltert.


----------



## Xeragon (26. Februar 2002)

Die meisten String-Funktionen werden vermutlich bei der ersten 0 aufhören. Loop einfach durch pBuf und ersetz alle 0 durch z.b. '0'. Du selbst weißt ja anhand von iBufSize, wie lange der String ist.


----------



## DanielKobzan (26. Februar 2002)

Hast du ein Codebeispiel, ich blick nämlich nicht durch. Loop durch pBuf ersetzen, nicht andersrum? Loop hab ich nämlich nicht, sondern nur pBuf


----------



## DanielKobzan (26. Februar 2002)

Hab was neues festgestellt, das sechste Zeichen von zb 
"ÿÿÿÿDLeGoLaS" ist im Ascicode die Anzahl der Spieler am Server, also wenn der Ascicode 5 ist dann sind 5 Leute am Server, jetzt muß ich das nur auslesen, in VB geht das so:

Private Sub Command1_Click()
Text2.Text = ""
If Text1.Text <> "" Then
For i = 1 To Len(Text1.Text)
For j = 0 To 255
If Mid(Text1.Text, i, 1) = Chr(j) Then Text2.Text = Text2.Text & j & " "
Next j
Next i
End If
End Sub

also durch ausprobieren mit chr(), nur gibts es was wie chr() in C++?


----------



## Xeragon (26. Februar 2002)

mit "loopen" mein ich: eine schleife schreiben, die das ganze array durchläuft und 0 durch irgendwas ersetzt.


```
for(char* pch = pBuf; (pch+pBuf) < iBufSize; ++pch)
{
if(*pch == 0) // == '\0'
   *pch = '0';
}
```

Da du es aber hier wie gesagt mit "nicht-text-daten" zu tun hast, rate ich dir das format zu analysieren und z.b. ein struct zu schreiben, dass eine person repräsentiert und dann darin die daten entsprechend zu speichern.


----------



## DanielKobzan (26. Februar 2002)

Danke, aber ich habs anders gelöst. Da die Speieranzahl, wie gesagt an der 6. Stelle steht hab ich den Code nun so aufgebaut:


//Spieleranzahl ermitteln
int j = 0;
string a;
while (j != 255)
{
a = char(j);
if (Message.substr(5,1) == a.c_str())
	Anzahl = j;
j++;
}

Danke trotzdem. Aber keine Angst, ich hab bestimmt mind. noch ein Problem


----------



## DanielKobzan (27. Februar 2002)

So, jetzt hab ich doch nochmal ein problem, wie schaff ich es, dass ein programm ohne Murren beendet wird, also Befehl un dProgramm ist weg. habs mit DestroyWindow(); probiert, da gibt er abe rne Fehlermeldung und stürzt ab.


----------



## Xeragon (27. Februar 2002)

Hmm, send einfach eine WM_QUIT Message?
(könnte allerdings bei Doc/View usw. etwas komplizierter sein, hab schon lang nicht mehr MFC programmiert *g*)


----------



## Xeragon (27. Februar 2002)

*Update...*

DestroyWindow() scheint schon zu stimmen. Stell sicher, dass du deine Resourcen alle korrekt freigibst.
Welche Fehlermeldung kommt eigentlich?


----------



## DanielKobzan (27. Februar 2002)

Irgendwas Debug assertion failed und dann gibt er dieses Codestück an

DWORD CWnd::GetStyle() const
{
	ASSERT:IsWindow(m_hWnd));

	if (m_pCtrlSite == NULL)
		return (DWORD)GetWindowLong(m_hWnd, GWL_STYLE);
	else
		return m_pCtrlSite->GetStyle();
}

Auch macht er wenn er Destroy.. gelesen hat, im Debugger, munter weiter.

Und noch ne andere Frage, was muß ich machen, damit ich das Programm veröffentlichen kann. Bei meinem Freund meckert das Programm, dass ein paar Daten fehlen.

msvcrtd.dll
mfc42d.dll
mfco42d.dll
mfcn42d.dll
msvcp60d.dll


----------



## Xeragon (27. Februar 2002)

Diese DLLs musst du mitveröffentlichen.

Die assertion sagt im Prinzip aus, dass GetStyle() aufgerufen wurde, nachdem das Fenster schon zertört war, check deinen Code nochmal auf Fehler.


----------



## DanielKobzan (27. Februar 2002)

Das is mir auch schon klar gewesen, aber das ist ja kein Wunder, wenn das Programm einfach munter weitermacht und dann ein Textfeld aufrufen will, das weg ist.


----------



## Xeragon (27. Februar 2002)

Geh mal beim Beenden im Einzelschritt-Modus durch den Debugger, irgendwo wird (hoffentlich) etwas auffallen.


----------



## DanielKobzan (27. Februar 2002)

Er kommt bis return TRUE; von OnInitDialog() danach verlier ich die Spur.


----------



## Xeragon (27. Februar 2002)

Hmm, warum wird OnInitDialog() beim Beenden eigentlich aufgerufen?


----------



## DanielKobzan (27. Februar 2002)

Es wird nicht aufgerufen in der Funktion steht nur DestroyWindow() drinnen, das hat aber keine Wirkung, es läuft einfach weiter. Oder wird bei DestroyWindow() eine andere Funktion aufgerufen?


----------



## Xeragon (27. Februar 2002)

DestroyWindow() ist eine andere Funktion und ruft ziemlich sicher andere Funktionen auf.

Allerdings: Warum rufst du in OnInitDialog() DestroyWindow() auf? OnInitDialog() wird kurz vor dem anzeigen der Dialog-Box aufgerufen, warum willst du sie zerstören bevor sie überhaupt (visuell) existiert?


----------



## DanielKobzan (27. Februar 2002)

Mein Programm liest am Anfang die Kommandozeile aus, liest die HL-Serverdaten aus, checkt wie vile Leute am Server sind und startet HL nach bedarf und das soll alles passieren, wenn das Program startet. So, jetzt hab ich aber ne Prüffunktion eingebaut, die untersucht, ob die Kommandozeile richtig eingegeben ist, wenn sie das nicht ist, gibt es einen Hinweis und das Programm soll sich beenden. Wende willst, schicke/poste ich mal den ganzen Quellcode (aber net hauen, meine Quellcodes sehen immer schrecklich aus). Wie heißt die Funktion, die beim Beenden aufgerufen wird?


----------



## Xeragon (27. Februar 2002)

hmm, post bitte mal das programm, heut kann ich's mir aber leider nicht mehr anschaun


----------



## DanielKobzan (27. Februar 2002)

OK, ich hab ne Funktion gefunden, die beim Beenden aufgerufen wird, ab erich glaub nicht, dass die das Programm beendet, ich denke mit der ist es einfach möglich noch was auszuführen.

void CCSConnectorDlg::OnDestroy() 
{
	CDialog::OnDestroy();

	// TODO: Add your message handler code here

}


----------



## Xeragon (27. Februar 2002)

Die wird u.a. von Windows (indirekt) als Reaktion auf DestroyWindow() aufgerufen.


----------



## DanielKobzan (27. Februar 2002)

Kein Problem, muß eh in mein Bettchen. Aber Danke, das du dir überhaupt die Mühe machst, aber wie gesagt, es gibt sicher hundert Sachen die man besser machen könnte. Project ist in der Anhängung.

Post bezieht sich auf deine Vorherige Meldung


----------



## Xeragon (1. März 2002)

Nach kurzer Wieder-Einarbeitungszeit in die MFC: Benutze statt DestroyWindow() EndDialog(IDCANCEL) um den Dialog sauber zu schließen.


----------



## DanielKobzan (1. März 2002)

Jo Danke, jetzt läuft das Wunderbar, zumindest für net Beta. Jetzt muß ich nur noch das Problem mit den DLLs lösen und es ist perfekt(alles ist relativ). Is schon dämlöich, für ein Programm, dass 120 KB groß ist, braucht man Dlls, die 2 MB groß sind.


----------

