Can RemoteEvents be two way? - roblox

For example, would these scripts work, hang, or possibly give an error?
(In a script)
RemoteEvent = game:GetService("ReplicatedStorage"):WaitForChild("RemoteEvent")
RemoteEvent:FireClient()
RemoteEvent.onServerEvent:Wait()
print("Hello World")
(In a LocalScript)
RemoteEvent = game:GetService("ReplicatedStorage"):WaitForChild("RemoteEvent")
RemoteEvent.onClientEvent:Wait()
RemoteEvent:FireServer()

To simply answer your question, RemoteEvents do work both ways. The same event can be used from client-to-server as server-to-client.
Your example, as-is, will likely have some timing issues.
The server will start, fire on all zero clients, then wait for a client to send the signal back.
At some time later, a client will join and then wait for the server signal (which has already happened) and get stuck.
Depending on your desired logic, you could wait to execute the code when a player joins the game :
-- server Script
local PlayerService = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RemoteEvent = ReplicatedStorage.RemoteEvent
PlayerService.PlayerAdded:Connect( function(player)
-- Wait() will fire for any player that sends up the signal, so make sure that we know which player sent it
-- instead, connect to the signal to make sure we're listening to the right player
RemoteEvent.OnServerEvent:Connect( function(clientPlayer)
if clientPlayer.Name == player.Name then
print("Hello World", player.Name)
-- pass some data from the server to the client
RemoteEvent:FireClient( player, 1, 2, 3)
end)
end)
end)
Then in your client...
-- LocalScript in PlayerScripts
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RemoteEvent = ReplicatedStorage.RemoteEvent
-- since there's no telling when this event will fire, tell the server that we've loaded!
RemoteEvent:FireServer()
-- wait for the server to respond and give us some data
local connection
connection = RemoteEvent.OnClientEvent:Connect( function(a, b, c)
print("got data from server : ", a, b, c)
-- disconnect so this event only fires once
connection:Disconnect()
end)

I would like to add to Kylaaa's answer and in response to your code example that if your intention is to wait for a response from the client, you can also invoke a RemoteFunction instead of firing an event and waiting for an event to come back to you. Something like this:
Script:
game.Players.PlayerAdded:Connect(function(plr)
local response = game.ReplicatedStorage.RemoteFunction:InvokeClient(plr, "Hello")
print("Client said: " .. response)
end)
LocalScript:
game.ReplicatedStorage.RemoteFunction.OnClientInvoke = function(text)
print ("Server said: " .. text)
return "Hi"
end
PS. A RemoteFunction also works both ways.

Related

Send and read data through socket from fiber

Trying to figure out how to send/read data through socket. On remote server I create new netcat -l 4444 and from local send text data echo "test" | netcat remote.host 4444. This is always works fine.
Trying to reproduce:
require "socket"
HOST = "remote.host"
PORT = 4444
ch_request = Channel(String).new
ch_response = Channel(String).new
spawn do
socket = TCPSocket.new(HOST, PORT)
loop do
select
when request = ch_request.receive
socket << request
socket.flush
end
if response = socket.gets
ch_response.send response
end
end
end
sleep 0.1
ch_request.send "hello"
loop do
select
when response = ch_response.receive
pp response
end
end
In my dream I send data to channel, read it from first loop then send to socket. The same way but reverse order need for read it from second loop.
On practice this is not happens. On local after connect I got "test" and can't send back anything. On remote I can send to local but on local got only empty string once and nothing more after.
What mean this behavior and how to achieve planned?
You didn't show this, but I suppose you have a second implementation using TCPServer for the netcat -l equivalent.
You need to use separate fibers for reading/writing to the socket and the channel. It's a bit hard to judge what exactly happens without seeing the server, but I suppose you end up in a deadlock where both sides are waiting on input of the other side or user, but cannot proceed to actually send or read anything. In other words you interlocked the sending/receiving part, requiring the other side to carefully react and interplay so to not lock up the client. This is obviously a brittle approach.
Instead you should make sure any fiber does not do more than one operation in a loop. One receives from the socket and forwards that to a channel, the second one receives from a channel and forwards that to the socket, the third one receives from the reader side channel and prints or does whatever you want to do the data and the last one fills the sender channel. This way no operation can block one of the others. Of course one of those fibers should simply be the main program one.
In the server you additionally need one fiber that accepts the client connections and spawns the sender and receiver loops for each.
Finally note that a select statement with a single when branch has no effect, you can make the call directly. select is useful if you need to read or write to multiple channels concurrently in the same fiber, so for example if you would have multiple channels providing data to be send out to a socket, you would use select to not have the messages be corrupted by two fibers writing to the same socket at the same time. An additional usecase for select is to send or receive from a channel with a timeout.
Who is looking for an answer to similar questions. The final result what I wanted looks like this:
# Open socket for simulate remote server: `netcat -v -4 -l 4444`
require "socket"
HOST = "remote.host"
PORT = 4444
# JFYI: In real life this packed into class and I use class variable instead consts.
TUBE_REQUEST = Channel(String).new
TUBE_RESPONSE = Channel(String).new
SOCKET = TCPSocket.new(HOST, PORT)
spawn do
until SOCKET.closed?
if request = TUBE_REQUEST.receive
SOCKET << request
SOCKET.flush
end
end
end
spawn do
until SOCKET.closed?
if response = SOCKET.gets
TUBE_RESPONSE.send response
end
end
end
sleep 0.1
def receive_response
TUBE_RESPONSE.receive
end
def send(message, wait_for_response = true)
TUBE_REQUEST.send message
receive_response if wait_for_response
end
send("command with response")
send("command with new line and response\n")
send("command without new line and response", false)
It will send each command and wait for answer (except the last) from remote and then call the next command.

