每次连接到服务器时接收客户端数据

时间:2019-07-28 21:33:44

标签: c# sockets connection

我需要在客户端连接到服务器时,服务器采用该客户端的名称并将其保存在Dictionary<string, Socket>中,即,记录客户端及其套接字的历史记录。

我已经修改了现有代码,您可以看到发生了什么。该代码的目的是在连接期间将客户端连接到服务器并记录其信息。

客户

将客户端连接到服务器并尝试在连接时尝试发送数据的代码:

public class Client
{
    private Socket socket;
    private string user;

    public Client()
    {
        user = "admin";
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    }

    public void ConnectToServer(IPEndPoint ipServer)
    {
        socket.BeginConnect(ipServer, new AsyncCallback(ConnectCallBack), null);
    }

    private void ConnectCallBack(IAsyncResult asyncResult)
    {
        try
        {
            byte[] buffer = Encoding.Default.GetBytes(user);
            socket.Send(buffer);
            socket.EndConnect(asyncResult);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
}

服务器

接收客户及其数据的代码:

public class Server
{
    private Socket socket;
    private Dictionary<string, Socket> data;

    public Server()
    {
        this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        data = new Dictionary<string, Socket>();
    }

    public void Start(IPEndPoint ipServer)
    {           
        this.socket.Bind(ipServer);
        this.socket.Listen(0);
        this.socket.BeginAccept(socket.ReceiveBufferSize, AcceptCallBack, null);
        Console.WriteLine($"Server connected in [{ipServer.Address}] : port [{ipServer.Port}]");
    }

    private void AcceptCallBack(IAsyncResult asyncResult)
    {
        try
        {
            byte[] buffer = new byte[this.socket.ReceiveBufferSize];    
            Socket socket = this.socket.EndAccept(out buffer, asyncResult);
            string user = Encoding.Default.GetString(buffer);

            // edit
            if (!data.ContainsKey(user) && !string.IsNullOrEmpty(user))
            {
                data.Add(user, socket);
            }

            Console.WriteLine("user '{0}' connected", user);    
            this.socket.BeginAccept(AcceptCallBack, null);
        }
        catch (Exception e)
        {
            throw e;
        }
    }
}

例如,一个人加入了服务器,但再次注销。下次该客户端连接到服务器时,服务器将不再提取数据,即客户端名称。这在空服务器缓冲区中可以看到。即使客户端从ConnectCallBack()方法发送其byte []名称,服务器也会将缓冲区声明为空。

下面我说明了问题

1º
--------------->---------------------------->------------------------->    
client connect | server recognizes the data | add client in dictionary
--------------->---------------------------->------------------------->

2º
------------------>--------->--------------------------->    
client disconnect | server  | remove client in dictionary
------------------>--------->--------------------------->

3º problem here
---------------------->--------------------------------------------->    
client connects again | server does not recognize client socket data
---------------------->--------------------------------------------->

如何解决此运输问题?

1 个答案:

答案 0 :(得分:0)

问题出在服务器端的代码内。不幸的是,您决定捕获所有异常并忽略它们(请注意空的捕获子句)。这将隐藏当同一用户第二次连接并且您尝试再次将相同的键添加到字典时抛出的ArgumentExceptionDictionary只能包含每个密钥一次。

由于您提到创建历史记录,因此可以使用List代替字典:

socket.BeginAccept(socket.ReceiveBufferSize, OnConnect, null);
List<KeyValuePair<string, Socket>> data;

private void AcceptCallBack(IAsyncResult asyncResult)
{
    try
    {
         byte[] buffer = new byte[this.socket.ReceiveBufferSize];

         Socket socket = this.socket.EndAccept(out buffer, asyncResult);
         string user = Encoding.Default.GetString(buffer);
         data.Add(new KeyValuePair<string, Socket>(user, socket));
         Console.WriteLine("user '{0}' connected",user);

         this.socket.BeginAccept(OnConnect, null);
     }
     catch 
     {
         // Still, don't do this. Catch exceptions and at least log the error message
     }
}

此示例使用KeyValuePair,因为它也是Dictionary枚举的类型。在此处创建新类型或使用C#7元组可能会很有用。

或者,如果您希望坚持使用字典,并且对先前的连接不感兴趣,请使用索引器将对Add的调用交换为索引并覆盖旧值:

socket.BeginAccept(socket.ReceiveBufferSize, OnConnect, null);
Dictionary<string, Socket> data;

private void AcceptCallBack(IAsyncResult asyncResult)
{
    try
    {
         byte[] buffer = new byte[this.socket.ReceiveBufferSize];

         Socket socket = this.socket.EndAccept(out buffer, asyncResult);
         string user = Encoding.Default.GetString(buffer);
         data[user] = socket;
         Console.WriteLine("user '{0}' connected",user);

         this.socket.BeginAccept(OnConnect, null);
     }
     catch 
     {
         // Still, don't do this. Catch exceptions and at least log the error message
     }
}