Home > Back-end >  Sending large content over Network
Sending large content over Network

Time:04-07

so I want to send huge stuff via tcp, therefore I use the Network.Socket library...

First of all I have a wrapper data type (Example!):

data Message =
        List [Int]
    |   String String
    |   END
    deriving stock Generic
    deriving anyclass Binary
    deriving Show
    deriving Eq

so a normal work-pipeline would be:

--client
let bytestring = encode $ List [1..1000000] -- Lazzy ByteString
send socket $ toStrict bytestring
--server
Just msg <- recv socket 1024 
print $ decode $ fromStrict msg

the problem with this is: I get the following error:

Data.Binary.Get.runGet at position 1017: not enough bytes CallStack (from HasCallStack): error, called at libraries/binary/src/Data/Binary/Get.hs:351:5 in binary-0.8.8.0:Data.Binary.Get ClientProjekt: Network.Socket.sendBuf: resource vanished (Broken pipe)

Allthough

  • just sending/reciving and printing without decoding on the server works fine.
  • encoding -> toStrict -> fromStrict -> decoding: without sending over tcp works fine.

so what does the network do with the bytestring which breaks the decoding functionality?

CodePudding user response:

The main problem is that recv socket 1024 requests only 1024 bytes from the socket, so the received message is being truncated. Also send doesn't actually guarantee that the full bytestring will be sent. You want to use sendAll instead.

I'd advise using sendAll and getContents from Network.Socket.ByteString.Lazy to avoid converting your lazy bytestring to and from a strict bytestring and to save you the trouble of manually writing a recv loop. A minimal server might look like this:

{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}

import GHC.Generics
import Data.Binary
import Network.Socket
import Network.Socket.ByteString.Lazy
import Prelude hiding (getContents)

data Message = List [Int]
  deriving (Generic, Binary)

main = do
  let hints = defaultHints
        { addrFlags = [AI_NUMERICHOST, AI_NUMERICSERV]
        , addrSocketType = Stream }
  addr:_ <- getAddrInfo (Just hints) (Just "127.0.0.1") (Just "8080")
  s <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
  bind s (addrAddress addr)
  listen s 1
  (s', _) <- accept s
  List xs <- decode <$> getContents s'
  close s'
  print $ sum xs

and a matching minimal client might look like this:

{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}

import GHC.Generics
import Data.Binary
import Network.Socket
import Network.Socket.ByteString.Lazy

data Message = List [Int]
  deriving (Generic, Binary)

main = do
  let hints = defaultHints
        { addrFlags = [AI_NUMERICHOST, AI_NUMERICSERV]
        , addrSocketType = Stream }
  addr:_ <- getAddrInfo (Just hints) (Just "127.0.0.1") (Just "8080")
  s <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
  connect s (addrAddress addr)
  sendAll s (encode (List [1..1000000]))
  close s
  • Related