C# Language
Asynchrone aansluiting
Zoeken…
Invoering
Door asynchrone sockets te gebruiken, kan een server luisteren naar inkomende verbindingen en in de tussentijd een andere logica uitvoeren, in tegenstelling tot synchrone socket wanneer ze luisteren, blokkeren ze de hoofdthread en reageert de toepassing niet meer en loopt een test vast totdat een client verbinding maakt.
Opmerkingen
Socket en netwerk
Hoe toegang te krijgen tot een server buiten mijn eigen netwerk? Dit is een veel voorkomende vraag en wanneer deze wordt gesteld, wordt deze meestal als onderwerp gemarkeerd.
Serverzijde
Op het netwerk van uw server moet u uw router doorsturen naar uw server.
Voor bijvoorbeeld een pc waarop de server actief is:
lokaal IP = 192.168.1.115
Server luistert naar poort 1234.
Stuur binnenkomende verbindingen op Port 1234
router door naar 192.168.1.115
Kant van de cliënt
Het enige dat u hoeft te veranderen is het IP. U wilt geen verbinding maken met uw loopback-adres maar met het openbare IP-adres van het netwerk waarop uw server actief is. Dit IP kunt u hier krijgen.
_connectingSocket.Connect(new IPEndPoint(IPAddress.Parse("10.10.10.10"), 1234));
Dus nu maakt u een aanvraag voor dit eindpunt: 10.10.10.10:1234
als u uw poort doorstuurt naar uw router, zullen uw server en client probleemloos verbinding maken.
Als je verbinding wilt maken met een lokaal IP-adres, hoef je niet te portforwart, verander het loopback-adres in 192.168.1.178
of iets dergelijks.
Gegevens verzenden:
Gegevens worden in byte-array verzonden. U moet uw gegevens in een byte-array verpakken en aan de andere kant uitpakken.
Als u bekend bent met socket, kunt u ook proberen uw byte-array te coderen voordat u deze verzendt. Dit voorkomt dat iemand uw pakket steelt.
Voorbeeld van asynchrone socket (client / server).
Server Side voorbeeld
Maak luisteraar voor server
Begin met het maken van een server die clients behandelt die verbinding maken en aanvragen die worden verzonden. Dus maak een luisteraar klasse die dit aankan.
class Listener
{
public Socket ListenerSocket; //This is the socket that will listen to any incoming connections
public short Port = 1234; // on this port we will listen
public Listener()
{
ListenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
}
Eerst moeten we de luisteraar-socket initialiseren waar we kunnen luisteren naar eventuele verbindingen. We gaan een Tcp-socket gebruiken, daarom gebruiken we SocketType.Stream. We specificeren ook naar welke poort de server moet luisteren
Daarna beginnen we te luisteren naar inkomende verbindingen.
De boommethoden die we hier gebruiken zijn:
Deze methode bindt de socket aan een IPEndPoint . Deze klasse bevat de host- en lokale of externe poortinformatie die een toepassing nodig heeft om verbinding te maken met een service op een host.
De parameter achterstand geeft het aantal binnenkomende verbindingen aan dat in de wachtrij kan worden geplaatst voor acceptatie.
ListenerSocket.BeginAccept ();
De server begint te luisteren naar inkomende verbindingen en gaat door met andere logica. Wanneer er een verbinding is, schakelt de server terug naar deze methode en voert de methode AcceptCallBack uit
public void StartListening()
{
try
{
MessageBox.Show($"Listening started port:{Port} protocol type: {ProtocolType.Tcp}");
ListenerSocket.Bind(new IPEndPoint(IPAddress.Any, Port));
ListenerSocket.Listen(10);
ListenerSocket.BeginAccept(AcceptCallback, ListenerSocket);
}
catch(Exception ex)
{
throw new Exception("listening error" + ex);
}
}
Dus wanneer een client verbinding maakt, kunnen we deze als volgt accepteren:
Drie methoden die we hier gebruiken zijn:
We zijn begonnen met het terugbellen met
Listener.BeginAccept()
nu moeten we dat terugbellen beëindigen.The EndAccept()
methodeThe EndAccept()
accepteert een parameter IAsyncResult. Hiermee wordt de status van de asynchrone methode opgeslagen. Vanuit deze status kunnen we de socket extraheren waar de binnenkomende verbinding vandaan kwam.ClientController.AddClient()
Met de socket die we van
EndAccept()
hebben gekregen, maken we een client met een eigen methode (code ClientController onder servervoorbeeld) .We moeten opnieuw beginnen met luisteren wanneer de socket klaar is met het verwerken van de nieuwe verbinding. Geef de methode door die deze callback zal vangen. En geef ook de Listener-socket door, zodat we deze socket voor toekomstige verbindingen kunnen hergebruiken.
public void AcceptCallback(IAsyncResult ar)
{
try
{
Console.WriteLine($"Accept CallBack port:{Port} protocol type: {ProtocolType.Tcp}");
Socket acceptedSocket = ListenerSocket.EndAccept(ar);
ClientController.AddClient(acceptedSocket);
ListenerSocket.BeginAccept(AcceptCallback, ListenerSocket);
}
catch (Exception ex)
{
throw new Exception("Base Accept error"+ ex);
}
}
Nu hebben we een Listening Socket, maar hoe ontvangen we gegevens die door de client zijn verzonden, dat is wat de volgende code toont.
Maak een serverontvanger voor elke client
Maak eerst een ontvangstklasse met een constructor die een Socket als parameter opneemt:
public class ReceivePacket
{
private byte[] _buffer;
private Socket _receiveSocket;
public ReceivePacket(Socket receiveSocket)
{
_receiveSocket = receiveSocket;
}
}
In de volgende methode beginnen we eerst met het geven van de buffer een grootte van 4 bytes (Int32) of pakket bevat onderdelen {lengte, werkelijke gegevens}. Dus de eerste 4 bytes reserveren we voor de lengte van de gegevens, de rest voor de werkelijke gegevens.
Vervolgens gebruiken we de methode BeginReceive () . Deze methode wordt gebruikt om te beginnen met het ontvangen van verbonden clients en wanneer gegevens worden ontvangen, wordt de functie ReceiveCallback
.
public void StartReceiving()
{
try
{
_buffer = new byte[4];
_receiveSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveCallback, null);
}
catch {}
}
private void ReceiveCallback(IAsyncResult AR)
{
try
{
// if bytes are less than 1 takes place when a client disconnect from the server.
// So we run the Disconnect function on the current client
if (_receiveSocket.EndReceive(AR) > 1)
{
// Convert the first 4 bytes (int 32) that we received and convert it to an Int32 (this is the size for the coming data).
_buffer = new byte[BitConverter.ToInt32(_buffer, 0)];
// Next receive this data into the buffer with size that we did receive before
_receiveSocket.Receive(_buffer, _buffer.Length, SocketFlags.None);
// When we received everything its onto you to convert it into the data that you've send.
// For example string, int etc... in this example I only use the implementation for sending and receiving a string.
// Convert the bytes to string and output it in a message box
string data = Encoding.Default.GetString(_buffer);
MessageBox.Show(data);
// Now we have to start all over again with waiting for a data to come from the socket.
StartReceiving();
}
else
{
Disconnect();
}
}
catch
{
// if exeption is throw check if socket is connected because than you can startreive again else Dissconect
if (!_receiveSocket.Connected)
{
Disconnect();
}
else
{
StartReceiving();
}
}
}
private void Disconnect()
{
// Close connection
_receiveSocket.Disconnect(true);
// Next line only apply for the server side receive
ClientController.RemoveClient(_clientId);
// Next line only apply on the Client Side receive
Here you want to run the method TryToConnect()
}
Daarom hebben we een server ingesteld die inkomende verbindingen kan ontvangen en beluisteren. Wanneer een client verbinding maakt, wordt deze toegevoegd aan een lijst met clients en heeft elke client zijn eigen ontvangstklasse. Om de server te laten luisteren:
Listener listener = new Listener();
listener.StartListening();
Sommige klassen gebruik ik in dit voorbeeld
class Client
{
public Socket _socket { get; set; }
public ReceivePacket Receive { get; set; }
public int Id { get; set; }
public Client(Socket socket, int id)
{
Receive = new ReceivePacket(socket, id);
Receive.StartReceiving();
_socket = socket;
Id = id;
}
}
static class ClientController
{
public static List<Client> Clients = new List<Client>();
public static void AddClient(Socket socket)
{
Clients.Add(new Client(socket,Clients.Count));
}
public static void RemoveClient(int id)
{
Clients.RemoveAt(Clients.FindIndex(x => x.Id == id));
}
}
Voorbeeld clientzijde
Verbinden met de server
Allereerst willen we een klasse maken die verbinding maakt met de server die we de naam geven: Connector:
class Connector
{
private Socket _connectingSocket;
}
Volgende methode voor deze klasse is TryToConnect ()
Deze methode heeft enkele interessante dingen:
Maak de socket;
Vervolgens loop ik totdat de socket is aangesloten
Elke lus die het slechts 1 seconde vasthoudt, willen we de server XD niet DOSEREN
Met Connect () probeert het verbinding te maken met de server. Als het mislukt, genereert het een uitzondering, maar houdt het programma verbinding met de server. Je kunt hiervoor een Connect CallBack- methode gebruiken, maar ik ga gewoon een methode aanroepen wanneer de Socket is verbonden.
Merk op dat de client nu probeert verbinding te maken met uw lokale pc op poort 1234.
public void TryToConnect() { _connectingSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); while (!_connectingSocket.Connected) { Thread.Sleep(1000); try { _connectingSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234)); } catch { } } SetupForReceiveing(); } } private void SetupForReceiveing() { // View Client Class bottom of Client Example Client.SetClient(_connectingSocket); Client.StartReceiving(); }
Een bericht naar de server verzenden
Dus nu hebben we een bijna voltooide of Socket-applicatie. Het enige dat we niet hebben is een klasse voor het verzenden van een bericht naar de server.
public class SendPacket
{
private Socket _sendSocked;
public SendPacket(Socket sendSocket)
{
_sendSocked = sendSocket;
}
public void Send(string data)
{
try
{
/* what hapends here:
1. Create a list of bytes
2. Add the length of the string to the list.
So if this message arrives at the server we can easily read the length of the coming message.
3. Add the message(string) bytes
*/
var fullPacket = new List<byte>();
fullPacket.AddRange(BitConverter.GetBytes(data.Length));
fullPacket.AddRange(Encoding.Default.GetBytes(data));
/* Send the message to the server we are currently connected to.
Or package stucture is {length of data 4 bytes (int32), actual data}*/
_sendSocked.Send(fullPacket.ToArray());
}
catch (Exception ex)
{
throw new Exception();
}
}
Klik ten slotte op twee knoppen, één voor verbinden en de andere voor het verzenden van een bericht:
private void ConnectClick(object sender, EventArgs e)
{
Connector tpp = new Connector();
tpp.TryToConnect();
}
private void SendClick(object sender, EventArgs e)
{
Client.SendString("Test data from client");
}
De clientklasse die ik in dit voorbeeld heb gebruikt
public static void SetClient(Socket socket)
{
Id = 1;
Socket = socket;
Receive = new ReceivePacket(socket, Id);
SendPacket = new SendPacket(socket);
}
Merk op
De klasse Ontvangen van de server is dezelfde als de klasse Ontvangen van de client.
Conclusie
U hebt nu een server en een client. U kunt dit basisvoorbeeld uitwerken. Zorg er bijvoorbeeld voor dat de server ook bestanden of andere tings kan ontvangen. Of stuur een bericht naar de klant. Op de server heb je een lijst met clients, dus als je iets ontvangt waarvan je weet dat het bij de client vandaan komt.