Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Socket Programming 1

Status
Not open for further replies.

Trancemission

Technical User
Oct 16, 2001
108
0
0
GB
I have been using PERL for over 6 months now on and off and I must say I a coming along quite nicely.

My next project involces creating a set of scripts that will allow data to be passed from a client to a server. It is only a v.small amount of data.

I have setup a simple server using the USE:IO:Socket module:

$local = IO::Socket::INET->new(Proto=>"tcp", LocalPort=>"200",Listen=>"1")
or die "Cannot open socket\n";

$remote = $local->accept;

When I telnet onto the port when this is listening I am assuming that data is only sent when I hit the enter key becuase when I put:

while(<$remote>){
print;
}

It prints the data on the server terminal when I hit enter.


The data that I will be passing is

Swtich name - London
status - Down
condition - OSPF Process failed

I will then insert these into a mysql table in a DB. Any help on this would be appreciated. (I am okay with the syntax of accessing mysql but need help in getting the data ffrom the client and breaking it down.

Thus I also need to create the 'client' and I am not sure how to do this. I could use a telnet module but I would prefer to have my own 'client' that initiates a session and then sends data over the session.

Many Thanks

Trancemission
=============
If it's logical, it'll work!
 
looks like you're thinking along the right lines to me

couple of things

bear in mind that the end-of-line char might not be LF, different versions of Perl do things differently, see perldoc perlport

there are some good examples in perldoc perlipc of client server stuff Mike
______________________________________________________________________
&quot;Experience is the comb that Nature gives us after we are bald.&quot;

Is that a haiku?
I never could get the hang
of writing those things.
 
Here is a really simple Perl socket client.

You should also consider wether your socket server is going to receive multiple clients at a time, if so you will probably need to set a listen value (kinda like call waiting), or &quot;fork&quot; of a child process to handle each socket connection you accept.

If you are certain that the &quot;-&quot; will not appear in the field name (eg. &quot;status&quot;), then you could extract this with a reg-ex (assuming single line data string in $_):
/^S_(.*?)\s*-\s*(.*)$/ and $incoming_data{$1} = $2;
If a match occurs will place the user-supplied data into a hash, keyed by the fieldname, to use it later, just say something like:
print $incoming_data{'status'};
Alternatively you could use split to achieve something similar.

From a security aspect you should taint check the data, if you are not already.

I would also recomment Network Programming with Perl by Lincoln Stein.

Hope that at least some of this helps,
Paul :)

#!/usr/bin/perl -w
use IO::Socket;

# Connect to Socket
my $sock = new IO::Socket::INET (
PeerAddr => 'NAME_OF_HOST_HERE',
PeerPort => 'PORT_NUMBER_HERE',
Proto => 'tcp'
) or die &quot;Could not open socket connection: $!\n&quot;;

# Form and send query to server
my @Query = ( &quot;FIELD1:DATA1&quot; ,
&quot;FIELD2:DATA2&quot; ,
&quot;FIELD3:DATA3&quot; ,
&quot;FIELD4:DATA4&quot; );
$&quot; = &quot;\n&quot;;

print $sock &quot;@Query\n\n&quot;;

# Get Answer from server
my $return = <$sock>;
print &quot;$return\n&quot;;

close ($sock);
 
Many thanks for you help, however I am getting a little confused about closing sockets (i know RTFM - But still getting confiused). Using the client script above (or below!) the script just stays 'running'. The data is being passed perfectly though. But I have to Ctrl-C to exit the script. This then causes the server script to stop 'running' and more importantly to stop listening. Is this where I need to fork out any incoming connections?!?! If so then I will be even more confused :eek:

Thanks
Trancemission
=============
If it's logical, it'll work!
 
I think you'l find that the reason the above client & server just sit waiting, is because my client anticipates a response from the server after it has sent all it's data. Perl &quot;blocks&quot; (ie. sits waiting) untill it receives the data (
Code:
my $return = <$sock>;
).

In code I've written the server recognises the end of data transmission by a specific flag (eg. &quot;END_OF_DATA&quot;), this string must obviously not be contained in your data!

After sending this the client can then wait to receive a response from the server:
Code:
my $return = <$sock>;
this will allow you to confirm to the user that the data was received and format was Ok etc.

