【问题标题】:Sockets maintain socket open and just check the messages that the client is sending套接字保持套接字打开并仅检查客户端发送的消息
【发布时间】:2014-06-30 21:00:33
【问题描述】:

我想要做的只是检查客户端发送的消息。 我想要的是保持创建的套接字打开,并检查客户端不时发送的获取消息。

我只使用System.Net.Sockets,我想基于 barkeley 套接字进行实现。

这是我的代码,

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Runtime.Remoting.Metadata.W3cXsd2001;
namespace Socket_v1._0
{
    class Program
    {
        private const int BUFSIZE = 200;// Size of receive buffer
        private const int BACKLOG = 5; // O valor backlog define o número de pedidos de conexão que podem ser mantidos em espera sem serem aceites pela "system-call" accept.
        static void Main(string[] args)
        {
            int servPort = 8070; //port number

            Socket server = null;
            try
            {
                // Create a socket to accept client connections
                server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                server.Bind(new IPEndPoint(IPAddress.Any, servPort));
                server.Listen(BACKLOG);
            }
            catch (SocketException se)
            {
                Console.WriteLine(se.ErrorCode + ": " + se.Message);
                Environment.Exit(se.ErrorCode);
            }
//for now the server socket is waiting for connections
            byte[] rcvBuffer = new byte[BUFSIZE]; // definir buffer
            int bytesRcvd = 0 ; // numero de bytes recebidos
            for (; ; )
            { // Run forever, accepting and servicing connections
                Socket client = null;
                try
                {

                    client = server.Accept(); // Get client connection
                    Console.Write("Handling client at " + client.RemoteEndPoint + " - ");

                    do {
                        bytesRcvd = client.Receive(rcvBuffer, 0, rcvBuffer.Length, SocketFlags.None);
                        string s = Encoding.ASCII.GetString(rcvBuffer, 0, bytesRcvd);
                        }while (true);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    client.Close();
                }
            }
        }
      }
    }

所以客户端不时发送一个http get,我想看看那些消息,

现在这是第一次运行字符串获取 http get,并且因为我保持套接字打开,所以我想等待下一条消息 (do{}(while(true)))。 它返回到 client.receive,然后停止。 在检测到来自客户端的另一条消息后,它进入永久循环并且字符串为空。

我想要类似于聊天的东西,客户端发送消息,服务器显示消息。

那么打开socket后,我应该怎么做才能从客户端获取消息,然后显示它,然后等待另一个消息?

让我烦恼的另一件事是客户端 http get:

这是http获取

GET /iclock/cdata?SN=2182682370001&options=all&pushver=2.1.2&language=80 HTTP/1.1
Host: 192.168.1.43:8070
User-Agent: iClock Proxy/1.09
Connection: close
Accept: * / *

连接关闭是什么意思?每次客户端要发送请求时我都需要创建一个新套接字吗?

试着弄明白,我对代码做了一些更改,虽然我得到了这个

 while ((bytesRcvd = client.Receive(rcvBuffer/*, 0, rcvBuffer.Length, SocketFlags.None*/)) > 0) 
                    { 
                        totalBytesEchoed += bytesRcvd;
                        string s = Encoding.ASCII.GetString(rcvBuffer, 0, bytesRcvd);
                        Console.WriteLine(s);
                    } 

图片:

所以在图像中我运行了wireshark 和我的控制台应用程序。

根据wireshark,客户端试图发送2个http请求,但根据控制台它只显示一个......

我想显示这两个电话..

几年前,我用c语言做了一些关于套接字的练习。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void main(void)
{
  struct sockaddr_in me, from;
  int newSock, sock = socket(AF_INET, SOCK_STREAM, 0);
  int adl = sizeof(me);
  char linha[81];
  bzero((char *) &me, adl);
  me.sin_family = AF_INET;
  me.sin_addr.s_addr = htonl(INADDR_ANY ); /* endereco IP local */
  me.sin_port = htons(8450); /* porta local  */
  if (-1 == bind(sock, (struct sockaddr *) &me, adl))
  {
    close(sock);
    puts("Porta ocupada");
    exit(1);
  }
  listen(sock, 5);
  newSock = accept(sock, (struct sockaddr *) &from, &adl);
  close(sock);
  do
  {
    read(newSock, linha, 81);
    puts(linha);
  } while (strcmp(linha, "exit"));
  close(newSock);
}

这是在c中,所以你可以看到,在接受套接字保持打开状态并且客户端发送消息后,它只会在用户向服务器发送退出时关闭套接字...... 可能有时客户端不发送......但套接字仍然打开,并且当它检测到时

这是我想要实现的示例,使用 system.net.sockets 库,,,

谢谢

