【发布时间】:2016-01-28 15:57:22
【问题描述】:
编辑:
我正在用 Haskell 编写一个 UDP BitTorrent 跟踪器。状态基于将我的数据类型 ServerState 传递到 runUDPServer、acceptConnections、handleConnection 和 handleRequestData 的 STM(两个 TVar 地图)时间>。客户端将请求启动“连接”、宣布或抓取。每次有人向服务器发送消息时,他们都应该收到一条消息。 (协议在这里:http://www.rasterbar.com/products/libtorrent/udp_tracker_protocol.html)。
我将进行一些二进制解析,在 IO monad 中进行一些处理(实际上只是 STM),然后将二进制编码的消息发送回发送者。最初,我在想我可以在它自己的线程中运行这样的每个请求,但我想我可以只分叉几个线程,让它们来完成工作。一个问题可能是整个服务器(所有线程)会被 n 人以非常慢的速度发送 UDP 包阻塞(但实际上这可能是不可能的)。
我想我可以更清楚地定义我的问题:如果我只是分叉 n 个同时运行 handleConnection 的线程,那会以某种方式弄乱套接字吗?另外,(如何)理想情况下,我能否以某种方式为每个收到的数据包生成一个新线程?
我的意思是,当我分叉几个线程并写入标准输出时,输出将是乱码/从单独线程打印的内容之间的混合。 Network.accept 实际上提供了一个句柄,各个线程并不真正需要了解套接字,但我不能使用 accept。 我不会假设同时从多个线程写入套接字是安全的。
{-# LANGUAGE OverloadedStrings #-}
import Control.Exception (bracket)
import qualified Data.ByteString.Char8 as BS
import qualified Network.Socket as S hiding (send, sendTo, recv, recvFrom)
import qualified Network.Socket.ByteString as S
runUDPServer serverState port =
S.withSocketsDo $ bracket (createSocket port) S.close (acceptConnections serverState)
where
createSocket port = do
serverAddr <- fmap head $ S.getAddrInfo
(Just (S.defaultHints {S.addrFlags = [S.AI_PASSIVE]}))
Nothing
(Just port)
socket <- S.socket (S.addrFamily serverAddr) S.Datagram S.defaultProtocol
S.bind socket $ S.addrAddress serverAddr
return socket
acceptConnections serverState socket = do
handleConnection serverState socket
acceptConnections socket
handleConnection serverState socket = do
(requestData, remoteAddress) <- S.recvFrom socket 2048
responseData <- handleRequestData serverState requestData remoteAddress
S.sendTo socket responseData remoteAddress
handleRequestData :: ServerState -> BS.ByteString -> S.SockAddr -> IO BS.ByteString
handleRequestData serverState requestData remoteAddress = do
putStrLn "-----"
putStrLn $ "Received UDP message"
putStrLn $ "Address: " ++ show remoteAddress
-- (left out code here)
return "Dummy ByteString"
如果有任何提示、指点等,我将不胜感激。
【问题讨论】:
-
“我宁愿没有一个线程池,每个线程都运行 recvFrom,但我想我可以。我不确定如何以合理的方式从多个线程写入套接字。 "为什么不?这一切都应该在posix上是原子的,对吧?如果是这样,您可以
replicateM n (forkIO $ forever $ handleConnection socket)。我不知道 UDP 套接字上的争用是否会对性能产生影响。如果是这样,让一个线程读取数据并将其穿梭到阻塞的 FIFO 队列中可能会更快。 -
@jberryman 您可以动态创建线程,而不是线程池,每个连接一个线程。
-
这个问题没有包含足够的信息来提供完整的答案。例如,您想在某个地方收集所有客户提供的信息吗?您需要由客户收集它们吗?您是否确认您当前的方法不够快/“并发”? UDP 是否适合您的用例?
-
@jberryman 如果这在 POSIX 上是原子的,那是个好消息。我试试看。
-
@Zeta 我已经编辑了这个问题,我希望现在更清楚了。是的,我正在收集诸如 IP 地址以及实际请求之类的内容。不,我不确定我是否需要让服务器比现在更快,但是我正在做这个项目来学习如何使用 Haskell 和多线程等,所以我有点想尝试一下方法。 UDP 是我需要支持的协议。
标签: multithreading sockets haskell server udp