If you simply want one-way communication, you can simply remove the my
Code:
$return = <$sock>;
and
Code:
print $return;
lines. I believe your server will terminate at this point unless it is contained within a loop:
Code:
while ( my $new_sock = $sock->accept() ) { CODE_HERE }
so that it returns to listening for new connections once it has finished it's previous connection.

Additionally you can fork a child process when you accept the connection, this has the added benefit that you can accept further connections whilst processing the current connection, otherwise users might get IO connection refused error.

Server code: here I'm using a hash %config to hold the config settings, these are read in from a text config file, to make the code easily maintainable, rather than hard coding every-thing in.
Code:
my $sock = new IO::Socket::INET (
   LocalHost => $config{'HOST'},
   LocalPort => $config{'PORT'},
   Proto     => 'tcp',
   Listen    => $config{'LIST'},
   Reuse     => $config{'REUS'},
 ) or die &quot;Could not create socket: $!\n&quot;;

# Accept Socket connection
while ( my $new_sock = $sock->accept() ) {
    my $pid = fork();
    die &quot;Cannot fork: $!\n&quot; unless defined($pid);
    if ( $pid == 0 ) {
#-------------------------------------------
# Child Process begins here
#  -- Handles the accepted connection
#-------------------------------------------
    } # end if $pid==0
} # end while
close ($sock);

Hope this helps
Paul :)
 
Thanks for all your help. (Been away for a few days)

Scripts a coming along fine, but no I have another little query. Up until now I thought I would only have one client, now we need 50. I need to be able to get the IP address of the client when it connects to the server. I need to be able to do this from the server script as I will then log into a mysql DB

Cheers

Nevere ceases to amaze me how much things change in a few days. Specially when it's my project!!! (little moan there - feels better)
Trancemission
=============
If it's logical, it'll work!
 
Glad to hear your script's coming along.

You should be able to get the IP address (along with a load of other stuff) from the %ENV hash.

I think you should be fine in terms of number of machines accessing your server, provided you fork a new process to handle each one, which the code I posted previously does.

Hope this helps,
Paul :)
 
I cannot seem to find any data to do with the client in the %ENV hash. The UNIX stuff (from my server) is all there. I have tried calling %ENV before a connection, during a connection and after and always the same.

Cheers


Trancemission
=============
If it's logical, it'll work!
 
Trancemission,
I don't think the %ENV is OS dependant, but am not entirely confident of this, does anyone else know? (it certainly works on UNIX).
For debugging purposes have you tried printing out the contents of %ENV (just after accepting connection):
Code:
foreach $key (keys %ENV) {
     print &quot;Key:$key\tValue:$ENV{$key}&quot;;
}
 
Yeah I have tried that and I get:

ENV:/root/.bashrc
HISTSIZE:1000
HOME:/root
HOSTNAME:pluto.thedomainname.co.uk
HOSTTYPE:i586
INPUTRC:/etc/inputrc
LANG:en
LANGUAGE:en_GB:en
LC_COLLATE:en_GB
LC_CTYPE:en_GB
LC_MESSAGES:en_GB
LC_MONETARY:en_GB
LC_NUMERIC:en_GB
LC_TIME:en_GB
LESS:-MM
LESSKEY:/etc/.less
LESSOPEN:|/usr/bin/lesspipe.sh %s
LOGNAME:root
LS_COLORS:no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:eek:r=01;05;37;41:mi=01;05;37;41:ex=01;32:*.cmd=01;32:*.exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32:*.tar=01;31:*.tgz=01;31:*.tbz2=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lha=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tz=01;31:*.rpm=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.xbm=01;35:*.xpm=01;35:*.png=01;35:*.tif=01;35:*.tiff=01;35:
MACHTYPE:i586-mandrake-linux-gnu
MAIL:/var/spool/mail/root
NLSPATH:/usr/share/locale/%l/%N
OLDPWD:/root/tmp
OSTYPE:linux-gnu
PATH:/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin
PS1:[\u@\h \W]\$
PWD:/tmp
RPM_INSTALL_LANG:en_GB:en
SECURE_LEVEL:2
SHELL:/bin/bash
SHLVL:1
SSH_TTY:/dev/pts/0
TERM:xterm
USER:root
USERNAME:root
_:./multiserver.mt


Trancemission
=============
If it's logical, it'll work!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top