草庐IT

sockets - 从半开套接字读取

coder 2023-09-18 原文

我正在尝试连接到 Apple Push Notification Service,该服务使用基于受 TLS(或 SSL)保护的 TCP 的简单二进制协议(protocol)。该协议(protocol)表明,当遇到错误时(大约有 10 种明确定义的错误条件)APNS 将发回错误响应,然后关闭连接。这导致套接字半关闭,因为远程对等方关闭了套接字。我可以看到它正常关闭,因为 APNS 使用 tcpdump 发送 FIN 和 RST。

在所有错误情况中,我可以在发送前处理大部分错误并进行验证。失败的情况是当通知发送到无效的设备 token 时,由于 token 可能格式错误,因此无法轻松处理。 token 是不透明的 32 字节值,由 APNS 提供给设备,然后向我注册。我无法知道它在提交给我的服务时是否有效。据推测,APNS 以某种方式对 token 进行校验和,以便他们可以快速对 token 进行快速验证。

无论如何,

我做了我认为正确的事:-

a.  open socket
b.  try writing
c.  if write failed, read the error response

不幸的是,这似乎不起作用。我认为 APNS 正在发送错误响应,但我没有正确读回它,或者我没有正确设置套接字。我尝试了以下技术:-

  1. 每套接字使用一个单独的线程,每 5 毫秒左右尝试读取响应(如果有)。
  2. 在写入失败后使用阻塞读取。
  3. 在远程断开连接后使用最终读取。

我已经在 Windows 上使用 C# + .NET 4.5 并在 Linux 上使用 Java 1.7 进行了尝试。在任何一种情况下,我似乎都没有收到错误响应,并且套接字指示没有数据可供读取。

这些操作系统和/或框架是否支持半封闭套接字?似乎没有任何迹象表明这两种方式。

我知道我设置的方式是正确的,因为如果我使用一个有效的 token 和一个有效的通知,那些就会被传送。

作为对其中一条评论的回应,我正在使用增强的通知格式,因此响应应该来自 APNS。

这是我的 C# 代码:-

X509Certificate certificate = 
new X509Certificate(@"Foo.cer", "password");
X509CertificateCollection collection = new X509CertificateCollection();
collection.Add(certificate);

Socket socket = 
   new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect("gateway.sandbox.push.apple.com", 2195);

NetworkStream stream = 
   new NetworkStream(socket, System.IO.FileAccess.ReadWrite, false);
stream.ReadTimeout = 1000;
stream.WriteTimeout = 1000;

