C# Language
Asynkron uttag
Sök…
Introduktion
Genom att använda asynkrona uttag kan en server lyssna på inkommande anslutningar och göra någon annan logik under tiden i motsats till synkronuttag när de lyssnar blockerar de huvudtråden och applikationen blir svarsfri och kommer att frysa tills en klient ansluter.
Anmärkningar
Socket och nätverk
Hur kommer jag åt en server utanför mitt eget nätverk? Detta är en vanlig fråga och när den ställs flaggas det mest av ämnet.
Server sida
I ditt serverns nätverk måste du vidarebefordra din router till din server.
Till exempel PC där servern körs på:
lokal IP = 192.168.1.115
Servern lyssnar på port 1234.
Vidarebefordra inkommande anslutningar på Port 1234
routern till 192.168.1.115
Klientsidan
Det enda du behöver ändra är IP. Du vill inte ansluta till din loopback-adress utan till den offentliga IP-adressen från det nätverk som din server kör på. Denna IP kan du få här .
_connectingSocket.Connect(new IPEndPoint(IPAddress.Parse("10.10.10.10"), 1234));
Så nu skapar du en begäran på den här slutpunkten: 10.10.10.10:1234
om du gjorde 10.10.10.10:1234
vidarebefordrade din router kommer din server och klient att ansluta utan problem.
Om du vill ansluta till en lokal IP kommer du inte att behöva portvänta bara ändra loopback-adressen till 192.168.1.178
eller något liknande.
Skickar data:
Data skickas i byte-array. Du måste packa dina data i en byte-grupp och packa upp dem på andra sidan.
Om du känner till socket kan du också försöka kryptera din byte-grupp innan du skickar. Detta förhindrar att någon stjäl ditt paket.
Exempel på Asynchronous Socket (Client / Server).
Exempel på serversidan
Skapa lyssnare för servern
Börja med att skapa en server som hanterar klienter som ansluter och begäranden som kommer att skickas. Så skapa en lyssnarklass som hanterar detta.
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);
}
}
Först måste vi initiera lyssnaruttaget där vi kan lyssna på för alla anslutningar. Vi kommer att använda ett Tcp-uttag och det är därför vi använder SocketType.Stream. Vi specificerar också för att häxa port servern ska lyssna på
Sedan börjar vi lyssna efter inkommande anslutningar.
Trädmetoderna vi använder här är:
Den här metoden binder kontakten till en IPEndPoint . Den här klassen innehåller information om värd och lokal eller fjärrport som en applikation behöver för att ansluta till en tjänst på en värd.
Parametern för backlog anger antalet inkommande anslutningar som kan stå i kö för godkännande.
ListenerSocket.BeginAccept ();
Servern börjar lyssna efter inkommande anslutningar och fortsätter med annan logik. När det finns en anslutning växlar servern tillbaka till den här metoden och kör AcceptCallBack-metoden
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);
}
}
Så när en klient ansluter kan vi acceptera dem med den här metoden:
Tre metoder som vi använder här är:
Vi startade återuppringningen med
Listener.BeginAccept()
slutar nu måste vi avsluta det samtalet igen.The EndAccept()
accepterar en IAsyncResult-parameter, detta lagrar tillståndet för den asynkrona metoden. Från det här läget kan vi extrahera uttaget där den inkommande anslutningen kom från.ClientController.AddClient()
Med det uttag vi fick från
EndAccept()
skapar vi en klient med en egengjord metod (kod ClientController nedanför serverexempel) .Vi måste börja lyssna igen när uttaget är klart med hanteringen av den nya anslutningen. Ange metoden som kommer att fånga detta återuppringning. Och passera också lyssnaruttaget så att vi kan återanvända detta uttag för kommande anslutningar.
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 har vi ett lyssningsuttag men hur får vi datasändning av klienten som är vad nästa kod visar.
Skapa servermottagare för varje klient
Skapa först en mottagningsklass med en konstruktör som tar in en socket som parameter:
public class ReceivePacket
{
private byte[] _buffer;
private Socket _receiveSocket;
public ReceivePacket(Socket receiveSocket)
{
_receiveSocket = receiveSocket;
}
}
I nästa metod börjar vi först med att ge bufferten en storlek på 4 byte (Int32) eller paketet innehåller delar {längd, faktisk data}. Så de första 4 byte vi reserverar för längden på data resten för faktiska data.
Därefter använder vi metoden BeginReceive () . Den här metoden används för att börja ta emot från anslutna klienter och när den kommer att ta emot data kommer den att ReceiveCallback
funktionen 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()
}
Så vi har konfigurerat en server som kan ta emot och lyssna på inkommande anslutningar. När en klient ansluter kommer den att läggas till i en lista över klienter och varje klient har sin egen mottagningsklass. För att få servern att lyssna:
Listener listener = new Listener();
listener.StartListening();
Vissa klasser använder jag i det här exemplet
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));
}
}
Exempel på klientsidan
Ansluta till servern
Först och främst vill vi skapa en klass som ansluter till servern som vi ger det är: Connector:
class Connector
{
private Socket _connectingSocket;
}
Nästa metod för denna klass är TryToConnect ()
Den här metoden får några intressanta saker:
Skapa uttaget;
Därefter slingrar jag tills uttaget är anslutet
Varje slinga är det bara att hålla tråden i 1 sekund, vi vill inte DOS-servern XD
Med Connect () kommer det att försöka ansluta till servern. Om den misslyckas kommer det att kasta ett undantag men wile kommer att hålla programmet anslutet till servern. Du kan använda en Connect CallBack- metod för det här, men jag ska bara ringa för att ringa en metod när Socket är ansluten.
Observera att klienten nu försöker ansluta till din lokala dator på port 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(); }
Skickar ett meddelande till servern
Så nu har vi en nästan finish eller Socket-applikation. Det enda som vi inte har jet är en klass för att skicka ett meddelande till servern.
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();
}
}
Slutför låda två knappar en för anslutning och den andra för att skicka ett meddelande:
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");
}
Klientklass som jag använde i det här exemplet
public static void SetClient(Socket socket)
{
Id = 1;
Socket = socket;
Receive = new ReceivePacket(socket, Id);
SendPacket = new SendPacket(socket);
}
Lägga märke till
Mottagningsklassen från servern är densamma som mottagarklassen från klienten.
Slutsats
Du har nu en server och en klient. Du kan arbeta med detta grundläggande exempel. Till exempel gör det att servern också kan ta emot filer eller andra tings. Eller skicka ett meddelande till klienten. På servern fick du en lista över klienten så när du får något du kommer att veta från med klienten kom det från.