编辑:写在不同的线程的插座(在Haskell并发UDP服务器)
我写在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包的人阻止(但实际上这可能不是这样)。
我想我可以更清楚地定义我的问题:如果我只是fork了所有同时运行handleConnection的线程,那么是否会以某种方式与套接字混淆?另外,我怎么能理想地为每个接收到的数据包产生一个新的线程?
我的意思是,当我分叉几个线程并写入标准输出时,输出将混乱在从单独线程打印的内容之间。 Network.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
@jberryman您可以改为动态创建线程,而不是一组线程,每个连接一个线程。 – PyRulez
此问题未包含足够的信息以获得完整答案。例如,你想收集所有客户提供的信息吗?你需要通过客户收集它们吗?你是否确认你目前的方法不够快/“并发”? UDP是否适合您的用例? – Zeta