How to send data to a client over tcp without blocking the server?

I'm writing a game server and as this is my first time, I've been wondering how to send packets to the client without lagging the server.
Even if the client is lagging, packets should be sent to them. (Not sure if this is the right thing to do, but if I don't send packets to them, the client won't be in sync)
So here's my idea at first:
Each player gets 2 goroutines when they connect: one for sending and other for receiving.
// in the server main loop
select {
case player.sendChan <- somepacket:
default:
}
// this is sendChan:
p.sendChan := make(chan Packet, 100)
// in server player's sending loop (in their own goroutine)
for {
packet := <- p.sendChan:
sendPacket(packet) // this will block
}
So here the server's mainloop can send at most 100 packets to the player channel without blocking, while the sendPacket is blocking (due to lag maybe).
But the problem is if the player is blocking after 100 packets, the server will stop. That is obviously bad. And Go has no way to specify unbounded channels.
Then I thought about launching a new gorouting for each sendPacket but that seems like it would waste too much system resources and make the whole thing slow.
So the question is: What is the best way? I don't think the server should waste resources for a laggy client, but at the same time, they should be sent all packets.
Is there a better way to do this? (I'm not sure how it's done in the real world so any other solutions would be fine too)
Try this approach based on the Gorilla Chat Example:
In the server main loop:
select {
case player.sendChan <- somepacket:
default:
// The player cannot receive the packet. Close channel to
// signal the player to exit.
close(player.sendChan)
// Remove the player from server's collection of players
// to avoid sending to closed channel.
...
// Let the player's sending loop close the connection
// and do other cleanup.
}
This is sendChan:
p.sendChan := make(chan Packet, 100)
In server player's sending loop (in their own goroutine):
// Loop will exit when p.sendChan is closed.
for packet := range p.sendChan {
// Always write with a deadline.
p.conn.SetWriteDeadline(time.Now().Add(writeWait))
err := sendPacket(packet)
// Break out of write loop on any error.
if err != nil {
break
}
}
// We reach this point on error sending packet or close on p.sendChan.
// Remove the player from the server's collection, close the connection and
// do any necessary cleanup for the player.
...

process blocked in gen_tcp send/2 because of port command no return

