修改
我正在Haskell中编写UDP BitTorrent跟踪器。状态基于STM(两个TVar Maps)以我的数据类型ServerState传递到 runUDPServer , acceptConnections , handleConnection 和 handleRequestData 。客户要么开始“连接”,要么宣布或要求。每当有人向服务器发送消息时,他们都应该收到消息。 (协议在这里:http://www.rasterbar.com/products/libtorrent/udp_tracker_protocol.html)。
我将进行一些二进制解析,在IO monad中进行一些处理(实际上只是STM)并将以二进制编码的消息发送回发送方。最初,我认为我可以在自己的线程中运行这样的每个请求,但我想我可以只分叉几个线程让他们做相反的工作。一个问题可能是整个服务器(所有线程)都会被n个人发送UDP包的阻塞非常慢(但可能实际上并不可能)。
我想我可以更明确地定义我的问题:如果我只是分叉所有同时运行handleConnection的线程,那么它会以某种方式搞乱套接字吗?另外,(如何)我可以理想地以某种方式为每个接收的数据包生成一个新线程?
我的意思是,当我分叉几个线程并写入stdout时,输出将是乱码/从单独线程打印的内容之间的混合。 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"
我会非常感谢任何提示,指示等。
答案 0 :(得分:0)
就个人而言,我只有一个线程用于读取,只是在一个循环中调用recv
,然后将请求转移到Chan
然后另一个用于写入,假脱机{{1} }。但是,我确实怀疑,虽然无法确认,您可以在您描述的架构中直接执行多个线程。我认为这可能没问题的原因是IO通过IO管理器进行流量处理,IO管理器处理多路复用。虽然不包含最新的发展,但我认为MIO paper应涵盖目前实施方式的基础知识。
答案 1 :(得分:-1)
你需要学习一些并发兄弟!
您需要学习的第一件事是forkIO :: IO () -> IO ThreadId
。这是所有并发开始的地方。你给它一个IO动作,它启动一个线程来运行那个IO动作,就像魔术师一样!一切,包括你所谈论的accept
事,都可以追溯到forkIO
!由于Haskell数据是不可变的,因此使用起来非常安全(如果你不使用锁,没问题(然后死锁是不可能的)。)
Haskell中学习并发的其余部分是如何使用forkIO
和基于forkIO
的库(以及一些相关的原语)。首先,阅读Control.Concurrent
。然后阅读Parallel and Concurrent Haskell(免费电子书)。要了解本书如何帮助您处理您的情况,请转到Chapter 12。
Haskell 更好地处理并发性比任何命令式和/或不纯的语言更好除非专门为并发而构建(cue downvotes from non-Haskellers)。拥抱它。
好的,为此,你会使用某种渠道。实际上MVar
就足够了(只要你的写作速度比你制作的速度快)。见this。如果您的写作速度超过您的写作速度,请参阅this。
stm
包具有类似的结构。