W tym poście chciałbym przedstawić dwie aplikacje, jedna działa jako klient druga jako serwer. Każda z nich działa na jako aplikacja konsolowa.
Procedurę uruchomienia należy rozpocząć od postawienia serwera. W funkcji tworzy się nowe połączenie o podanych parametrach. Następnie ustanawia się maksymalną liczbę akceptowalnych połączeń.Ostatnim elementem jest utworzenie tzw. callbacka, który będzie wywoływany w momencie otrzymania danych.
Zamknięcie serwera odbywa się za pomocą tej funkcji. Sprawdza ona wszystkie uruchomione połączenia.
Akceptowanie połączenia oraz odbieranie wiadomości wykonane jest na callbackach.
Akceptacja połączenia:
Odebranie oraz dekodowanie wiadomości:
Jedna z akceptowalnych połączeń pozwala na wyświetlenie w odpowiedzi ustawionego adresu IP:
Funkcja główna wygląda następująco:
W tym przypadku wykonywane jest połączenie z serwerem przy użyciu następującej funkcji:
Funkcja wykonuje 100 prób połączenia. Jeśli uda się wykonać połączenie to funkcja zwraca jeden i przechodzi do dalszej części programu. Inaczej zwraca zero i program kończy działanie.
Odłączenie klienta od serwera wygląda następująco:
Funkcje obsługujące dane wyglądają następująco:
W pętli while przygotowywane są wiadomości do wysłania, następnie w kolejnej funkcji są one odbierane i przetwarzane tak aby je wyświetlić w konsoli. Do wysyłania użyta jest metodasocketu send, odbieranie obsługiwane jest przez receive. Wysłanie przez klienta wiadomości close powoduje odłączenie klienta od serwera oraz jego wyłączenie.
Main prezentuje się następująco:
Aplikacja Serwer konsola:
Procedurę uruchomienia należy rozpocząć od postawienia serwera. W funkcji tworzy się nowe połączenie o podanych parametrach. Następnie ustanawia się maksymalną liczbę akceptowalnych połączeń.Ostatnim elementem jest utworzenie tzw. callbacka, który będzie wywoływany w momencie otrzymania danych.
- private static void SetupServer()
- {
- IPAddress setIp = IPAddress.Any; /* Pass any Ip adress into variable */
- Console.WriteLine(@"Start setting up server...");
- serverSocket.ExclusiveAddressUse = true; /* No other socket can't bing to selected port */
- serverSocket.LingerState = new LingerOption(true, 10); /* The socket will linger for 10 seconds after calling Socket.Close */
- serverSocket.NoDelay = true; /* Disable the Nagle Algorithm for this tcp socket */
- serverSocket.ReceiveBufferSize = 8192; /* Set the receive buffer size to 8k */
- serverSocket.ReceiveTimeout = 1000;
- serverSocket.Ttl = 42; /* Set the Time To Live (TTL) to 42 router hops */
- serverSocket.Bind(new IPEndPoint(setIp, port)); /* Connect with define port and set IP */
- serverSocket.Listen(15); /* Max connection that can wait in queue */
- serverSocket.BeginAccept(AcceptCallback, null); /* Begins an asynchronous operation to accept
- an incoming connection attempt */
- Console.WriteLine(@"Server setup complete");
- }
Zamknięcie serwera odbywa się za pomocą tej funkcji. Sprawdza ona wszystkie uruchomione połączenia.
- private static void CloseAllSockets()
- {
- foreach (Socket socket in clientSockets)
- {
- socket.Shutdown(SocketShutdown.Both); /* Block sending and receiving */
- /*
- or
- socket.Close();
- */
- }
- Console.WriteLine(@"All sockets has been closed");
- }
Akceptowanie połączenia oraz odbieranie wiadomości wykonane jest na callbackach.
Akceptacja połączenia:
- private static void AcceptCallback(IAsyncResult AR)
- {
- Socket socket;
- try{
- socket = serverSocket.EndAccept(AR); /* Asynchronously accepts an incoming connection attempt
- and creates a new Socket to handle remote host communication. */
- }
- catch (ObjectDisposedException ex){
- Console.WriteLine(@"Caught: {0}", ex.Message);
- return;
- }
- clientSockets.Add(socket); /* Add new connection*/
- /* Begins to asynchnous receive data */
- socket.BeginReceive(buffer, 0, bufferSize, SocketFlags.None, ReceiveCallback, socket); /* Get data */
- Console.WriteLine(@"Client connected...");
- serverSocket.BeginAccept(AcceptCallback, null); /* Begins asonchronyous operation for
- accepting try for connection */
- }
Odebranie oraz dekodowanie wiadomości:
- private static void ReceiveCallback(IAsyncResult AR)
- {
- Socket recDataCurrentConnection = (Socket)AR.AsyncState;
- int received;
- try{
- received = recDataCurrentConnection.EndReceive(AR);
- }
- catch (SocketException ex){
- Console.WriteLine(@"Client disconnected" + ex);
- recDataCurrentConnection.Close(); /* Only close connection don't need to shutdown communication */
- clientSockets.Remove(recDataCurrentConnection); /* Remove client from socket list */
- return; /* Exit from function */
- }
- byte[] recBuf = new byte[received];
- Array.Copy(buffer, recBuf, received);
- string text = Encoding.ASCII.GetString(recBuf);
- Console.WriteLine(@"Received Message: " + text);
- if (text.ToLower() == "time"){
- Console.WriteLine("Send time:");
- byte[] data = Encoding.ASCII.GetBytes(DateTime.Now.ToLongTimeString());
- recDataCurrentConnection.Send(data);
- Console.WriteLine("Message send to client:");
- }
- else if (text.ToLower() == "date") {
- Console.WriteLine("Send date:");
- byte[] data = Encoding.ASCII.GetBytes(DateTime.Now.ToLongDateString());
- recDataCurrentConnection.Send(data);
- Console.WriteLine("Message send to client:");
- }
- else if (text.ToLower() == "ip local"){
- Console.WriteLine("Send local ip:");
- byte[] data = Encoding.ASCII.GetBytes(GetLocalIPAddress());
- recDataCurrentConnection.Send(data);
- Console.WriteLine("Message send to client:");
- }
- else if (text.ToLower() == "close"){
- recDataCurrentConnection.Shutdown(SocketShutdown.Both);
- recDataCurrentConnection.Close();
- clientSockets.Remove(recDataCurrentConnection);
- Console.WriteLine("Client disconnected");
- return;
- }
- else{
- Console.WriteLine("Text is an invalid request");
- byte[] data = Encoding.ASCII.GetBytes("Invalid request");
- recDataCurrentConnection.Send(data);
- Console.WriteLine("Warning Sent");
- }
- recDataCurrentConnection.BeginReceive(buffer, 0, bufferSize,
- SocketFlags.None, ReceiveCallback, recDataCurrentConnection);
- }
Jedna z akceptowalnych połączeń pozwala na wyświetlenie w odpowiedzi ustawionego adresu IP:
- private static string GetLocalIPAddress()
- {
- var host = Dns.GetHostEntry(Dns.GetHostName());
- foreach (var ip in host.AddressList)
- {
- if (ip.AddressFamily == AddressFamily.InterNetwork)
- {
- return ip.ToString();
- }
- }
- throw new Exception("Local IP Address Not Found!");
- }
Funkcja główna wygląda następująco:
Aplikacja Klient konsola:
W tym przypadku wykonywane jest połączenie z serwerem przy użyciu następującej funkcji:
- private static byte ConnectToServer()
- {
- int connectAttempt = 0; /* Number of connection attempts before good connection */
- while (!ClientSocket.Connected) /* Wait for establish connection, block before connection will be set */
- {
- try{
- connectAttempt++; /* Add variable for next connection */
- Console.WriteLine(@"Connection attempts: " + connectAttempt);
- ClientSocket.Connect(IPAddress.Loopback, port); /* Set IPLoopback (127.0.0.1) to remote IP */
- }
- catch (SocketException ex){
- Console.Clear();
- Console.WriteLine("Error: {0}", ex.Message); /* Console clear and display error message */
- }
- if(connectAttempt == 100) {
- Console.Clear();
- Console.WriteLine(@"Can't connect to server: " + connectAttempt);
- CloseSocket();
- return 0;
- }
- }
- if(ClientSocket.Connected){
- Console.Clear();
- Console.WriteLine(@"Connected");
- }
- return 1;
- }
Funkcja wykonuje 100 prób połączenia. Jeśli uda się wykonać połączenie to funkcja zwraca jeden i przechodzi do dalszej części programu. Inaczej zwraca zero i program kończy działanie.
Odłączenie klienta od serwera wygląda następująco:
- private static void CloseSocket()
- {
- SendString("close"); /* Send close dat to server*/
- ClientSocket.Shutdown(SocketShutdown.Both); /* Block communication */
- ClientSocket.Close(); /* Close client */
- Environment.Exit(0);
- }
Funkcje obsługujące dane wyglądają następująco:
- private static void RequestLoop()
- {
- Console.WriteLine(@"Close command disconnect client from server");
- while (true){
- SendRequest(); /* Wait for sending command to server */
- ReceiveResponse();
- }
- }
- private static void SendRequest()
- {
- Console.Write("Send a command: ");
- string request = Console.ReadLine(); /* Read command */
- SendString(request); /* Send data to server */
- if (request.ToLower() == "close"){
- CloseSocket(); /* Close socket */
- }
- }
- private static void SendString(string text)
- {
- byte[] buffer = Encoding.ASCII.GetBytes(text);
- /* Send data to server, no flags is use to establish communication */
- ClientSocket.Send(buffer, 0, buffer.Length, SocketFlags.None);
- }
- private static void ReceiveResponse()
- {
- var buffer = new byte[8196];
- int received = ClientSocket.Receive(buffer, SocketFlags.None); /* Receive data */
- if (received == 0){
- Console.WriteLine(@"No data was received");
- }
- else{
- var data = new byte[received];
- Array.Copy(buffer, data, received);
- string text = Encoding.ASCII.GetString(data);
- Console.WriteLine(text);
- }
- }
W pętli while przygotowywane są wiadomości do wysłania, następnie w kolejnej funkcji są one odbierane i przetwarzane tak aby je wyświetlić w konsoli. Do wysyłania użyta jest metodasocketu send, odbieranie obsługiwane jest przez receive. Wysłanie przez klienta wiadomości close powoduje odłączenie klienta od serwera oraz jego wyłączenie.
Main prezentuje się następująco:
- private const int port = 1001; /* Port for connection with server */
- /*
- * InterNetwork: Address for IP in version 4
- * Stream: Support two way connection-based byte wihout duplication of data
- * ProtocolType: Transmission Control Protocol
- */
- private static readonly Socket ClientSocket = new Socket
- (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- static void Main()
- {
- Console.Title = @"Client TCP"; /* Console window title */
- /* Connect client to server */
- if (1 == ConnectToServer()){
- RequestLoop(); /* Loop for establish communication */
- CloseSocket(); /* Close communication */
- }
- }