我有一个UDP服务器,它反映了它收到的每条ping消息(我认为这很有效)。我是客户端,我想做两件事:
似乎由于UDP的性质或forkIO
事件,我的客户端代码过早结束/根本没有计数。
另外,我很惊讶地看到函数tryOnePing
返回Int 4的250倍。为什么会这样?
main = withSocketsDo $ do
s <- socket AF_INET Datagram defaultProtocol
hostAddr <- inet_addr host
thread <- forkIO $ receiveMessages s
-- is there any better way to eg to run that in parallel and make sure
-- that sending/receiving are asynchronous?
-- forM_ [0 .. 10000] $ \i -> do
-- sendTo s "ping" (SockAddrInet port hostAddr)
-- actually this would be preferred since I can discard the Int 4 that
-- it returns but forM or forM_ are out of scope here?
let tryOnePing i = sendTo s "ping" (SockAddrInet port hostAddr)
pings <- mapM tryOnePing [0 .. 1000]
let c = length $ filter (\x -> x==4) pings
-- killThread thread
-- took that out to make sure the function receiveMessages does not
-- end prematurely. still seems that it does
sClose s
print c
-- return()
receiveMessages :: Socket -> IO ()
receiveMessages socket = forever $ do
-- also tried here forM etc. instead of forever but no joy
let recOnePing i = recv socket 1024
msg <- mapM recOnePing [0 .. 1000]
let r = length $ filter (\x -> x=="PING") msg
print r
print "END"
答案 0 :(得分:6)
这里的主要问题是当主线程完成时,所有其他线程都会自动被杀死。您必须让主线程等待receiveMessages thread
,否则它将在收到任何回复之前完成所有可能性。一种简单的方法是使用MVar
。
MVar
是一个同步的单元格,可以为空或只保留一个值。如果当前线程尝试从空MVar
获取或插入完整线程,则将阻止当前线程。
在这种情况下,我们不关心价值本身,因此我们只会在其中存储()
。
我们将从MVar
开始。然后主线程将分离接收器线程,发送所有数据包,并尝试从MVar
获取值。
import Control.Concurrent.MVar
main = withSocketsDo $ do
-- prepare socket, same as before
done <- newEmptyMVar
-- we need to pass the MVar to the receiver thread so that
-- it can use it to signal us when it's done
forkIO $ receiveMessages sock done
-- send pings, same as before
takeMVar done -- blocks until receiver thread is done
在接收方线程中,我们会收到所有消息,然后在()
中添加MVar
来表示我们已接收完毕。
receiveMessages socket done = do
-- receive messages, same as before
putMVar done () -- allows the main thread to be unblocked
这解决了主要问题,程序在我的Ubuntu笔记本电脑上正常运行,但还有一些你想要处理的事情。
sendTo
不保证将发送整个字符串。您必须检查返回值以查看已发送的内容,如果不是全部发送,则重试。如果发送缓冲区已满,即使是"ping"
之类的短消息,也会发生这种情况。
recv
需要连接套接字。您需要使用recvFrom
代替。 (虽然它仍然可以在我的电脑上运行,原因不明)。
打印到标准输出不同步,因此您可能希望对其进行更改,以便MVar
将用于传达接收到的数据包数而不仅仅是()
。这样,您可以从主线程执行所有输出。或者,使用另一个MVar
作为互斥锁来控制对标准输出的访问。
最后,我建议您仔细阅读Network.Socket,Control.Concurrent和Control.Concurrent.MVar的文档。我的大部分答案都是根据那里发现的信息拼凑而成的。