Send tuple through tcp socket in Erlang - sockets

I'm trying to send a tuple using a tcp socket, I'm using gen_tcp:send(Socket, {elem1,elem2}) but I'm receiving this error, "bad value on output port 'tcp_inet' " can anyone tell me how can I send a tuple through a socket?
Thanks for your replies.

The second argument must be a Packet:
Packet = string() | binary() | HttpPacket
HttpPacket = HttpRequest
| HttpResponse
| HttpHeader
| http_eoh
| HttpError
HttpRequest = {http_request, HttpMethod, HttpUri, HttpVersion}
HttpResponse =
{http_response, HttpVersion, integer(), HttpString}
HttpHeader =
{http_header,
integer(),
HttpField,
Reserved :: term(),
Value :: HttpString}
...
in your case {elem1,elem2} does match any of these type and you get the error. The usual way to send an arbitrary term is to serialize it first:term_to_binary(YourTerm)
and deserialize it when you receive it : binary_to_term(ReceivedBinary)

Related

TcpStream::connect - match arms have incompatible type

I'm trying to write basic networking code in Rust, but running into an error I don't understand. I have been using match statements to error check everything in Rust so far, but when I try to error check TcpStream::connect(), I get an unexpected error:
My code:
use std::net::TcpStream;
fn main() {
let mut server = match TcpStream::connect("127.0.0.1:23456"){
Ok(x) => x,
Err(x) => println!("Could not connect to server: {x}"),
};
}
The compiler error:
error[E0308]: `match` arms have incompatible types
--> src/main.rs:8:19
|
6 | let mut server = match TcpStream::connect("127.0.0.1:23456"){
| ______________________-
7 | | Ok(x) => x,
| | - this is found to be of type `TcpStream`
8 | | Err(x) => println!("Could not connect to server: {x}"),
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
expected struct `TcpStream`, found `()`
9 | | };
| |_____- `match` arms have incompatible types
|
Every other time I use a match statement it allows me to destructure the Result type into a return value in the OK case (as above), or an error string in the error case.
It is the case that TcpStream::connect() returns a TcpStream, but why is the compiler insisting that the error case also needs to return a TcpStream?
The value of the match statement gets assigned to server.
However, both branches of your match statement return a different type.
Ok(x) returns x, which is of type TcpStream.
Err(x) returns the result of println!(), which has the return value ().
TcpStream and () are incompatible.
Just think about the code after the match statement. What should the server variable be? You don't stop the execution when the error happens, you simply println!() and continue. So something has to be written to the server variable.
If you panic!() instead of println!(), meaning print and abort, then it compiles because it knows that the Err case won't continue afterwards:
use std::net::TcpStream;
fn main() {
let mut server = match TcpStream::connect("127.0.0.1:23456") {
Ok(x) => x,
Err(x) => panic!("Could not connect to server: {x}"),
};
}
thread 'main' panicked at 'Could not connect to server: Connection refused (os error 111)', src/main.rs:6:19
That said, if this is your desired behavior, there is a short form for it:
use std::net::TcpStream;
fn main() {
let mut server = TcpStream::connect("127.0.0.1:23456").expect("Could not connect to server");
}
thread 'main' panicked at 'Could not connect to server: Os { code: 111, kind: ConnectionRefused, message: "Connection refused" }', src/main.rs:4:60
I recommend handling the error properly, though.
There are many ways to do that, so this part will be opinionated.
I personally like miette (an alternative would be anyhow):
use miette::{Context, IntoDiagnostic};
use std::net::TcpStream;
fn main() -> miette::Result<()> {
let mut _server = TcpStream::connect("127.0.0.1:23456")
.into_diagnostic()
.wrap_err("Could not connect to server.")?;
// Do something with server
Ok(())
}
Error:
× Could not connect to server.
╰─▶ Connection refused (os error 111)
Your server variable can't be both TcpStream and the unit type ()
Try propagating the error instead.
Here is an example from the official documentation:
use std::io::prelude::*;
use std::net::TcpStream;
fn main() -> std::io::Result<()> {
let mut stream = TcpStream::connect("127.0.0.1:34254")?;
stream.write(&[1])?;
stream.read(&mut [0; 128])?;
Ok(())
}
If it can't connect to the server, it emits an error message
Error: Os { code: 111, kind: ConnectionRefused, message: "Connection refused" }
And sets a non-zero exit code
$ echo $?
1