sslStream = 
  new SslStream(stream, true, 
     new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
sslStream.AuthenticateAsClient("gateway.sandbox.push.apple.com", collection,
                               SslProtocols.Default, false);
sslStream.ReadTimeout = 10000;
sslStream.WriteTimeout = 1000;

// Task rdr = Task.Factory.StartNew(this.reader);
// rdr is used for parallel read of socket sleeping 5ms between each read.
// Not used now but another alternative that was tried.

Random r = new Random(DateTime.Now.Second);
byte[] buffer = new byte[32];
r.NextBytes(buffer);
byte[] resp = new byte[6];

String erroneousToken = toHex(buffer);

TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
int timestamp  = (int) t.TotalSeconds;

try
{
    for (int i = 0; i < 1000; ++i)
    {
        // build the notification; format is published in APNS docs.
        var not = new ApplicationNotificationBuilder().withToken(buffer).withPayload(
             @'{"aps": {"alert":"foo","sound":"default","badge":1}}').withExpiration(
                timestamp).withIdentifier(i+1).build();

        sslStream.Write(buffer);
        sslStream.Flush();
        Console.Out.WriteLine("Sent message # " + i);

        int rd = sslStream.Read(resp, 0, 6);

        if (rd > 0)
        {
            Console.Out.WriteLine("Found response: " + rd);
            break;
        }

        // doesn't really matter how fast or how slow we send
        Thread.Sleep(500);
    }
}
catch (Exception ex)
{
    Console.Out.WriteLine("Failed to write ...");

    int rd = sslStream.Read(resp, 0, 6);

    if (rd > 0)
    {
        Console.Out.WriteLine("Found response: " + rd); ;
    }
}

// rdr.Wait(); change to non-infinite timeout to allow error reader to terminate

最佳答案

我在 Java 中为 APNS 实现了服务器端,并且在可靠地读取错误响应时遇到了问题(意思是 - 永远不会错过任何错误响应),但我确实设法获得了错误响应。

可以看到这个related question , 尽管它没有足够的答案。

如果您从未设法读取错误响应,那么您的代码一定有问题。

  1. 虽然不是 100% 可靠,但使用单独的线程阅读对我有用。
  2. 在写入失败后使用阻塞读取 - 这是 Apple 建议的做法,但它并不总是有效。有可能您发送了 100 条消息,而第一条消息的 token 无效,只有在第 100 条消息之后您才会收到写入失败。此时从套接字读取错误响应有时为时已晚。
  3. 我不确定你的意思。

如果你想保证错误响应的读取有效,你应该尝试在每次写入后读取,并有足够的超时时间。当然,这对于在生产中使用是不切实际的(因为它非常慢),但您可以使用它来验证您读取和解析错误响应的代码是否正确。您还可以使用它来遍历您拥有的所有设备 token ,并找到所有无效的设备 token ,以清理您的数据库。

您没有发布任何代码,所以我不知道您使用什么二进制格式向 APNS 发送消息。如果您使用的是简单格式(以 0 字节开头且没有消息 ID),您将不会收到来自 Apple 的任何响应。

关于sockets - 从半开套接字读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19988513/

有关sockets - 从半开套接字读取的更多相关文章

  1. ruby-openid:执行发现时未设置@socket - 2

    我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

  2. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  3. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  4. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  5. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  6. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  7. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  8. ruby - 是否可以在不实际发送或读取数据的情况下查明 ruby​​ 套接字是否处于 ESTABLISHED 或 CLOSE_WAIT 状态? - 2

    s=Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)s.connect(Socket.pack_sockaddr_in('port','hostname'))ssl=OpenSSL::SSL::SSLSocket.new(s,sslcert)ssl.connect从这里开始,如果ssl连接和底层套接字仍然是ESTABLISHED,或者它是否在默认值7200之后进入CLOSE_WAIT,我想检查一个线程几秒钟甚至更糟的是在实际上不需要.write()或.read()的情况下关闭。是用select()、IO.select()还是其他方法完成

  9. ruby-on-rails - Ruby 的 'open_uri' 是否在读取或失败后可靠地关闭套接字? - 2

    一段时间以来,我一直在使用open_uri下拉ftp路径作为数据源,但突然发现我几乎连续不断地收到“530抱歉,允许的最大客户端数(95)已经连接。”我不确定我的代码是否有问题,或者是否是其他人在访问服务器,不幸的是,我无法真正确定谁有问题。本质上,我正在读取FTPURI:defself.read_uri(uri)beginuri=open(uri).readuri=="Error"?nil:urirescueOpenURI::HTTPErrornilendend我猜我需要在这里添加一些额外的错误处理代码...我想确保我采取一切预防措施来关闭所有连接,这样我的连接就不是问题所在,但是我

  10. ruby - 如何从 ARGF 读取 csv - 2

    在Ruby1.9中,我如何从ARGF中读取CSV?我尝试了以下方法,但没有打印任何内容:require'csv'CSV(ARGF).readdo|row|prowendhttp://www.ruby-doc.org/core-1.9.3/ARGF.htmlhttp://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV.html 最佳答案 如果你想偷懒你可以试试:CSV.new(ARGF.file).eachdo|row|...end来源:http://www.ruby-doc.org/std

随机推荐