Related
I have one simple udp server written in gen_server behaviour. When I run it, and try to send message by using gen_udp:send, the server replies nothing, seems like the udp server didn't accept packet successfully. Here is my code
gen_udp_server.erl:
-module(gen_udp_server).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-export([start_link/1]).
-define(SERVER, ?MODULE).
-record(state, {socket,
port,
local_ip,
broad_ip}).
start_link(Port) ->
{ok, Socket} = gen_udp:open(Port, [binary,
{active, false},
{reuseaddr, true}]),
gen_server:start_link(?MODULE, [Socket, Port], []).
init([Socket, Port]) ->
{ok, #state{socket = Socket, port = Port}}.
handle_cast(_Request, State) ->
{noreply, State}.
handle_call(_Request, _From, State) ->
{noreply, State}.
handle_info({udp, _Socket, _Addr, _Port, Data}, #state{socket = Socket} = State) ->
inet:setopts(Socket, [{active, once}]),
io:format("Server received data ~p from socket ~p~n", [Data, Socket]),
{ok, State}.
terminate(_Reason, {socket = LSocket}) ->
gen_udp:close(LSocket).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
Starting server on server 192.168.146.129: gen_udp_server:start_link(10000).
Sending message from 192.168.146.128:
{ok, Socket} = gen_udp:open(4399, [binary, {active, false}]).
gen_udp:send(Socket, {192,168,146,129}, 10000, "hello").
The udp server should print some message when it receives packets, but my one failed. Can anyone help me?
handle_info() deals with unknown messages (i.e. unhandled messages) that arrive in the gen_server's mailbox. But when a process opens a socket in passive mode: {active, false}, messages sent to the socket do not land in the process's mailbox. Instead, the process has to manually read messages from the socket by calling gen_udp:recv(Socket, Length). After all, the whole point of creating a passive socket is to keep messages from flooding the process's mailbox. As a result, handle_info() doesn't get called when a client sends a message to a passive socket.
Furthermore, because gen_server is event driven you need to call gen_udp:recv(Socket, Length) in response to some event. For instance, you could define the server functions:
process_message() ->
gen_server:cast(?MODULE, process_msg).
handle_cast(process_msg, #state{socket=Socket} = State) ->
Data = gen_udp:recv(Socket, 0),
io:format("Server received data ~p from socket ~p~n", [Data, Socket]),
{noreply, State}.
Then you need someone to periodically call process_message(). The following seems to work:
start() ->
io:format("start~n"),
{ok, Server} = gen_server:start_link({local,?MODULE}, ?MODULE, [], []),
Poller = spawn(?MODULE, poll, []), %%<***** HERE *****
io:format("Server: ~w~nPoller: ~w~n", [Server,Poller]).
...
...
handle_cast(process_msg, #state{socket=Socket} = State) ->
case gen_udp:recv(Socket, 10000, 500) of
{error, timeout} -> %%Timeout message.
ok;
{error, Error} ->
io:format("Error: ~p~n", [Error]);
Data ->
io:format("Server received data ~p from socket ~p~n", [Data, Socket])
end,
{noreply, State}.
poll() ->
timer:sleep(1000),
process_message(),
poll().
As for the Length in the recv(), I'm not sure what you're supposed to specify: I tried 0, 2, and 10,000, and I couldn't discern a difference.
Here's my client:
client() ->
Port = 15000,
{ok, Socket} = gen_udp:open(0, [binary, {active, false}]),
gen_udp:send(Socket, "localhost", Port, "hello").
Note that open(0, ....) instructs erlang to open any free port (the client and the server cannot open the same port if running on the same computer--contrary to what you need with a gen_tcp socket). However, gen_udp:send() must specify the same port that the server opened. Also, the atom localhost and the list "localhost" both work for me.
Complete server code:
-module(s2).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([start/0, process_message/0, poll/0]).
-record(state, {socket,
port,
local_ip,
broad_ip}).
%%======== PASSIVE SOCKET: {active,false} ===========
%% External interface:
start() ->
io:format("start~n"),
{ok, Server} = gen_server:start_link({local,?MODULE}, ?MODULE, [], []),
Poller = spawn(?MODULE, poll, []),
io:format("Server: ~w~nPoller: ~w~n", [Server,Poller]).
process_message() ->
gen_server:cast(?MODULE, process_msg).
poll() ->
timer:sleep(1000),
process_message(),
poll().
%%Internal server methods:
init([]) ->
Port = 15000,
{ok, Socket} = gen_udp:open(Port, [binary,
{active, false},
{reuseaddr, true}]),
{ok, #state{socket = Socket, port = Port}}.
handle_cast(process_msg, #state{socket=Socket} = State) ->
case gen_udp:recv(Socket, 10000, 500) of
{error, timeout} -> %%Timeout message.
ok;
{error, Error} ->
io:format("Error: ~p~n", [Error]);
Data ->
io:format("Server received data ~p from socket ~p~n", [Data, Socket])
end,
{noreply, State}.
handle_call(_Request, _From, State) ->
{noreply, State}.
handle_info(Msg, State) ->
io:format("Msg: ~w, State:~w~n", [Msg, State]),
{noreply, State}.
terminate(_Reason, #state{socket = LSocket}) ->
gen_udp:close(LSocket).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
In the shell:
shell #1---
$ erl
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2 (abort with ^G)
1> c(s2).
{ok,s2}
2> s2:start().
start
Server: <0.64.0>
Poller: <0.65.0>
ok
shell #2--
$ erl
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2 (abort with ^G)
1> c(c2).
{ok,c2}
2> c2:client().
ok
shell #1--
Server received data {ok,{{127,0,0,1},61841,<<"hello">>}} from socket #Port<0.2110>
3>
shell #2--
3> c2:client().
ok
4>
shell #1--
Server received data {ok,{{127,0,0,1},63983,<<"hello">>}} from socket #Port<0.2110>
3>
I am making an example when using gen_sctp create soscket and associates in server-client model.
In server side:
{ok,serverSocket} = gen_sctp:open(1234,[{ip,{127,0,0,1}},{reuseaddr,true},{active,true}]).
ok = gen_sctp:listen(S,true).
In client side:
{ok,Client} = gen_sctp:open(1243,[{ip,{127,0,0,1}},{reuseaddr,true}]).
{ok,Ass} = gen_sctp:connect(S,{127,0,0,1},1234,[{active,true}]).
And then client send message to server by send/4:
gen_sctp:send(S,Ass,2,<<"hellooooo">>).
And message receive in server side:
{sctp,#Port<0.6126>,
{127,0,0,1},
1243,
{[{sctp_sndrcvinfo,2,1,[],0,0,0,1409953138,0,18}],
<<"hellooooo">>}}
So how can server can reply message to client by send/3?
Thanks and Best Regards,
Tran.
gen_sctp:send/3 is like gen_sctp:send/4 but you can set more flags and options. You have already used gen_sctp:send/4 in client code (while you messed around with client and server sockets):
{ok, Assoc} = gen_sctp:connect(ClientSocket, {127,0,0,1}, 1234,[{active,true}]).
gen_sctp:send(ClientSocket, Assoc, 2, <<"hellooooo">>).
And Assoc is of sctp_assoc_change record type while gen_sctp:send/4 looks just for assoc_id if you provide sctp_assoc_change. So currently providing #sctp_assoc_change{} or just association id behave exactly same.
And how one can find out association id of client in server? It's provided in message which server received:
{sctp,#Port<0.6126>,
{127,0,0,1},
1243,
{[{sctp_sndrcvinfo,2,1,[],0,0,0,1409953138,0,18}],
<<"hellooooo">>}}
#sctp_sdnrcvinfo{} record has fields telling association id and stream number which data is received from. You can get current association id from assoc_id field and pass it to another gen_sctp:send/4:
gen_sctp:send(ServerSocket, AssocID, 2, <<"welcome!">>).
Stream number of 2 probably won't make it fail because by default gen_sctp:open makes 10 incoming and outgoing streams, but you can safely provide 0 as stream number.
Here is an example of sending and receiving data with sctp:
#!/usr/bin/escript
-include_lib("kernel/include/inet_sctp.hrl").
server_loop(Socket) ->
receive
{sctp, Socket, _FromIP, _FromPort, {[#sctp_sndrcvinfo{assoc_id=AssocID}],
Payload}} ->
gen_sctp:send(Socket, #sctp_sndrcvinfo{assoc_id=AssocID, stream=0},
<<"pong">>),
% or less complex gen_sctp:send/4
gen_sctp:send(Socket, AssocID, 0, <<"pong">>);
Rest ->
io:format("server got unhandled message ~w~n", [Rest])
end,
server_loop(Socket).
create_server_socket() ->
{ok, Socket} = gen_sctp:open(1234, [{ip,{127,0,0,1}}, {reuseaddr,true},
{active,true}]),
gen_sctp:listen(Socket, true),
{ok, Socket}.
run_server() ->
Spawner = self(),
spawn_link(fun() ->
{ok, Socket} = create_server_socket(), Spawner ! ready, server_loop(Socket)
end),
receive
ready ->
io:format("server is up~n"),
ok
after 100 ->
throw(server_timeout)
end.
ping_server() ->
{ok, Socket} = gen_sctp:open(1243, [{ip,{127,0,0,1}}, {reuseaddr, true}]),
{ok, AssocChange} = gen_sctp:connect(Socket, {127,0,0,1}, 1234, [{active, true}]),
gen_sctp:send(Socket, AssocChange, 2, <<"ping">>),
receive
{sctp, Socket, _FromIP, _FromPort, {[#sctp_sndrcvinfo{}], Payload}} ->
io:format("client got payload ~p~n", [Payload])
after 2000 ->
throw(client_timeout)
end.
main([]) ->
run_server(),
ping_server().
Writing a very simple TCP server for starters, but for the life of me, gen_tcp:accept in my 'accept_loop' function keeps returning {error, closed} immediately when it's called. So far I've been able to get everything else working thus far, but this one is really stumped me. Wondering if I fell into a beginner trap.
Anyway here is the code.
-module(doom_server).
-behaviour(gen_server).
-export([start_link/1, handle_client/1, accept_loop/1, accept/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(TCP_OPTIONS, ).
-record(server_state, {
port,
ip=any,
timeout=10,
lsocket=null}).
start_link(_Args) ->
gen_server:start_link(?MODULE, _Args, []).
init(_Args) ->
% Get configuration from doom.config and create a new server state.
Port = proplists:get_value(port, _Args),
BindAddress = proplists:get_value(bind_address, _Args),
Timeout = proplists:get_value(timeout, _Args),
case gen_tcp:listen(Port, [binary,
{packet, 0},
{active, false},
{reuseaddr, true}]) of
{ok, ListenSocket} ->
% Construct our server state.
NewState = #server_state {
lsocket = ListenSocket,
port = Port,
ip=BindAddress,
timeout=Timeout
},
{ok, accept(NewState)};
{error, Reason} ->
io:format("Failed: ~w~n", [Reason]),
{stop, Reason}
end.
handle_cast({accepted, _Pid}, State=#server_state{}) ->
{noreply, accept(State)}.
accept_loop({Server, ListenSocket, Timeout}) ->
case gen_tcp:accept(ListenSocket, Timeout) of
{ok, Socket} ->
% Let the server spawn a new process and replace this loop
gen_server:cast(Server, {accepted, self()}),
handle_client(Socket);
{error, Type} ->
io:format("Error: ~w~n", [Type])
end.
% To be more robust we should be using spawn_link and trapping exits
accept(State = #server_state{lsocket=ListenSocket, timeout=Timeout}) ->
proc_lib:spawn(?MODULE, accept_loop, [{self(), ListenSocket, Timeout}]),
State.
handle_client(Socket)->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
gen_tcp:send(Socket, Data),
handle_client(Socket);
{error, closed} ->
ok
end.
% We get compile warnings from gen_server unless we define these
handle_call(_Msg, _Caller, State) -> {noreply, State}.
handle_info(_Message, Library) -> {noreply, Library}.
terminate(_Reason, _Library) -> ok.
code_change(_OldVersion, Library, _Extra) -> {ok, Library}.
I've ran erlang:port_info(ListenSocket) just after the gen_tcp:listen and inside the accept_loop. Both output the same data, and if I put gen_tcp:accept inside the main thread or right after the gen_tcp:listen it works as expected. Am I doing something wrong when I spawn a new process?
P.S I'm using this tutorial as point of reference: http://20bits.com/article/erlang-a-generalized-tcp-server
I Wrote this module for a simple server program that receives a request and send a response to client. It works fine But when if recompile this module while a client connected, the client disconnects and if i reconnect client to this server and send a request, there is no response. Is there any idea?!
Here is my module:
-module(controller).
-export([start/1, loop/1, response/2]).
-include_lib("types.hrl").
-define(END_CHAR, "$").
-spec start(Port) -> no_return() when
Port :: char().
-spec loop(Listen) -> no_return() when
Listen :: port().
-spec handler(Socket) -> no_return() when
Socket :: port().
-spec response(HandlerPID, Data) -> {send_msg, Msg} when
HandlerPID :: pid(),
Data :: string(),
Msg :: response().
start(Port) ->
{ok, Listen} = gen_tcp:listen(Port, [{active, once}]),
spawn(?MODULE, loop, [Listen]).
loop(Listen) ->
{ok, Socket} = gen_tcp:accept(Listen),
spawn(?MODULE, loop, [Listen]),
handler(Socket).
handler(Socket) ->
receive
{tcp, Socket, Data} ->
%io:format("recv ~p~n", [Data]),
spawn(?MODULE, response, [self(), Data]),
inet:setopts(Socket, [{active, once}]),
handler(Socket);
{tcp_closed, Socket} ->
%io:format("disconnected~n", []),
gen_tcp:close(Socket);
{send_msg, Msg} ->
gen_tcp:send(Socket, lists:flatten(io_lib:format("~p", [Msg])) ++ ?END_CHAR),
handler(Socket)
end.
response(PID, Data) ->
[Req|Args] = string:tokens(Data, ?END_CHAR),
{ReqPID, ReqRef} = spawn_monitor(view, request, [list_to_atom(Req), self(), Args]),
receive
{'DOWN', ReqRef, process, ReqPID, {function_clause, _}} -> PID ! {send_msg, invalid_request};
{'DOWN', ReqRef, process, ReqPID, {{case_clause, _}, _}} -> PID ! {send_msg, bad_args};
{'DOWN', ReqRef, process, ReqPID, {{badmatch, _}, _}} -> PID ! {send_msg, bad_args};
Resp -> PID ! {send_msg, Resp}
end.
Note
{tcp_closed, Socket} ->
%io:format("disconnected~n", []),
gen_tcp:close(Socket);
in handler. It doesn't call handler again, it just returns and so the server process stops.
Your loop function name is also misleading, handler is the actual loop.
I would recommend to take a look at Ranch - A socket acceptor pool for the TCP connections.
I did a simple translation of the example C# code here:
let socket_CreateBindListen (sv_ : string) (pt_ : int) : Socket option =
let (he : IPHostEntry) = Dns.GetHostEntry(sv_)
let rsock, ipe =
he.AddressList
|> Seq.map (fun s ->
let (ipe2 : IPEndPoint) = new IPEndPoint (s, pt_)
let (rsock2 : Socket) = new Socket (ipe2.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
rsock2.Connect ipe2 ///// <---- No connection could be made because the target machine actively refused it
rsock2, ipe2)
|> Seq.find (fun (s, t) -> s.Connected)
try
rsock.Bind ipe
rsock.Listen 10
printfn "%s now listening on port %d" sv_ pt_
Some (rsock)
with
| _ ->
printfn "Failed to connect to %s on port %d" sv_ pt_
None
socket_CreateBindListen "127.0.0.1" 50000
I made sure to first open port 50000 on my machine for TCP for both inbound and outbound connections. (I even tried disabling the firewall completely.) Nothing seems to be working. I keep getting the error message:
No connection could be made because the target machine actively refused it.
I am using Windows 8. I would really appreciate some pointers on what else I might try.
Thanks in advance for your time.
EDIT
I hope I am not violating any of StackOverflow's posting policies by blogging about my progress here.
I have updated the code to the following:
let socket_CreateBindListen (sv_ : string) (pt_ : int) : Socket option =
let (he : IPHostEntry) = Dns.GetHostEntry(sv_)
let rsock, ipe =
he.AddressList
|> Seq.map (fun s ->
let (ipe2 : IPEndPoint) = new IPEndPoint (s, pt_)
let (rsock2 : Socket) = new Socket (ipe2.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
try
rsock2.Connect ipe2
rsock2, ipe2
with
| _ ->
null, null)
|> Seq.find (fun (s, t) -> (s <> null) && (s.Connected))
try
rsock.Bind ipe
rsock.Listen sbl
printfn "%s now listening on port %d" sv_ pt_
Some (rsock)
with
| _ ->
printfn "Failed to connect to %s on port %d" sv_ pt_
None
Now I am getting the following error:
KeyNotFoundException was unhandled
An unhandled exception of type 'System.Collections.Generic.KeyNotFoundException' occurred in FSharp.Core.dll
I have looked extensively on Google and Bing, without any luck.
EDIT 2
As requested by Jack P., here is the output from netstat -a, as well as what happens when the binary is executed:
PS C:\Users\shredderroy> netstat -a
Active Connections
Proto Local Address Foreign Address State
TCP 0.0.0.0:80 SPEEDMACHINE:0 LISTENING
TCP 0.0.0.0:135 SPEEDMACHINE:0 LISTENING
TCP 0.0.0.0:445 SPEEDMACHINE:0 LISTENING
TCP 0.0.0.0:2179 SPEEDMACHINE:0 LISTENING
TCP 0.0.0.0:49152 SPEEDMACHINE:0 LISTENING
TCP 0.0.0.0:49153 SPEEDMACHINE:0 LISTENING
TCP 0.0.0.0:49154 SPEEDMACHINE:0 LISTENING
TCP 0.0.0.0:49155 SPEEDMACHINE:0 LISTENING
TCP 0.0.0.0:49156 SPEEDMACHINE:0 LISTENING
TCP 192.168.0.139:139 SPEEDMACHINE:0 LISTENING
TCP 192.168.0.139:49159 bn1wns2011708:https ESTABLISHED
TCP 192.168.0.139:49167 vc-in-f108:imaps ESTABLISHED
TCP 192.168.0.139:49171 vc-in-f108:imaps ESTABLISHED
TCP 192.168.0.139:49239 a23-67-250-112:http CLOSE_WAIT
TCP [::]:80 SPEEDMACHINE:0 LISTENING
TCP [::]:135 SPEEDMACHINE:0 LISTENING
TCP [::]:445 SPEEDMACHINE:0 LISTENING
TCP [::]:2179 SPEEDMACHINE:0 LISTENING
TCP [::]:49152 SPEEDMACHINE:0 LISTENING
TCP [::]:49153 SPEEDMACHINE:0 LISTENING
TCP [::]:49154 SPEEDMACHINE:0 LISTENING
TCP [::]:49155 SPEEDMACHINE:0 LISTENING
TCP [::]:49156 SPEEDMACHINE:0 LISTENING
UDP 0.0.0.0:500 *:*
UDP 0.0.0.0:4500 *:*
UDP 0.0.0.0:5355 *:*
UDP 127.0.0.1:53194 *:*
UDP 127.0.0.1:60316 *:*
UDP 127.0.0.1:61644 *:*
UDP 192.168.0.139:137 *:*
UDP 192.168.0.139:138 *:*
UDP [::]:500 *:*
UDP [::]:4500 *:*
UDP [::]:5355 *:*
UDP [fe80::b193:4f6:d053:6324%20]:546 *:*
PS C:\Users\shredderroy> cd "C:\Users\shredderroy\Documents\Visual Studio 2012\Projects\FSharp\SocketTests_Server\SocketTests_Server\bin\Debug
"
PS C:\Users\shredderroy\Documents\Visual Studio 2012\Projects\FSharp\SocketTests_Server\SocketTests_Server\bin\Debug> .\SocketTests_Server.exe
Unhandled Exception: System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it [::1]:50000
at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.Sockets.Socket.Connect(EndPoint remoteEP)
at SocketTests_Server.TestModule_1.heo#22.Invoke(IPAddress s) in C:\Users\shredderroy\Documents\Visual Studio 2012\Projects\FSharp\SocketTests_Server\SocketTests_Server\TestModule_1.fs:line 25 at Microsoft.FSharp.Collections.SeqModule.TryFind[T](FSharpFunc`2 predicate, IEnumerable`1 source)
at SocketTests_Server.TestModule_1.socket_CreateBindListen_1(String sv_, Int32 pt_) in C:\Users\shredderroy\Documents\Visual Studio
2012\Projects\FSharp\SocketTests_Server\SocketTests_Server\TestModule_1.fs:line 20
at SocketTests_Server.Program.Main(String[] args) in C:\Users\shredderroy\Documents\Visual Studio 2012\Projects\FSharp\SocketTests_Server\SocketTests_Server\Program.fs:line 9
PS C:\Users\shredderroy\Documents\Visual Studio 2012\Projects\FSharp\SocketTests_Server\SocketTests_Server\bin\Debug>
I am going to keep trying. So far I have added my programme, SocketTests_Server.exe, to the list of allowed programmes in Windows Firewall, opened the relevant port for inbound and outbound connections, disabled the firewall completely--all to no avail.
I think what is happening is that you are connecting twice, one of the addresses connects fine. Your code then keeps looking through the remaining addresses and tries to connect again.
Your code is lacking the equivalent of break in the C# code.
Some possible solutions:
use a while loop instead of Seq.map
Use a reference variable inside the map
Compress all the code into Seq.find and remove the call to map (probably the most elegant)
I translated the code from the page you linked -- it compiles, though I haven't tried running it.
open System
open System.Text
open System.Net
open System.Net.Sockets
let connectSocket (server : string, port : int) : Socket option =
// Get host related information.
let hostEntry = Dns.GetHostEntry server
// Loop through the AddressList to obtain the supported AddressFamily. This is to avoid
// an exception that occurs when the host IP Address is not compatible with the address family
// (typical in the IPv6 case).
hostEntry.AddressList
|> Seq.tryPick (fun address ->
let ipe2 = IPEndPoint (address, port)
let rsock2 = new Socket (ipe2.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
rsock2.Connect ipe2
if rsock2.Connected then
Some rsock2
else None)
// This method requests the home page content for the specified server.
let socketSendReceive (server : string, port : int) : string =
let request =
sprintf "GET / HTTP/1.1\r\nHost: %s\r\nConnection: Close\r\n\r\n" server
let bytesSent =
Encoding.ASCII.GetBytes request
let bytesReceived = Array.zeroCreate 256
// Create a socket connection with the specified server and port.
match connectSocket (server, port) with
| None ->
"Connection failed"
| Some s ->
// Send request to the server.
s.Send (bytesSent, Array.length bytesSent, SocketFlags.None)
|> ignore
// Receive the server home page content.
let mutable bytes = 0
let mutable page =
sprintf "Default HTML page on %s:\r\n" server
// The following will block until the page is transmitted.
while bytes > 0 do
bytes <- s.Receive (bytesReceived, Array.length bytesReceived, SocketFlags.None)
page <- page + Encoding.ASCII.GetString (bytesReceived, 0, bytes)
page
//
let main args =
let port = 80
let host =
if Array.isEmpty args then
// If no server name is passed as argument to this program,
// use the current host name as the default.
Dns.GetHostName ()
else
args.[0]
socketSendReceive (host, port)
|> Console.WriteLine
EDIT: I did a little digging, and "actively refused" is the key to that error message, according to: No connection could be made because the target machine actively refused it. In short, double-check that the server you're trying to connect to is set up properly; when the connection is actively refused, there's no firewall problem -- the server is actually rejecting your connection.
If you're absolutely certain the server is set up correctly, there's one last thing you could try. According to Socket Connection Is Actively Refused (MSDN Forums), you may need to use the IPAddress.NetworkToHostOrder Method to set the port value correctly for the IPEndPoint. In that case, just add this as the first line in my connectSocket function (above):
let port = IPAddress.NetworkToHostOrder port
EDIT 2: Looking at the netstat information you posted -- there's no server LISTENING on port 50000. If you're sure the server software is set up correctly, make sure it's using the port you want (and not just choosing a random port).
First, I would like to thank everyone, in particular, #Jack P. and #John Palmer, for reading my code and offering very good and instructive suggestions. Your guidance is greatly appreciated.
I have circumvented the problem described above by using the TcpListener and TcpClient classes instead. So I thought I would write the simple F# translations of this server function and this client function, which together implement the server and client. Hopefully, this sample code will save other beginners some time.
Server
let connectAndListen () : unit =
let (laddr : IPAddress) = IPAddress.Parse ("127.0.0.1")
let lpt = 32000
let (svr : TcpListener) = new TcpListener (laddr, lpt)
try
svr.Start ()
let (bta : byte[]) = Array.create 256 (byte (0))
while true do
printfn "Waiting for a connection..."
let (cl : TcpClient) = svr.AcceptTcpClient ()
printfn "Connected"
let (ns : NetworkStream) = cl.GetStream ()
let mutable (i : int) = ns.Read (bta, 0, (Array.length bta))
while i <> 0 do
let (dat : string) = System.Text.Encoding.ASCII.GetString (bta, 0, i)
printfn "Received: %s" dat
let (msg : byte[]) = System.Text.Encoding.ASCII.GetBytes (dat.ToUpper ())
ns.Write (msg, 0, (Array.length msg))
printfn "Sent: %s" (dat.ToUpper ())
i <- ns.Read (bta, 0, (Array.length bta))
cl.Close ()
with
| excp ->
printfn "%s" excp.Message
svr.Stop ()
Client
let connect (sv_ : string) (pt_ : int) (msg_ : string) : unit =
try
let cl = new TcpClient (sv_, pt_)
let (dat : byte[]) = System.Text.Encoding.ASCII.GetBytes msg_
let (st : NetworkStream) = cl.GetStream ()
st.Write (dat, 0, (Array.length dat))
printfn "Sent: %s" msg_
let (rdat : byte[]) = Array.create 256 (byte (0))
let (i : int) = st.Read (rdat, 0, (Array.length rdat))
let (rsp : string) = System.Text.Encoding.ASCII.GetString (rdat, 0, i)
printfn "Received: %s" rsp
st.Close ()
st.Dispose ()
with
| :? ArgumentNullException as excp ->
printfn "ArgumentNullException encountered: %s" excp.Message
| :? SocketException as excp ->
printfn "SocketException encountered: %s" excp.Message
printfn "Press ENTER to continue"
Console.ReadLine () |> ignore
Place the code in separate projects, compile and run them. They work on my machine.