【问题讨论】:

  • 你可以试试 SignalR,似乎很适合你的需要
  • 我可以,但我只想使用套接字库,我知道它需要更多的工作,但这不可能吗?
  • 您的代码应该可以工作。它应该显示单个连接的客户端发送的所有内容。您能否更具体地说明您要解决的问题?
  • 我添加了一张图片并描述了它...
  • HTTP 默认不使用 keep-alive 连接——每个请求-响应对使用一个新的 TCP 连接(因此是一个新的客户端套接字)。当Receive 方法返回零时,它告诉您连接已关闭。总而言之,除非你想学习很多关于 TCP 和 HTTP 工作原理(以及如何在 C# 中正确处理)的新知识,否则你应该使用更高抽象级别的东西。使用例如HttpListener 将为您省去很多麻烦。或者查看 OWIN 以获得成熟的进程内 Web 服务器 :)

标签: c# .net sockets


【解决方案1】:

socket有两种:你用来监听的socket(它从不连接)和对应连接的socket(每个socket代表一个连接)。

Accept 将一个已连接的套接字返回给刚刚被接受的客户端。对 Accept 的每次调用都接受一个新的独立客户端。

如果您想一次处理多个客户(这几乎总是需要),您必须确保对 Accept 的调用始终处于挂起状态,以便可以接受新客户。

实现此目的的一个简单模型是永远接受循环,并为您接受的每个客户端启动一个线程:

while (true) {
 var clientSocket = listeningSocket.Accept();
 Task.Factory.StartNew(() => HandleClient(clientSocket));
}

【讨论】:

  • 但是如果客户端是一样的呢?我确实明白,要让它处理各种客户端,我需要使用线程。但在这种情况下,现在只有一个客户端每隔 1 秒发送一些请求,就像上面 c 中的示例一样,可以打开套接字并保持打开状态,直到客户端发送退出...今天我会尝试创建一个示例示例,例如聊天我尝试创建多个客户端,看看它是如何进行的..
  • 第二个请求真的在同一个 TCP 连接上吗?正如我所说,您的代码在消耗一个连接的意义上是有效的。但是,在第一次连接后它永远不会继续,因为你只是无限期地阅读。客户这个词不是很精确。您可能应该说连接。 TCP 不了解客户端或 HTTP 是什么。
  • 感谢您的帮助..所以我有服务器套接字和客户端套接字,服务器套接字正在侦听新连接,并且当它检测到它会创建一个新套接字..使用客户端 = 服务器。接受();之后我需要添加线程以处理客户端套接字吗?
  • 是的。从概念上讲,这很简单。不过,要做好继续犯错的准备,因为套接字真的很难。我希望这对您来说只是一个学习练习,而不是生产代码。在生产中,更喜欢更高级别的解决方案,如 Web 服务。希望问题已为您解决。
  • 谢谢,在这个周末我要去尝试做一个测试程序,通过创建一个服务器和一个客户端并让它工作,然后我会在这里解决我发现的问题......以及完整的练习一旦完成并正常工作。
【解决方案2】:

很可能是客户端关闭了连接。

要在服务器端检测到这一点,您需要针对0 测试调用Receive() 的结果。

来自MSDN's documentation on Receive()(我强调):

如果您使用的是面向连接的 Socket,则 Receive 方法将读取尽可能多的数据,最多为 size 参数指定的字节数。 如果远程主机使用 Shutdown 方法关闭了 Socket 连接,并且所有可用数据都已接收,Receive 方法将立即完成并返回 个字节。

【讨论】:

    【解决方案3】:

    根据我的经验,每次客户端与服务器/侦听器断开连接时,都会在服务器上注册为 SocketException - 客户端已强制断开连接。我现在所做的只是尝试抓住它并“很好地”忽略它(记录/显示某种消息或运行一个操作来通知用户)。

    看看你的代码,稍微重构一下。尝试运行它,看看发生了什么。我添加了一些 cmets 来解释一下。希望这会有所帮助。

    .NET 中还有一个名为 TcpListenner 的类,您可以使用它来实现所需的行为。

    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    
    namespace SimpleListener
    {
        class Program
        {
            const int PORT = 8070;
            const int BACKLOG = 5;
    
            static Socket socket;
            private static byte[] rcvBuffer;
            private static int bytesRcvd;
            private static string message;
    
            static void Main(string[] args)
            {
                try
                {
                    StartListener();
    
                    Listen();
                }
                catch (Exception ex)
                {
                    ShowMessage(ex.Message);
                }
            }
    
            private static void StartListener()
            {
                try
                {
                    //Initialize socket and start listenning
                    socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    socket.Bind(new IPEndPoint(IPAddress.Any, PORT));
                    socket.Listen(BACKLOG);
    
                    ShowMessage("I'm listenning now..");
                }
                catch (Exception)
                {
                    throw;
                }
            }
    
            private static void Listen()
            {
                try
                {
                    rcvBuffer = new Byte[256];
                    bytesRcvd = 0;
                    message = string.Empty;
    
                    //Start listnening/waiting for client to connect
                    while (true)
                    {
                        var client = socket.Accept();
                        ShowMessage("Client with IP " + client.RemoteEndPoint.ToString() + " connected!");
    
                        //Client has connected, keep receiving/displaying data
                        while (true)
                        {
                            SocketError rcvErrorCode;
    
                            bytesRcvd = 0;
                            message = string.Empty;
    
                            bytesRcvd = client.Receive(rcvBuffer, 0, rcvBuffer.Length, SocketFlags.None);
    
                            if (rcvErrorCode != SocketError.Success)
                            {
                                Console.WriteLine("Client with IP " + client.RemoteEndPoint.ToString() + " disconnected!");
                                break;
                            }
    
                            message = Encoding.ASCII.GetString(rcvBuffer, 0, bytesRcvd);
    
                            Console.WriteLine(DateTime.Now.ToLongTimeString() + " - " + message);
                        }
                    }
                }
                catch (Exception)
                {
                    throw;
                }
            }
    
            private static void ShowMessage(string message)
            {
                Console.WriteLine(message);
                Console.WriteLine();
            }
        }
    }
    

    【讨论】:

    • 您的体验似乎不包括正确的 TCP 行为 - 通过返回零的 Read 发出 TCP 连接正常关闭的信号 - 这告诉您连接已关闭。只有当您再次尝试对该套接字执行某些操作时,才会引发该异常,并且它基本上是在告诉您您做错了。
    • 是的,你是对的。我已经调整了代码。谢谢你的评论,我学到了一些东西。
    猜你喜欢
    • 1970-01-01
    • 2017-08-14
    • 1970-01-01
    • 2018-05-16
    • 1970-01-01
    • 2018-09-08
    • 2021-10-26
    • 2013-04-01
    • 2017-07-01
    相关资源
    最近更新 更多