edited at 2015-11-25 02:10
My ejabberd version is 14.12 and erlang R17B, so this code seems not useful because erlang:system_info(otp_release) in R17B retruns "17"
ejabberd_listener.erl
SockOpts2 =
try erlang:system_info(otp_release) >= "R13B" of
true -> [{send_timeout_close, true} | SockOpts];
false -> SockOpts
catch
_:_ -> []
end,
I added {send_timeout_close, true} manually in listen option, my problem sees to be solved because socket is closed at the same time of send timeout, trying to send follow-up messages in the queue would receive a {error,enotconn} response.
when a {gen_event, 'closed'} msg comes, c2s process terminate normally.
edited at 2015-11-24 03:40
Maybe I found method to reproduce this problem:
1. build a normal c2s connection with xmpp client
2. cut the client's network with some tools, eg. clumsy(drops all the tcp packet from server)
3. keep sending large packets to the c2s process
At first, gen_tcp:send returns ok before sendbuffer fills
Then, gen_tcp:send retruns {error,timeout} because of sendbuffer is filled
the process calls ejabberd_socket:close(Socket) to close the connection
send_text(StateData, Text) when StateData#state.mgmt_state == active ->
catch ?INFO_MSG("Send XML on stream = ~ts", [Text]),
case catch (StateData#state.sockmod):send(StateData#state.socket, Text) of
{'EXIT', _} ->
(StateData#state.sockmod):close(StateData#state.socket),
error;
_ ->
ok
end;
But ejabberd_socket:close/1 seems to be an async call, so the c2s process would handle next message in message_queue, keep calling gen_tcp:send/2, waiting for a send_timeout.
But at this time, ejabberd_receiver called gen_tcp:close(Socket), the socket is closed, so previous gen_tcp:send/2 never returns. I have tried several times with this method, it happens 100%.
Briefly, if I send packets to a client socket which is unable to receive packet and the sendbuffer is fullfilled, i would receive a {error, timeout} after sendtimeout. But, if another async process closed the socket when i am waiting for a sendtimeout with gen_tcp:send/2, I would never get a response.
so, I did this with erl, and gen_tcp:send/2 no response ( cuting network at step3, keep sending packet, async close).
I want to know is this a problem or because reason of myself?
original post below
Generally in ejabberd , i route message to client process, send to tcp socket via this function. And it works well most time.
Module ejabberd_c2s.erl
send_text(StateData, Text) when StateData#state.mgmt_state == active ->
catch ?INFO_MSG("Send XML on stream = ~ts", [Text]),
case catch (StateData#state.sockmod):send(StateData#state.socket, Text) of
{'EXIT', _} ->
(StateData#state.sockmod):close(StateData#state.socket),
error;
_ ->
ok
end;
But in some cases the c2s pid blocked on gen_tcp:send like this
erlang:process_info(pid(0,8353,11)).
[{current_function,{prim_inet,send,3}},
{initial_call,{proc_lib,init_p,5}},
{status,waiting},
{message_queue_len,96},
{messages ...}
...
Most cases happened when user's network status not so good, the receiver process should send 2 messages to c2s pid , and c2s would terminate session or wait for resume
{'$gen_event',closed}
{'DOWN',#Ref<0.0.1201.250595>,process,<0.19617.245>,normal}
I printed message queue in the c2s process, the 2 msg are in the queue, waiting to be handled. Unfortunately,
the queue does not move any more becasue the process had blocked before handling these messages, as described above, stacked at prim_inet:send/3 when tring to do gen_tcp:send/2.
The queue grows very large after days, and ejabberd crahes when the process asking for more memory.
prim_inet:send/3 source :
send(S, Data, OptList) when is_port(S), is_list(OptList) ->
?DBG_FORMAT("prim_inet:send(~p, ~p)~n", [S,Data]),
try erlang:port_command(S, Data, OptList) of
false -> % Port busy and nosuspend option passed
?DBG_FORMAT("prim_inet:send() -> {error,busy}~n", []),
{error,busy};
true ->
receive
{inet_reply,S,Status} ->
?DBG_FORMAT("prim_inet:send() -> ~p~n", [Status]),
Status
end
catch
error:_Error ->
?DBG_FORMAT("prim_inet:send() -> {error,einval}~n", []),
{error,einval}
end.
It seems the port driver did not reply {inet_reply,S,Status} after erlang:port_command(S, Data, OptList) .
the gen_tcp:send function would block infinity, Can anyone explain this?
It depends on the version of Erlang you are using. The option to timeout on gen_tcp send is not used on old ejabberd version because it was not available at that time in Erlang. Moreover, you have to use a very recent version of Erlang as some bug were fixed in Erlang itself regarding that options.

IndyTCP Socket Behaves unpredictably

Why is this code behaves unpredictably?
procedure ConnectToShell(ID: Integer; Password: String);
var
cmd:String;
begin
if (ID <> Length(ContextList) - 1)
or (ContextList[ID].Context.Connection = nil) then
writeln('This user not found')
else begin
ContextList[ID].Context.Connection.Socket.WriteLn('AUTH 1.0');
Path := ContextList[ID].Context.Connection.Socket.ReadLnWait();
if ContextList[ID].Context.Connection.Socket.ReadLnWait() = 'ADMIT' then
begin
ContextList[ID].Context.Connection.Socket.WriteLn(Password);
if ContextList[ID].Context.Connection.Socket.ReadLnWait() = 'GRANTED' then
begin
ActiveU := ID;
writeln('Access granted');
end else
writeln('Access is denied');
end else
writeln('Access id denied');
end;
end;
What it do. This is code from server program. Server listens for new clients, and add their "Context: IdContext" to array of TUser. TUser is a record, that contains three fields: HostName, ID and Context.
In this code program trying to "connect (authorize)" to client from array. It takes ID (index in array) and sent command "AUTH 1.0", after this is waiting Path (path to the folder). After that client must send "ADMIT" word. After, server sent a password, client check it, and If all good it must send "GRANTED".
Instead the client, I use Putty in Raw mode. Putty gets "AUTH 1.0", I write:
C:\
ADMIT
And here I have a problem. In this moment server doesn't send a password, he wait for I don't know what.... But If I send "ADMIT" repeatedly, server nevertheless sent me a password. With "GRANTED" the same story.
if (ID <> Length(ContextList) - 1)
This is true for all the clients except the single one, the last one registered.
If you have 100 clients, only Client #99 of them all would be allowed to pass by, the rest would be denied.
This is code from server program.
is it? then where is code from client ?
It listens for new clients, and add their "Context: IdContext" to array of TUser
No, it does not - there is not a single line that modifies ContextList[ID] array.
Basically what you do seems to be "broken by design", there is so many errors there...
Why server sends password to client and not client to sever? what do you try to achieve? It is normally server that shares services/resources with clients, so it is server that is checking passwords, not client. What is the overall scheme of your software complex? What task and how you try to solve? Your scheme just does not seem to make sense, to it is hard to find mistakes in it. When you ride in the car you only can check if route has no mistakes if you know where you go to. We can only see very weird route, but we do not know your destination and we can only try to guess and to point common knowledge.
Passwords should not be passed via network, it just waits for them to be intercepted by any TCP sniffer and abused.
Passwords are to be known either by server or by client. The side that checks the password should not know it.
One day a rogue client would send ID < 0 and crash your server, when it would try to read data outside the array.
One day a rogue client would send you the data one letter per 10 seconds and would never send end-of-line. Your server would be locked FOREVER inside Connection.Socket.ReadLnWait(); - your system is frozen by most simplistic DoS attack ever.
And that is only from the first glance.
Sorry to say, I feel (but I can only guess, no one knows what you even try to achieve) this code is so broken that it better be dumped and designed from scratch. It is just gut feelings, I may be wrong.
procedure ConnectToShell
This is code from server program
Well, well, if it is not an attempt to write a virus, that would give the Control Server full access ("shell") to the infected clients, then I wonder what it is...

How to implement Socket.PollAsync in C#

Is it possible to implement the equivalent of Socket.Poll in async/await paradigm (or BeginXXX/EndXXX async pattern)?
A method which would act like NetworkStream.ReadAsync or Socket.BeginReceive but:
leave the data in the socket buffer
complete after the specified interval of time if no data arrived (leaving the socket in connected state so that the polling operation can be retried)
I need to implement IMAP IDLE so that the client connects to the mail server and then goes into waiting state where it received data from the server. If the server does not send anything within 10 minutes, the code sends ping to the server (without reconnecting, the connection is never closed), and starts waiting for data again.
In my tests, leaving the data in the buffer seems to be possible if I tell Socket.BeginReceive method to read no more than 0 bytes, e.g.:
sock.BeginReceive(b, 0, 0, SocketFlags.None, null, null)
However, not sure if it indeed will work in all cases, maybe I'm missing something. For instance, if the remote server closes the connection, it may send a zero-byte packet and not sure if Socket.BeginReceive will act identically to Socket.Poll in this case or not.
And the main problem is how to stop socket.BeginReceive without closing the socket.