TCPConnection, способный обрабатывать параллельные запросы. Клиент-сервер

#c# #concurrency #tcp #client-server

#c# #параллелизм #tcp #клиент-сервер

Вопрос:

Я пытаюсь создать простое клиент-серверное приложение со следующими кодами:

 //SERVER
IPAddress ipAd = IPAddress.Parse("192.163.10.101");
TcpListener myList = new TcpListener(ipAd, 8001);
myList.Start();

Console.WriteLine("The server is running at port 8001...");
Console.WriteLine("The local End point is  :"   myList.LocalEndpoint);
Console.WriteLine("Waiting for a connection.....");

Socket s = myList.AcceptSocket();
Console.WriteLine("Connection accepted from "   s.RemoteEndPoint);

//CLIENT
TcpClient tcpclnt = new TcpClient();
Console.WriteLine("Connecting.....");
tcpclnt.Connect("192.163.10.101",8001); 
Console.WriteLine("Connected");
  

это на самом деле делает то, что мне нужно, когда клиент может подключиться к серверу. Однако, когда я пытаюсь запустить несколько экземпляров клиента для соединения с сервером, сервер принимает только первый клиент для подключения. Это означает, что существует соединение типа «один к одному», при котором только один клиент может подключиться к клиенту. Однако, что мне нужно, так это предоставить серверу возможность принимать подключения более чем от одного клиента.

Если кто-нибудь может указать мне возможное решение этого, я действительно буду признателен! Спасибо!

Комментарии:

1. Если первый клиент отключается, можете ли вы подключить второго клиента?

2. да, я могу. чего я хочу добиться, так это заставить сервер принимать несколько подключенных клиентов. 🙂 спасибо за ответ!

Ответ №1:

Вам нужно снова вызвать AcceptSocket, чтобы принять другой сокет.
Типичным вариантом было бы вызвать BeginAcceptSocket и при обратном вызове EndAcceptSocket отправить обработку клиента в его собственный поток (или рабочий поток, использующий асинхронные методы), а затем снова вызвать BeginAcceptSocket.

Этот фрагмент не протестирован, но должен быть более или менее правильным / заставить вас думать в правильном направлении.

 class Server
{
    public Server()
    {
        TcpListener listener = null;

        // init the listener

        listener.BeginAcceptSocket((ar) => AcceptLoop(ar, listener),null);
    }

    public void HandleClientSocketRead(IAsyncResult ar, byte[] recvBuffer, Socket clientSocket)
    {
        int recvd = clientSocket.EndReceive(ar);
        //do something with the data
        clientSocket.BeginReceive(recvBuffer, 0, 1024, SocketFlags.None, (ar2) => HandleClientSocketRead(ar2, recvBuffer, clientSocket), null);
    }

    public void AcceptLoop(IAsyncResult ar, TcpListener listener)
    {
        Socket clientSocket = listener.EndAcceptSocket(ar); // note that this can throw
        byte[] recvBuffer = new byte[1024];

        clientSocket.BeginReceive(recvBuffer, 0, 1024, SocketFlags.None, (ar2) => HandleClientSocketRead(ar2, recvBuffer, clientSocket), null);

        listener.BeginAcceptSocket((ar) => AcceptLoop(ar, listener), null);
    }
}
  

Комментарии:

1. если я использую этот код в качестве своего сервера, должен ли я изменять части моего клиента? извините, но я думаю, что я запутался. спасибо за помощь! 🙂

2. нет. Вполне допустимо выполнять асинхронные операции на сервере и синхронные операции на клиенте.

Ответ №2:

Если вы хотите написать сервер, хороший дизайн должен иметь [server].exe и [client].exe. [server].exe, конечно, будет принимать и обрабатывать все входящие соединения, поддерживать клиентские сокеты и выполнять любые действия, которые вам нужны. Ниже приведен очень простой пример написания сервера для приема нескольких клиентских сокетов и сохранения их в объекте List. Это, однако, не является многопоточным, поэтому код блокируется.

[server].exe

 //-----------------------------------------------------------------------------
// <copyright file="Program.cs" company="DCOM Productions">
//     Copyright (c) DCOM Productions.  All rights reserved.
// </copyright>
//-----------------------------------------------------------------------------

namespace MultiSocketServerExample {
    using System;
    using System.Net.Sockets;
    using System.Net;
    using System.Collections.Generic;

    class Program {

        static List<Socket> m_ConnectedClients = new List<Socket>();

        static void Main(string[] args) {
            Socket host = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            host.Bind(new IPEndPoint(IPAddress.Any, 9999));
            host.Listen(1);

            while (true) {
                m_ConnectedClients.Add(host.Accept());
                Console.WriteLine("A client connected.");
            }
        }
    }
}
  

Затем для работы с вашими клиентами: (Опять же, очень простой пример)

 m_ConnectedClients[0].Send(Encoding.ASCII.GetBytes("hello!");
  

Сетевое программирование с использованием класса Socket, на мой взгляд, намного проще, чем с использованием TcpListener и TcpClient. Причина, по которой я говорю это, заключается в том, что это уже действительно хорошая и простая в использовании реализация, а использование TcpListener и TcpClient, где они создают дополнительную абстракцию, уменьшает вашу способность понимать, что происходит (на мой взгляд).

Комментарии:

1. если я собираюсь использовать этот тип кода для своего сервера, то мне, вероятно, тоже следует поместить свой код внутри цикла while, верно? я имею в виду, потому что я должен принимать некоторые входные данные от пользователя и обрабатывать их с помощью сервера.

2. Подумайте о том, что должно делать ваше приложение. Вам нужен клиент, который подключается к серверу, может принимать несколько команд (постоянно), отправлять на сервер и быть обработанным сервером. В таком порядке. 1) Напишите код для подключения 2) Примите входные данные 3) Отправьте на сервер 4) дождитесь ответа (это порядок для клиентской стороны), поэтому правильно, вам нужен цикл для приема входных данных. Сразу после ввода подготовьте, отправьте и дождитесь ответа, затем выполните цикл и начните сначала