【问题标题】:Destination Unreachable (Port Unreachable) using Haskell使用 Haskell 无法到达目的地(Port Unreachable)
【发布时间】:2014-08-05 19:41:33
【问题描述】:

使用Wireshark调试,在localhost上发送UDP包时收到如下错误:

Destination Unreachable (Port Unreachable)

Checksum: 0x0000 (Illegal)

我首先在 10000 - 15000 之间的端口上使用

构建我的服务器
startServer :: Port -> IO Server
startServer port = withSocketsDo $ do
  -- Look up the server address and port information.
  addrs <- getAddrInfo (Just $ defaultHints { addrFlags = [AI_PASSIVE] }) Nothing (Just port)
  let serverAddress = head addrs

  -- Bind to the socket.
  sock <- socket (addrFamily serverAddress) Datagram defaultProtocol
  bindSocket sock (addrAddress serverAddress)

  -- Create the server and run the client send and receive threads.
  clients  <- newMVar $ createEmptyClients
  let server = Server sock port clients
  _ <- forkIO $ forever $ receiveClientJoin server
  return server

我正在监听通过 UDP 连接的新客户端

-- | Connected a client to the server.
receiveClientJoin :: Server -> IO ()
receiveClientJoin server = do
  print "Receiving"
  (msg, _, clSockAddr) <- recvFrom (sSocket server) 4096
  print $ "Server received client join message: " ++ msg

我正在使用客户端连接到服务器

connectToServer port = do
  -- Get the server's address and port information.
  addrInfo <- getAddrInfo Nothing (Just "localhost") (Just port)
  let serverAddr = head addrInfo
  sock  <- socket (addrFamily serverAddr) Datagram defaultProtocol
  sendTo sock "Hello from this client!" (addrAddress serverAddr)

为什么我的客户的数据包没有找到服务器?

【问题讨论】:

  • 你能发布一个完整的编译示例吗?
  • 这可能是 ipv4 与 ipv6 的问题。如果你使用127.0.0.1 而不是 localhost 会发生什么?
  • @ThomasM.DuBuisson 谢谢!更改为127.0.0.1 后即可使用。您能否简要说明一下为什么会出现这个问题?

标签: sockets haskell client-server


【解决方案1】:

问题是您正在侦听 IPv6 地址并尝试连接到 IPv4 地址。这实际上是一个稍微常见的问题。例如,我在使用 commsec 时遇到了这个问题。

考虑一下您发现 AddrInfo 的片段:

import Network.Socket 

main :: IO ()
main = do
  let port = "2474"
  addrs <- getAddrInfo (Just $ defaultHints { addrFlags = [AI_PASSIVE] }) Nothing (Just port)
  let serverAddress = head addrs
  print serverAddress

  addrInfo <- getAddrInfo Nothing (Just "localhost") (Just port)
  let serverAddr = head addrInfo
  print serverAddr

现在输出会因机器而异,但在我的一个同时具有 IPv4 和 IPv6 地址的 CentOS 系统上,输出清楚地显示第二个(连接)地址是 IPv6,而第一个(监听)地址是 IPv4:

AddrInfo {addrFlags = [AI_PASSIVE], addrFamily = AF_INET, addrSocketType = Stream, addrProtocol = 6, addrAddress = 0.0.0.0:2474, addrCanonName = Nothing}
AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET6, addrSocketType = Stream, addrProtocol = 6, addrAddress = [::1]:2474, addrCanonName = Nothing}

一种解决方案是通过提示或地址(例如我的评论中的 IPv4 地址)强制使用特定版本的 IP。提示解决方案可能更可取:

-- For servers:
  addrs <- getAddrInfo (Just defaultHints { addrFamily = AF_INET6
                                          , addrFlags = [AI_PASSIVE] })
                       Nothing (Just port)
-- For clients:
addrInfo <- getAddrInfo (Just defaultHints { addrFamily = AF_INET6 })
                        (Just "localhost") (Just port)

【讨论】:

  • 再次感谢,这是有道理的。
猜你喜欢
  • 2013-09-24
  • 2013-04-21
  • 1970-01-01
  • 2019-11-07
  • 1970-01-01
  • 2020-10-09
  • 2012-07-08
  • 2020-03-26
  • 1970-01-01
相关资源
最近更新 更多