How to identify that a specific text was sent by socket, to activate a command, with if ==?

I have a code that sends a pre-established text by socket. If the default text sent is "hi", I want that in addition to receiving the hi, print that the text sent was hi. I'm a beginner, I tried doing if msg == hi: but it didn't work. How do I do it please?
Server
from socket import *
host = gethostname()
port = 8889
print(f'HOST: {host} , PORT {port}')
serv = socket(AF_INET, SOCK_STREAM)
serv.bind((host, port))
serv.listen(5)
con, adr = serv.accept()
msg = con.recv(1024)
print(msg.decode())
if msg == "hi":
print('A mensagem foi hi')
Client
from socket import *
host = gethostname()
port = 8889
cli = socket(AF_INET, SOCK_STREAM)
cli.connect((host, port))
msg = ("hi")
cli.send(msg.encode())
You aren't actually updating your msg variable to the decoded string, you are just printing the string. When comparing msg to "hi", msg still contains encoded bytes. You should add
msg = msg.decode()
or you might as well put it earlier:
msg = con.recv(1024).decode()

Ktor Secure Sockets (SSL/TLS) windows example?

I was trying to follow the ktor documentation for Raw Sockets and in specific the part related to secured sockets (https://ktor.io/servers/raw-sockets.html):
runBlocking {
val socket = aSocket(ActorSelectorManager(ioCoroutineDispatcher)).tcp().connect(InetSocketAddress("google.com", 443)).tls()
val w = socket.openWriteChannel(autoFlush = false)
w.write("GET / HTTP/1.1\r\n")
w.write("Host: google.com\r\n")
w.write("\r\n")
w.flush()
val r = socket.openReadChannel()
println(r.readUTF8Line())
}
You can adjust a few optional parameters for the TLS connection:
suspend fun Socket.tls(
trustManager: X509TrustManager? = null,
randomAlgorithm: String = "NativePRNGNonBlocking",
serverName: String? = null,
coroutineContext: CoroutineContext = ioCoroutineDispatcher
): Socket
But the NativePRNGNonBlocking SecureRandom algorithm is not available on Windows, so my only option was to use SHA1PRNG (https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#SecureRandomImp)
This is the code I'm running to connect to a listening socket :
socket = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().connect(InetSocketAddress(host, port))
.tls(Dispatchers.IO, randomAlgorithm = "SHA1PRNG")
Unfortunately, I always receive the same error: "Channel was closed"
If I remove tls, keeping only the raw socket:
socket = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().connect(InetSocketAddress(host, port))
Everything works as expected.
Does anyone has used Ktor Secure Sockets in Windows ? (Unfortunately, Ktor's documentation still has a long way to go).
Thanks,
J

Sending data to socket (after reading) only occurs when the socket connection ends

I'm trying to write a very basic webserver in Haskell. This is my code:
{-# LANGUAGE OverloadedStrings #-}
import Network (withSocketsDo, listenOn, PortID(..))
import Network.Socket (Socket, accept, close, setSocketOption, SocketOption(..))
import Network.Socket.ByteString (send, sendAll, recv)
import Control.Concurrent.Async (async)
import Control.Monad (forever)
import Data.ByteString.Char8 (unpack)
import Request
main = withSocketsDo $ do
sock <- listenOn $ PortNumber 3000
putStrLn "Listening on port 3000..."
forever $ do
(conn, _) <- accept sock
async $ handleAccept conn
handleAccept :: Socket -> IO ()
handleAccept sock = do
putStrLn $ "Connected!"
rawReq <- recv sock 4096
let req = parseRawRequest $ unpack rawReq -- returns Maybe Request
putStrLn $ show req
handleRequest sock req
handleRequest :: Socket -> Maybe Request -> IO ()
handleRequest sock Nothing = do
putStrLn "Closing..."
handleRequest sock req = do
sendAll sock "In handleRequest!" -- Doesn't appear until server is killed.
This is what I expected to happen:
Start server.
"Listening on port 3000..." is printed on server-side.
Do curl localhost:3000
"Connected!" is printed server-side.
The request is printed server-side.
"In handleRequest!" is printed.
What actually happens:
Start server.
"Listening on port 3000..." is printed on server-side.
Do curl localhost:3000
"Connected!" is printed server-side.
The request is printed server-side.
I wait patiently
I kill the server with CTRL+C
"In handleRequest!" prints client-side.
I suspect this has something to do with possible laziness in recv, although I use the value immediately afterwards (I parse the raw request into a Request type), so theoretically it should be evaluated.
If I put sendAll sock "Yadda yadda at the end of handleAccept, everything works fine. It's when I move this behaviour into a new function, handleRequest, that things go wonky.
Any thoughts? I'm new-ish to Haskell, so I'd appreciate any comments on the issue, or my code generally.
Cheers.
EDIT:
This is super weird! I "fixed" it, but I have no idea why this occurs.
This is the line that only appeared after I killed the server:
handleRequest sock req = do
sendAll sock "In handleRequest!" -- Doesn't appear until server is killed.
If I intentionally close the socket after sending, it works:
handleRequest sock req = do
sendAll sock "In handleRequest!" -- Now appears without killing the server
close sock
So it sends when the connection is closed. This is consistent with previous behaviour, since the connection automatically closes when the server is killed.
Now for the confusing bit. If I replace it with:
handleRequest sock req = do
sendAll sock "In handleRequest!\n" -- Works perfect
This works without closing the connection! It does what I expected, just by adding a newline. Why does this occur?
What on earth? Is it a printing problem with my terminal, not the code? (OSX iTerm2)
EDIT 2:
Was asked to provide the code for my Request module:
import Data.List (isInfixOf)
import Data.List.Split (splitOn)
data RequestType = GET | PUT
deriving Show
data Request =
Request {
reqType :: RequestType,
path :: String,
options :: [(String, String)]
} deriving Show
-- Turn a raw HTTP request into a request
-- object.
parseRawRequest :: String -> Maybe Request
parseRawRequest rawReq =
Request <$> parseRawRequestType rawReq
<*> parseRawRequestPath rawReq
<*> parseRawRequestOps rawReq
-- Turn an (entire) raw HTTP request into just
-- the request type.
parseRawRequestType :: String -> Maybe RequestType
parseRawRequestType rawReq =
case typ of
"GET" -> Just GET
"PUT" -> Just PUT
_ -> Nothing
where typ = (head . words . head . lines) rawReq
-- Turn an (entire) raw HTTP request into just
-- the path.
parseRawRequestPath :: String -> Maybe String
parseRawRequestPath = Just . (!! 1) . words . head . lines
-- Turn an (entire) raw HTTP request into just
-- a lookup table of their options.
parseRawRequestOps :: String -> Maybe [(String, String)]
parseRawRequestOps rawReq = Just [("One", "Two")] -- Test impl
I have one answer and one suggestion.
The suggestion is for you to turn off the naggle algorithm after accept:
setSocketOption conn NoDelay 1
The answer is that your sendAll is sending data but curl is not printing it. You can confirm this with netcat, for example. I commented out your Nothing case so that no matter what I typed in netcat I was sure to get the "In handleRequest!" message back:
server:
% ghc so.hs && ./so
Listening on port 3000...
Connected!
Nothing
client:
% nc localhost 3000
test ; My input, with a newline
In handleRequest! ; Printed out, no newline
Alternatively, you can use curl's -N option to disable buffering.
% curl -N localhost:3000
In handleRequest!

HTTP4S client. How to get the exact request and response body

I am writing a small http4s client
val client = SimpleHttp1Client()
val uri = Uri.fromString(requestUrl).valueOr(throw _)
val task = POST(uri, UrlForm("username" -> userName, "password" -> password)).map{request => println("request: " + request.body)}
try {
val response = client.expect[String](task).unsafePerformSync
println("token: " + response)
response
} catch {
case e: Exception => println(e.getMessage);"BadToken"
}
The output is like
[info] Running com.researchnow.nova.shield.NovaShieldSetup
[info] Emit(Vector(ByteVector(44 bytes, 0x757365726e616d653d616268737269766173746176612670617373776f72643d41726)))
[info] Failed: unexpected HTTP status: 400 Bad Request
[info] token: BadToken
How to convert the binary request body to String? I want to see the body and headers in clear text.
I had a conversation with the http4s team on gitter and found the response. since gitter talk is not returned by google I am putting the answer here
val loggedReq = req.copy(body = request.body.observe(scalaz.stream.io.stdOutBytes))
println(loggedReq)
this prints all the headers. If we do something with the loggedReq then we get the entire body which is posted
loggedReq.as[String].run