TCP/IP na przykładzie .NET
Pierwszy podstawowy program
Uruchamiamy Visual Studio
Zakładamy nowy projekt (ctrl+shift+n)
Pierwszy podstawowy program
Z zakładki „tools” wybieramy odpowiednie kontrolki i
układamy na formatce
Pierwszy podstawowy program
We właściwościach zmieniamy następujące parametry
•Minimum size: 300;300
•Name: textBoxAdres
•Name: textBoxPort
•Name: buttonPolacz
•Name: buttonSerwuj
•Name: richTextBoxOdbior
•Name: textBoxKomunikat
•Name: buttonWyslij
•Anchor: Top, Left, Right
•Anchor: Top
•Anchor: Top, Bottom, Left, Right
•Anchor: Bottom, Left, Right
Pierwszy podstawowy program
Używamy dodatkowej przestrzeni nazw:
using
System.Net.Sockets;
using
System.Net;
W klasie Form1 dodajemy zmienne
private
TcpListener tcpLsn;
private
Encoding ASCII = Encoding.ASCII;
//kodowanie
Socket s;
Pierwszy podstawowy program
Dodajemy obsługę guzika „Serwuj”
private void
buttonSerwuj_Click
(object
sender
,
EventArgs
e)
{
tcpLsn =
new
TcpListener(IPAddress.Parse(textBoxAdres.Text),
int.
Parse(textBoxPort.Text));
//zainicjiuj listenera na podanym
porcie i adresie
tcpLsn.Start();
Socket
sckt = tcpLsn.AcceptSocket();
//funkcja blokująca do czasu
nadejścia połączenia
Byte
[] odebraneBajty =
new
Byte
[100];
int
ret = sckt.Receive(odebraneBajty, odebraneBajty.Length, 0);
string
tmp =
null
;
tmp = System.Text.
Encoding
.ASCII.GetString(odebraneBajty);
if
(tmp.Length > 0)
{
richTextBoxOdbior.AppendText(tmp);
}
tcpLsn.Stop();
}
Pierwszy podstawowy program
Dodajemy obsługę guzika „Połącz” oraz „Wyślij”
private void
buttonPolacz_Click(
object
sender,
EventArgs e
)
{
s =
new
Socket
(
AddressFamily
.InterNetwork,
SocketType
.Stream,
ProtocolType
.Tcp);
IPAddress
hostadd
= IPAddress
.Parse(textBoxAdres.Text);
int
port =
Int32
.Parse(textBoxPort.Text);
IPEndPoint
EPhost =
new
IPEndPoint
(hostadd, port);
s.Connect(EPhost);
}
private void
buttonWyslij_Click(
object
sender,
EventArgs e
)
{
string
str = textBoxKomunikat.Text;
Byte
[] byteData = ASCII.GetBytes(str.ToCharArray());
s.Send(byteData, byteData.Length, 0);
}
Pierwszy podstawowy program
Pierwsza działająca wersja ale ma poważne wady:
Usługa zadziała tylko raz (przydałaby się pętla)
Jest kilka blokujących funkcji przez co tracimy kontrolę
w głównej formatce (przydałby się osobny wątek)
Brak obsługi błędów
Brak możliwości wielokrotnego podłączania się i
odłączania
Brak uodpornienia interfejsu na głupie zachowania
2 wersja programu część powyższych ma wyeliminowane
Drugi program
We właściwościach zmieniamy dodatkowo następujące
parametry:
•Text: 127.0.01
•Text: 2222
•ReadOnly: true
•Enabled: false
Drugi program
public delegate void
DodajKolorowyTekst
(
RichTextBox
RichTextBox,
string
Text,
Color
kolor);
private void
DodajKolorowyTekstFn(
RichTextBox
rtb,
string
tekst,
Color
kolor)
{
var
StartIndex = rtb.TextLength;
rtb.AppendText(tekst);
var
EndIndex = rtb.TextLength;
rtb.Select(StartIndex, EndIndex - StartIndex);
rtb.SelectionColor = kolor;
}
private void
AppendColoredText(
RichTextBox
RTB,
string
Text,
Color
kolor)
{
if
(RTB.InvokeRequired)
{
RTB.Invoke(
new
DodajKolorowyTekst
(DodajKolorowyTekstFn), RTB, Text, kolor);
}
else
{
DodajKolorowyTekstFn(RTB, Text, kolor);
}
}
private
Encoding
ASCII =
Encoding
.ASCII;
//kodowanie
private
TcpListener
tcpLsn;
Socket
s;
Thread
tcpThd;
Drugi program
private void
buttonSerwuj_Click(
object
sender,
EventArgs
e)
{
if
(buttonSerwuj.Text == "Stop")
{
try
{
tcpThd.Abort();
tcpLsn.Stop();
buttonSerwuj.Text =
"Serwuj"
;
buttonPolacz.Enabled =
true
;
textBoxAdres.Enabled =
true
;
textBoxPort.Enabled =
true
;
}
catch
(
Exception
ex)
{
AppendColoredText(richTextBoxOdbior, "
Bład odłączenia: \n
",
Color
.Red);
AppendColoredText(richTextBoxOdbior, ex.Message +
"\n",
Color
.Red);
}
}
Drugi program
else
{
try
{
//zainicjiuj listenera na podanym porcie i adresie
tcpLsn =
new
TcpListener
(
IPAddress
.Parse(textBoxAdres.Text),
int.
Parse(textBoxPort.Text));
tcpLsn.Start();
AppendColoredText(richTextBoxOdbior, "
Słucham na:
",
Color
.Red);
AppendColoredText(richTextBoxOdbior, tcpLsn.LocalEndpoint.ToString() + "
\n
",
Color
.Blue);
//przygotowanie wątku czekającego na połączenia
tcpThd =
new
Thread
(
new
ThreadStart
(WaitingForClient));
tcpThd.Start();
//wystartowanie wątku czekającego na połączenia
buttonSerwuj.Text =
"Stop";
//zmiana nazwy
buttonPolacz.Enabled =
false
;
//zmiany stanów
textBoxAdres.Enabled =
false
;
textBoxPort.Enabled =
false
;
}
catch
(
Exception
ex)
{
AppendColoredText(richTextBoxOdbior,
"Bład połączenia: \n"
,
Color
.Red);
AppendColoredText(richTextBoxOdbior, ex.Message +
"\n"
,
Color
.Red);
}
}
}
Drugi program
public void
WaitingForClient()
{
while
(
true
)
{
try
{
Socket
sckt = tcpLsn.AcceptSocket();
Byte
[] odebraneBajty =
new
Byte
[textBoxKomunikat.MaxLength];
AppendColoredText(richTextBoxOdbior,
"Podłączony klient z: "
,
Color
.Red);
AppendColoredText(richTextBoxOdbior, sckt.RemoteEndPoint.ToString() +
"\n"
,
Color
.Blue);
while
(sckt.Connected)
{
try
{
int
ret = sckt.Receive(odebraneBajty, odebraneBajty.Length, 0);
if
(ret > 0)
{
string
tmp =
null
;
odebraneBajty[ret] = 0;
tmp = System.Text.
Encoding
.ASCII.GetString(odebraneBajty);
if (
tmp.Length > 0)
{
AppendColoredText(richTextBoxOdbior, tmp,
Color
.Black);
AppendColoredText(richTextBoxOdbior,
"\n"
,
Color
.Black);
}
}
Drugi program
else
{
AppendColoredText(richTextBoxOdbior,
"Błąd odbioru\n"
,
Color
.Red);
break
;
}
}
catch
(
Exception
ex)
{
if
(!sckt.Connected)
{
AppendColoredText(richTextBoxOdbior,
"Połaczenie zerwane\n"
,
Color
.Red);
AppendColoredText(richTextBoxOdbior, ex.Message +
"\n"
,
Color
.Red);
break
;
}
}
}
}
catch
(
Exception
ex)
{
MessageBox
.Show("Koniec: " + ex.Message);
}
}
}
Drugi program
private void
buttonPolacz_Click(
object
sender,
EventArgs
e)
{
if
(buttonPolacz.Text ==
"Odłącz"
)
{
try
{
s.Disconnect(
false
);
textBoxKomunikat.Enabled =
false
;
buttonWyslij.Enabled =
false
;
buttonSerwuj.Enabled =
true
;
textBoxAdres.Enabled =
true
;
textBoxPort.Enabled =
true
;
buttonPolacz.Text =
"Połacz"
;
AppendColoredText(richTextBoxOdbior,
"Odłączono \n"
,
Color
.Red);
}
catch
(
Exception
ex)
{
AppendColoredText(richTextBoxOdbior,
"Błąd odłaczenia: \n"
,
Color
.Red);
AppendColoredText(richTextBoxOdbior, ex.Message +
"\n"
,
Color
.Red);
}
}
Drugi program
else
{
try
{
s =
new
Socket
(
AddressFamily
.InterNetwork,
SocketType
.Stream,
ProtocolType
.Tcp);
IPAddress
hostadd =
IPAddress
.Parse(textBoxAdres.Text);
int
port =
Int32
.Parse(textBoxPort.Text);
IPEndPoint
EPhost =
new
IPEndPoint
(hostadd, port);
s.Connect(EPhost);
if (
s.Connected)
{
textBoxKomunikat.Enabled =
true
;
buttonWyslij.Enabled =
true
;
buttonSerwuj.Enabled =
false
;
textBoxAdres.Enabled =
false
;
textBoxPort.Enabled =
false
;
buttonPolacz.Text =
"Odłącz"
;
AppendColoredText(richTextBoxOdbior,
"Podłączono \n"
,
Color
.Red);
}
}
catch
(
Exception
ex)
{
AppendColoredText(richTextBoxOdbior,
"Błąd podłaczenia: \n"
,
Color
.Red);
AppendColoredText(richTextBoxOdbior, ex.Message +
"\n"
,
Color
.Red);
}
}
}
Drugi program
private void
buttonWyslij_Click(
object
sender,
EventArgs
e)
{
if
(s != null && s.Connected)
//jeżeli jesteśmy podłączeni to wysyłamy
{
string
str = textBoxKomunikat.Text;
Byte
[] byteData = ASCII.GetBytes(str.ToCharArray());
s.Send(byteData, byteData.Length, 0);
}
}
private void
Form1_FormClosed(
object
sender,
FormClosedEventArgs e
)
{
if
(tcpThd !=
null
)
tcpThd.Abort();
if
(tcpLsn !=
null
)
tcpLsn.Stop();
}
Trzecia wersja programu
Wprowadzono następujące modyfikacje:
Obsługa wielu klientów jednocześnie (serwer
wielowątkowy)
Wydzielenie obsługi połączeń do osobnej klasy
Dzięki serializacji i deserializacji przekazywanie całych
obiektów
Informowanie za pomocą zdarzeń.