Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Async socket connection

by ljamison (Sexton)
on Feb 08, 2017 at 03:53 UTC ( [id://1181364]=perlquestion: print w/replies, xml ) Need Help??

ljamison has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks! I'm in need of some assistance with a socket server setup that I haven't been able to really get a lot of help with since I started. I've had previous code before but none of it really did the job so instead I'd like to give a specifications list to see what kind of ideas are out there that perhaps I haven't thought of yet.

Requirements:

  • The connection is a TCP socket connection
  • PC1 connects on port 8000
  • PC2 listens on port 8010
  • Haven't been able to find a solution like it, but would ideally like both client and server in same file.
  • Asynchronous communication (see Table 1 below)
  • PC1 and PC2 always need to be listening for messages from the other
  • PC1 represents the logic I have to create
  • PC2 represents a fully functioning server that has already been created (I have to create the PC1 to communicate correctly with pre-determined messages and message formats)
  • The trickiest part I've run into is with the RESP and STAT messages because of the connection needing to be closed after the RESP is sent from PC2, listen for a STAT from PC2, then reply



Table 1 PC1 PC2 --- --- ADD --> <-- RESP (handshake) # STAT messages are sent arbitrarily <-- STAT (status message) RESP -->


Any assistance or guidance on this would be very much welcomed and even more appreciated!

Replies are listed 'Best First'.
Re: Async socket connection
by Corion (Patriarch) on Feb 08, 2017 at 08:07 UTC

    I don't understand your specification.

    You talk about "client" and "server", but later on talk about "PC1" and "PC2". Is "PC1" a client or a server, or should "PC1" be both, client and server? (then, it would be neither)

    If you mean that PC1 should have one socket for incoming connections on port 8010 and one port for outgoing connections to port 8000, this still doesn't show me what the TCP connections between the two machines PC1 and PC2 would look like. Maybe you can draw a diagram of the machines, open listening ports and which machine connects to which machine from/to which port.

    I don't understand the flow of your ADD, STAT and RESP messages. I think it would make things clearer if you show not only which machine sends them but also over which TCP connection(s) the messages are sent. Also, you mention closing connections, which makes the protocol sound even weirder, but maybe showing a sequence diagram here makes things clearer for me.

    As for implementing the communication, I am somewhat fond of using Future nowadays as that allows me to mostly avoid the callback hell.

    A bit more low-level would be AnyEvent or Net::Async or maybe POE for handling the multiplexing of sockets.

    If you want to implement your own multiplexing (which I don't really recommend except as a learning exercise), have a look at IO::Select and then select.

      Hello, thank you for your input and sorry for the confusion! I have updated the specification to hopefully better reflect the situation.

      1. I have duplicated the diagram which specifies the relationship here
      2. The message in the diagram would be the ADD command
      3. The acknowledgement in the diagram would be the RESP reply
      4. The STAT is only sent FROM PC2 TO PC1
      5. The RECEIVE of both PC1 and PC2 are required to always be listening

      Currently I'm able to run both independently (i.e. server.pl and client.pl) but the BIGGEST problem I face is for both server and client of PC1 to interact with one another which I have not been able to accomplish yet. Right now, they function correctly independently. Ideally, I would like to compress both client and server into one script though from my investigations it appears that would not be as easy (however I've had limited success using Brian D Foy's Modulino pattern).

      Thank you again for your input and any additional comments/suggestions would be welcomed!

        For completeness, I'll replicate the drawing here, just in case that other resource goes away:

        (port 8000) "SEND" PC1 (port 8010) "RECEIVE" | | v ADD ^ | | (port 8000) "RECEIVE" PC2 (port 8010) "SEND"
        (port 8000) "SEND" PC1 (port 8010) "RECEIVE" | | ^ ACK ^ | | (port 8000) "RECEIVE" PC2 (port 8010) "SEND"
        (port 8000) "SEND" PC1 (port 8010) "RECEIVE" | | v ^ STAT | | (port 8000) "RECEIVE" PC2 (port 8010) "SEND"

        From that diagram resp. my interpretation of it, you don't have "SEND" and "RECEIVE" sockets, but some sockets that are listening for and accepting incoming socket connections, and some sockets that are used to talk to the listening sockets. The listening sockets are listening on port 8000 (PC2) and port 8010 (PC1).

        You also use the terms "server" and "client", even though that distinction seems somewhat arbitrary to me because both machines seemm to be able to initiate connections to each other.

        I'm not clear on whether your current two programs client.pl and server.pl are running on both machines or whether client.pl only runs on PC1 and server.pl only runs on PC2.

        I would conceptually split up the "STAT" part separate from the "ADD" part. You can combine the server parts of both if the general format of an incoming message is the same.

        The easiest approach to combine the logic of the listeners and the senders in a single program is to launch for threads, two listener threads and two sender threads, or just two threads if you only need a listener on port 8000 and a sender to port 8010 (PC2) or a listener on port 8010 and a sender to port 8000 (PC1).

        The next step would be to look at AnyEvent or IO::Select to manage the writing to sockets and the reading from sockets. AnyEvent gives you callbacks for each socket when it's ready to write more data or when it's ready to read more data.

        Personally, I would go with the AnyEvent approach because that avoids race conditions.

Re: Async socket connection
by tybalt89 (Monsignor) on Feb 10, 2017 at 02:44 UTC

    Here's a guess.

    With an argument of "pc1" it runs as pc1, otherwise as pc2.
    $otherport needs to be changed to your systems, I have only one machine so I had to runs both programs on the same machine.

    This should be done with an async library like Corion suggests.

    P.S. There is a lot of cheating going on in this, which wouldn't be there with a proper async lib.

    #!/usr/bin/perl # http://perlmonks.org/?node_id=1181364 use strict; use warnings; use IO::Socket; use IO::Select; my $listenport; my $othersocket; my $otherport; my $sendinterval; my $pc1 = @ARGV && $ARGV[0] eq 'pc1'; if( $pc1 ) { $listenport = 8010; $sendinterval = 5; $otherport = 'localhost:8000'; } else { $listenport = 8000; $sendinterval = 7; $otherport = 'localhost:8010'; } my $ls = IO::Socket::INET->new( Listen => 10, LocalPort => $listenport, Reuse => 1, ) or die "$@ opening listen socket on port $listenport"; warn "listening on $listenport\n"; my $sel = IO::Select->new($ls); my $nextsend = $sendinterval + time; while(1) { my $delta = $nextsend - time; if( $delta <= 0 ) { if( not defined $othersocket) { if( $othersocket = IO::Socket::INET->new($otherport) ) { warn "opened client at $otherport\n"; $sel->add( $othersocket ); } else { warn "open failed $@\n"; } } if( $othersocket ) { my $msg = $pc1 ? "ADD\n" : "STAT\n"; print $othersocket $msg; print "sent $msg"; } $nextsend = $sendinterval + time; } else { for my $h ($sel->can_read($delta)) { if($h == $ls) { $sel->add(my $new = $h->accept); } elsif(sysread $h, my $in, 1024) { if( $othersocket and $h == $othersocket ) { print "should be ack: $in"; } else { print "got $in"; print $h "ack to $in"; } } else { $othersocket and $h == $othersocket and $othersocket = undef; $sel->remove($h); close $h; print "closed.\n"; } } } } __END__
Re: Async socket connection
by ljamison (Sexton) on Apr 03, 2017 at 18:57 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1181364]
Approved by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (4)
As of 2024-04-23 07:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found