Multithreading 从不同线程写入套接字(Haskell中的并发UDP服务器)

Multithreading 从不同线程写入套接字(Haskell中的并发UDP服务器),multithreading,sockets,haskell,server,udp,Multithreading,Sockets,Haskell,Server,Udp,编辑: 我正在Haskell中编写UDP BitTorrent跟踪器。该状态基于STM(两个TVar映射),在我的数据类型ServerState中传递到runUDPServer、acceptConnections、handleConnection和HandlerRequestData。客户端将请求启动“连接”、宣布或刮取。每次有人向服务器发送消息时,他们都应该收到一条消息。(协议在这里:) 我将进行一些二进制解析,在IO monad中进行一些处理(实际上只是STM),然后将一条二进制编码的消息发

编辑:

我正在Haskell中编写UDP BitTorrent跟踪器。该状态基于STM(两个TVar映射),在我的数据类型ServerState中传递到runUDPServer、acceptConnections、handleConnection和HandlerRequestData。客户端将请求启动“连接”、宣布或刮取。每次有人向服务器发送消息时,他们都应该收到一条消息。(协议在这里:)

我将进行一些二进制解析,在IO monad中进行一些处理(实际上只是STM),然后将一条二进制编码的消息发送回发送方。起初,我认为我可以在自己的线程中运行每个这样的请求,但我想我可以只分叉几个线程,让它们来完成工作。这样做的一个问题可能是,整个服务器(所有线程)将被n个发送UDP包的人阻塞,速度非常慢(但可能实际上不可能)

我想我可以这样更清楚地定义我的问题:如果我只分叉n个线程,所有线程都同时运行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"
{-#语言重载字符串}
导入控制。异常(括号)
将符合条件的Data.ByteString.Char8作为BS导入
将符合条件的Network.Socket作为S隐藏导入(发送、发送到、recv、recvFrom)
将合格的Network.Socket.ByteString作为S导入
runUDPServer服务器状态端口=
S.withSocketsDo$括号(createSocket端口)S.close(acceptConnections服务器状态)
哪里
createSocket端口=do

serverAddr你需要学习一些并发性,兄弟

你需要学习的第一件事是。这就是所有并发性开始的地方。你给它一个IO动作,它就会启动一个线程来运行IO动作,就像魔术师一样!所有的事情,包括你刚才谈论的
accept
事情,都要回到
forkIO
!因为Haskell数据是不可变的,所以使用它是完全安全的(如果不使用锁,就不会有问题(然后死锁就不可能了)

在Haskell中学习并发的其余部分是如何使用
forkIO
,以及基于
forkIO
(和一些相关原语)的库。首先,阅读。然后阅读(免费电子书)。要了解这本书对您的处境有何帮助,请访问

Haskell在处理并发性方面比任何命令式和/或不纯语言都要好得多,除非专门为并发性而构建(暗示非Haskeller的反对票)。拥抱它


好的,对于这个,你可以使用某种渠道<代码>MVar实际上已经足够了(只要您的编写速度比生成速度快)。看见如果您的生产速度快于您的写作速度,请参阅


stm
包具有类似的结构。

您需要了解一些并发性,兄弟

你需要学习的第一件事是。这就是所有并发性开始的地方。你给它一个IO动作,它就会启动一个线程来运行IO动作,就像魔术师一样!所有的事情,包括你刚才谈论的
accept
事情,都要回到
forkIO
!因为Haskell数据是不可变的,所以使用它是完全安全的(如果不使用锁,就不会有问题(然后死锁就不可能了)

在Haskell中学习并发的其余部分是如何使用
forkIO
,以及基于
forkIO
(和一些相关原语)的库。首先,阅读。然后阅读(免费电子书)。要了解这本书对您的处境有何帮助,请访问

Haskell在处理并发性方面比任何命令式和/或不纯语言都要好得多,除非专门为并发性而构建(暗示非Haskeller的反对票)。拥抱它


好的,对于这个,你可以使用某种渠道<代码>MVar实际上已经足够了(只要您的编写速度比生成速度快)。看见如果您的生产速度快于您的写作速度,请参阅


stm
包有类似的结构。

我个人会有一个读取线程,它只是在一个循环中调用
recv
,然后将请求抛给一个
Chan
,然后再抛给另一个进行写入,将
Chan
甩出。然而,我确实怀疑,尽管无法确认,您可能有多个线程直接在所描述的体系结构中执行此操作。我认为这样可以的原因是IO通过处理多路复用的IO管理器进行通信。虽然不包含最新的开发,但我认为应该涵盖当前实现方式的基本知识。

我个人会有一个读取线程,它只是在循环中调用
recv
,然后将请求抛给
Chan
,然后再抛给另一个进行写入,将
Chan
。然而,我确实怀疑,尽管无法确认,您可能有多个线程直接在所描述的体系结构中执行此操作。我认为这样可以的原因是IO通过处理多路复用的IO管理器进行通信。虽然不包含最新的开发,但我认为应该涵盖当前实现方式的基本知识。

“我不希望每个线程都运行recvFrom,但我想我可以。我不确定如何以合理的方式从多个线程写入套接字。”为什么不呢?在posix上应该是原子的,对吗?如果是的话,你