Unix Programming - Socket programming problem: can't generate output in server socket

This is Interesting: Free IT Magazines  
Home > Archive > Unix Programming > June 2005 > Socket programming problem: can't generate output in server socket





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author Socket programming problem: can't generate output in server socket
Fernando Barsoba

2005-06-21, 2:50 am

Hello all,

This may sound pretty basic stuff.. but I'm working on a socket example
in which the client seems to work fine, but the server doesn't send to
the client the expected result. The problem is that I want to trace what
the server socket is doing, but I'm unable to see any of my fprintf or
printf stuff.

Please take a look at the example:

#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

//x ------------------------------------------------------------ x

#define MAXLINE 4096 /* max text line length */

int main(int argc, char **argv)
{
int listenfd, connfd; // socket file descriptor
struct sockaddr_in servaddr; // IPv4 socket address structure
char buff[MAXLINE];
// ********************
char file[32];
FILE *fp;
strcpy(file,"output.txt");

if ((fp = fopen(file, "w")) == NULL)
{
printf("Can't open %s\n", file);
exit(1);
}
else
fprintf(fp, "\nFirst step...");
// ********************


listenfd = socket(AF_INET, SOCK_STREAM, 0); // call to socket function

bzero(&servaddr, sizeof(servaddr)); // initialization of socket
structure to 0
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9877); // Port in host byte order must be
converted
// to network byte order
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
// INADDR_ANY - wild card
// This tells the kernel to choose the IP address
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

fprintf(fp, "\nWaiting for connection, BEFORE call to listen()");
listen(listenfd, 5);
fprintf(fp, "\nWaiting for connection, AFTER call to listen()");
for ( ; ; ) {

if (connfd = accept (listenfd, NULL, NULL) < 0)
fprintf(fp, "\nERROR on accept");
else
fprintf(fp, "\nSUCCESS on accept");
// We are not interested in knowing the identity of the client
// Therefore, 2nd and 3rd param. to NULL
strcpy(buff, "Output to the client");
snprintf(buff, sizeof(buff), "%%" );
write(connfd, buff, strlen(buff));
close(connfd);
}
// exit(0);

// ********************
fclose(fp);
// ********************
}


As you can see, there are many fprintf instructions which work fine in
my socket client but not in the server. I guess I'm missing some
conceptual stuff here. Any idea?

Thanks!

Fernando
Maxim Yegorushkin

2005-06-21, 2:50 am

On Tue, 21 Jun 2005 08:08:32 +0400, Fernando Barsoba
<fbarsoba@verizon.net> wrote:

> Hello all,
>
> This may sound pretty basic stuff.. but I'm working on a socket example
> in which the client seems to work fine, but the server doesn't send to
> the client the expected result. The problem is that I want to trace what
> the server socket is doing, but I'm unable to see any of my fprintf or
> printf stuff.


Did you consider using tcpdump ?

[]

> for ( ; ; ) {
>
> if (connfd = accept (listenfd, NULL, NULL) < 0)
> fprintf(fp, "\nERROR on accept");
> else
> fprintf(fp, "\nSUCCESS on accept");
> // We are not interested in knowing the identity of the client
> // Therefore, 2nd and 3rd param. to NULL
> strcpy(buff, "Output to the client");
> snprintf(buff, sizeof(buff), "%%" );
> write(connfd, buff, strlen(buff));
> close(connfd);
> }


[]

You do odd things with buff. First you copy "Output to the client" into
buff, then you overwrite it with a % and a zero characters. In the end you
end up sending a client just those two characters. Is that what you want?

--
Maxim Yegorushkin
<firstname.lastname@gmail.com>
Rajan

2005-06-21, 2:50 am

It seems surprising , because you will start the server first, it
should atleast print the fprintf("First step...") atleast in your file.
Does it atleast do that?
The other thing which I feel is if you have put the client in an
endless loop and is doing a fprintf there then it fills in the entire
file, what it does it writes into that file only the client fprintf.
Could you also post the client code?
You should have some

Fernando Barsoba

2005-06-21, 5:53 pm

Maxim Yegorushkin wrote:
> On Tue, 21 Jun 2005 08:08:32 +0400, Fernando Barsoba
> <fbarsoba@verizon.net> wrote:
>
>
>
> Did you consider using tcpdump ?
>


I'm going to try to use tcpdump... thanks.

> []
>
>
>
> []
>
> You do odd things with buff. First you copy "Output to the client" into
> buff, then you overwrite it with a % and a zero characters. In the end
> you end up sending a client just those two characters. Is that what you
> want?
>


No, that's not what I want. But it would be ok with me if the server did
that. I'm not very good at C, and I'm just starting at network
programming through an example from the Richard Steven's book.

What about if I commented out the snprintf sentence? I would like the
whole "Output to the client" string to be printed out on the client console.
// snprintf(buff, sizeof(buff), "%%" );

Thanks,

Fernando
Fernando Barsoba

2005-06-21, 5:53 pm

Rajan wrote:
> It seems surprising , because you will start the server first, it
> should atleast print the fprintf("First step...") atleast in your file.
> Does it atleast do that?


No, its' very strange.. it doesnt' even print that.

> The other thing which I feel is if you have put the client in an
> endless loop and is doing a fprintf there then it fills in the entire
> file, what it does it writes into that file only the client fprintf.
> Could you also post the client code?
> You should have some
>


You're right.. let me show the client.. When I execute it, the
connection is established.

Output:
x--------------x
Connecting to socket...

Connection established!
x--------------x


#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>


int inet_pton(int, const char *, void *);

int inet_aton(const char *, struct in_addr *);

/* include inet_pton */
int inet_pton(int family, const char *strptr, void *addrptr)
{
if (family == AF_INET) {
struct in_addr in_val;

if (inet_aton(strptr, &in_val)) {
memcpy(addrptr, &in_val, sizeof(struct in_addr));
return (1);
}
return(0);
}
//errno = EAFNOSUPPORT;
return (-1);
}

int main(int argc, char **argv)
{
int sockfd; // socket file descriptor
struct sockaddr_in servaddr; // IPv4 socket address structure

//sockfd = socket(AF_INET, SOCK_STREAM, 0); // call to socket function
printf("Connecting to socket...");
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
printf("Socket Error");
bzero((char *) &servaddr, sizeof(servaddr)); // initialization of
socket structure to 0
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9877); // Port in host byte order must be
converted
// to network byte order

if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
printf("\ninet_pton error");

if (connect(sockfd,( struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
printf("\nError connecting to server!");
// Connection to port 9877, IP?
else
printf("\n\nConnection established!");
exit(0);
}

---------------

Thanks!

Fernando
Maxim Yegorushkin

2005-06-22, 2:50 am

On Tue, 21 Jun 2005 19:30:41 +0400, Fernando Barsoba
<fbarsoba@verizon.net> wrote:

[]

> No, that's not what I want. But it would be ok with me if the server did
> that. I'm not very good at C, and I'm just starting at network
> programming through an example from the Richard Steven's book.
>
> What about if I commented out the snprintf sentence? I would like the
> whole "Output to the client" string to be printed out on the client
> console.
> // snprintf(buff, sizeof(buff), "%%" );
>


I've just stripped down your example and compiled it:

int main(int argc, char **argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr;

listenfd = socket(AF_INET, SOCK_STREAM, 0);

memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9877);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

listen(listenfd, 5);

for ( ; ; ) {

connfd = accept (listenfd, NULL, NULL);
write(connfd, "Output to the client\n", sizeof("Output to the
client\n") - 1);
close(connfd);
}
}

Run it and it seems to work fine:

[max@my ~]$ telnet localhost 9877
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
Output to the client
Connection closed by foreign host.

--
Maxim Yegorushkin
Rajan

2005-06-22, 2:50 am

I am curious to know , what was the objective in writing such a client,
wherein you are not sending any data to the server?
It would be good if you do write(sockfd,buff,sizeof(buff)) in your
client program sends the data to the server, which in turn in the
server program you must do a read(connfd, buff,sizeof(buff)) and then
do a
write(1,buff,sizeof(buff)).
I would like to take Maxim's example and add some more things to it to
help you understand a simple client-server example.
I am also incorporating some changes in the client code that you have
written.
I think a simple client-server example does not need inet_aton,
inet_pton to be used, but I won't discourage you from using them, it's
your choice.

int main(int argc, char **argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[1024]; /* Added by Rajan */

listenfd = socket(AF_INET, SOCK_STREAM, 0);


memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9877);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));


listen(listenfd, 5);


for ( ; ; ) {


connfd = accept (listenfd, NULL, NULL);
/************* Added by Rajan **************************/
memset(buff,'\0',sizeof(buff));
read(connfd,buff,sizeof(buff));/* This will read data from the
socket and put it into the buff */
write(1,buff,strlen(buff));/* This will print the data sent
from the client to the stdout */
/ ****************************************
***********************************/

write(connfd, "Output to the client\n", sizeof("Output to the

client\n") - 1);
close(connfd);
}

}

Your client code may have following changes
int main(int argc, char **argv)
{
int sockfd; // socket file descriptor
struct sockaddr_in servaddr; // IPv4 socket address
structure
char buff[1024];/* Added by Rajan */

//sockfd = socket(AF_INET, SOCK_STREAM, 0); // call to
socket function
printf("Connecting to socket...");
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
printf("Socket Error");
bzero((char *) &



servaddr, sizeof(servaddr)); // initialization of
socket structure to 0
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9877); // Port in host byte
order must be
converted

// to network byte order


if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
printf("\ninet_pton error");

if (connect(sockfd,( struct sockaddr *) &servaddr,
sizeof(servaddr)) < 0)
printf("\nError connecting to server!");
// Connection to port 9877, IP?
else
{
printf("\n\nConnection established!");
/********************** Added by Rajan
***************************/
memset(buff,'\0',1024);
strcpy(buff,"Connection established");
write(sockfd,buff,sizeof(buff)); /* this will send the data
from the client to the server */
memset(buff,'\0',1024);/* The buff is again set to 0 so that
you refresh it.

read(sockfd,buff, sizeof(buff));/* This will read the data
"Output sent to the client" from the server socket to the buffer
write(1,buff,strlen(buff)); /* This will print the data
"Output sent to the client on the stdout

/ ****************************************
*******************************/

}
exit(0);

}

Rajan

2005-06-22, 7:54 am

I think in the client example add a char buff[1024] or of any size.
Then instead of writing on the stdout by using printf for "Connection
established" you can use a write(sockfd,buff,strlen(buff)) which sends
the data over the socket to the server.
Then in the client code you need to read(sockfd,buff,sizeof(buff));
and write(1,buff,strlen(buff)); which would print "Output to the
client" on the stdout.
In the server code of Maxim's I would like to add some more thing like
connfd = accept (listenfd, NULL, NULL);
/* Added by Rajan */
read(connfd,buff,sizeof(buff));
write(1,buff,strlen(buff)); /* This will print "Connection established"
on stdout .*/
write(connfd, "Output to the client\n", sizeof("Output to the

client\n") - 1);
close(connfd);

David Schwartz

2005-06-22, 7:54 am


"Rajan" <rstalekar@yahoo.com> wrote in message
news:1119430053.111503.171310@g44g2000cwa.googlegroups.com...

>I think in the client example add a char buff[1024] or of any size.
> Then instead of writing on the stdout by using printf for "Connection
> established" you can use a write(sockfd,buff,strlen(buff)) which sends
> the data over the socket to the server.
> Then in the client code you need to read(sockfd,buff,sizeof(buff));
> and write(1,buff,strlen(buff)); which would print "Output to the
> client" on the stdout.
> In the server code of Maxim's I would like to add some more thing like
> connfd = accept (listenfd, NULL, NULL);
> /* Added by Rajan */
> read(connfd,buff,sizeof(buff));
> write(1,buff,strlen(buff)); /* This will print "Connection established"
> on stdout .*/
> write(connfd, "Output to the client\n", sizeof("Output to the
>
> client\n") - 1);
> close(connfd);


That's just plain horrible style, requiring you to overwrite the buffer
with zeroes before each read or the following happens:

1) Your buffer is all zeroes.

2) You receive "foo bar baz".

3) You send "foo bar baz".

4) You loop to do it again, and receive "qux".

5) You now have "qux bar baz" in your buffer.

6) You send "qux bar baz" even though you have received it.

And you can't fix this by sending the terminating zero either. (If you
don't know why, you have no business writing TCP code!)

Much more logical is:

i=read(connfd, buff, sizeof(buff)-1);
if(i<=0)
{
/* EOF or error */
}
else
{
buf[i]=0; /* if and only if you need it zero-terminated */
write(1, buff, i);
}

You only need the 'buf[i]=0;' if you really need to do 'strlen' or some
other string function on the received data. Otherwise, there is no need to
put a zero byte at the end of it.

DS


Maxim Yegorushkin

2005-06-22, 5:57 pm

On Wed, 22 Jun 2005 11:32:08 +0400, Rajan <rstalekar@yahoo.com> wrote:

[]

> /************* Added by Rajan **************************/
> memset(buff,'\0',sizeof(buff));
> read(connfd,buff,sizeof(buff));
> write(1,buff,strlen(buff));
> / ****************************************
***********************************/


Please note, that the code suffers from a common error.

You buff is declared as char buff[1024]. TCP MSS (maximum segment size) is
often more than 1024 bytes., what means a read can fill the whole buff
without the null terminator, so guess what happens when you call strlen()
on the buff.

The correct code looks like:

// no need to zero out the buff
ssize_t n = read(connfd, buff, sizeof(buff));
if(n > 0)
write(..., buff, n); // no need for strlen
else if(0 == n)
// handle EOF
else
// handle error

--
Maxim Yegorushkin
Fernando Barsoba

2005-06-22, 5:57 pm

Maxim Yegorushkin wrote:
> On Tue, 21 Jun 2005 19:30:41 +0400, Fernando Barsoba
> <fbarsoba@verizon.net> wrote:
>
> []
>
>
> I've just stripped down your example and compiled it:
>
> int main(int argc, char **argv)
> {
> int listenfd, connfd;
> struct sockaddr_in servaddr;
>
> listenfd = socket(AF_INET, SOCK_STREAM, 0);
>
> memset(&servaddr, 0, sizeof(servaddr));
> servaddr.sin_family = AF_INET;
> servaddr.sin_port = htons(9877);
> servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
> bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
>
> listen(listenfd, 5);
>
> for ( ; ; ) {
>
> connfd = accept (listenfd, NULL, NULL);
> write(connfd, "Output to the client\n", sizeof("Output to the
> client\n") - 1);
> close(connfd);
> }
> }
>
> Run it and it seems to work fine:
>
> [max@my ~]$ telnet localhost 9877
> Trying 127.0.0.1...
> Connected to localhost.localdomain (127.0.0.1).
> Escape character is '^]'.
> Output to the client
> Connection closed by foreign host.
>


It works fine doing a telnet 127.0.0.1. On the other hand, when I
execute the client the output from the server doesn't show up in the
client stdout. I'm going to try all the recommendations received.

thanks a lot!

Fernando
Fernando Barsoba

2005-06-22, 5:57 pm

Rajan wrote:
> I think in the client example add a char buff[1024] or of any size.
> Then instead of writing on the stdout by using printf for "Connection
> established" you can use a write(sockfd,buff,strlen(buff)) which sends
> the data over the socket to the server.
> Then in the client code you need to read(sockfd,buff,sizeof(buff));
> and write(1,buff,strlen(buff)); which would print "Output to the
> client" on the stdout.
> In the server code of Maxim's I would like to add some more thing like
> connfd = accept (listenfd, NULL, NULL);
> /* Added by Rajan */
> read(connfd,buff,sizeof(buff));
> write(1,buff,strlen(buff)); /* This will print "Connection established"
> on stdout .*/
> write(connfd, "Output to the client\n", sizeof("Output to the
>
> client\n") - 1);
> close(connfd);
>


I'll try your recommendations. Thanks a lot!!

Fernando
Fernando Barsoba

2005-06-22, 5:57 pm

Maxim Yegorushkin wrote:
> On Wed, 22 Jun 2005 11:32:08 +0400, Rajan <rstalekar@yahoo.com> wrote:
>
> []
>
>
>
> Please note, that the code suffers from a common error.
>
> You buff is declared as char buff[1024]. TCP MSS (maximum segment size)
> is often more than 1024 bytes., what means a read can fill the whole
> buff without the null terminator, so guess what happens when you call
> strlen() on the buff.
>

That's absolutely true. It's a limitation of TCP. You have buff of 4096
and then you have to keep reading from buff until emptying it..

From Steven's book:
"Stream sockets (e.g., TCP sockets) exhibit a behavior with the read and
write functions that differs from normal file I/O. A read or write on a
stream socket might input or output fewer bytes than requested, but this
is not an error condition. The reason is that buffer limits might be
reached for the socket in the kernel. All that is required to input or
output the remaining bytes is for the caller to invoke the read or write
function again. Some versions of Unix also exhibit this behavior when
writing more than 4,096 bytes to a pipe. This scenario is always a
possibility on a stream socket with read, but is normally seen with
write only if the socket is nonblocking. "

> The correct code looks like:
>
> // no need to zero out the buff
> ssize_t n = read(connfd, buff, sizeof(buff));
> if(n > 0)
> write(..., buff, n); // no need for strlen
> else if(0 == n)
> // handle EOF
> else
> // handle error
>


Fernando Barsoba

2005-06-22, 5:57 pm

Rajan wrote:
> I am curious to know , what was the objective in writing such a client,
> wherein you are not sending any data to the server?


That's right.. , but I was just trying to comprehend how it worked the
std output from server to client

> It would be good if you do write(sockfd,buff,sizeof(buff)) in your
> client program sends the data to the server, which in turn in the
> server program you must do a read(connfd, buff,sizeof(buff)) and then
> do a
> write(1,buff,sizeof(buff)).


That was my next thing to do! :-)

> I would like to take Maxim's example and add some more things to it to
> help you understand a simple client-server example.
> I am also incorporating some changes in the client code that you have
> written.
> I think a simple client-server example does not need inet_aton,
> inet_pton to be used, but I won't discourage you from using them, it's
> your choice.


I woulnd't disagree with you...
>
> int main(int argc, char **argv)
> {
> int listenfd, connfd;
> struct sockaddr_in servaddr;
> char buff[1024]; /* Added by Rajan */
>
> listenfd = socket(AF_INET, SOCK_STREAM, 0);
>
>
> memset(&servaddr, 0, sizeof(servaddr));
> servaddr.sin_family = AF_INET;
> servaddr.sin_port = htons(9877);
> servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
> bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
>
>
> listen(listenfd, 5);
>
>
> for ( ; ; ) {
>
>
> connfd = accept (listenfd, NULL, NULL);
> /************* Added by Rajan **************************/
> memset(buff,'\0',sizeof(buff));
> read(connfd,buff,sizeof(buff));/* This will read data from the
> socket and put it into the buff */
> write(1,buff,strlen(buff));/* This will print the data sent
> from the client to the stdout */
> / ****************************************
***********************************/
>
> write(connfd, "Output to the client\n", sizeof("Output to the
>
> client\n") - 1);
> close(connfd);
> }
>
> }
>
> Your client code may have following changes
> int main(int argc, char **argv)
> {
> int sockfd; // socket file descriptor
> struct sockaddr_in servaddr; // IPv4 socket address
> structure
> char buff[1024];/* Added by Rajan */
>
> //sockfd = socket(AF_INET, SOCK_STREAM, 0); // call to
> socket function
> printf("Connecting to socket...");
> if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
> printf("Socket Error");
> bzero((char *) &
>
>
>
> servaddr, sizeof(servaddr)); // initialization of
> socket structure to 0
> servaddr.sin_family = AF_INET;
> servaddr.sin_port = htons(9877); // Port in host byte
> order must be
> converted
>
> // to network byte order
>
>
> if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
> printf("\ninet_pton error");
>
> if (connect(sockfd,( struct sockaddr *) &servaddr,
> sizeof(servaddr)) < 0)
> printf("\nError connecting to server!");
> // Connection to port 9877, IP?
> else
> {
> printf("\n\nConnection established!");
> /********************** Added by Rajan
> ***************************/
> memset(buff,'\0',1024);
> strcpy(buff,"Connection established");
> write(sockfd,buff,sizeof(buff)); /* this will send the data
> from the client to the server */
> memset(buff,'\0',1024);/* The buff is again set to 0 so that
> you refresh it.
>
> read(sockfd,buff, sizeof(buff));/* This will read the data
> "Output sent to the client" from the server socket to the buffer
> write(1,buff,strlen(buff)); /* This will print the data
> "Output sent to the client on the stdout
>
> / ****************************************
*******************************/
>
> }
> exit(0);
>
> }
>


I'm going to work in all the bunch of code received. Thanks!
Rajan

2005-06-23, 2:48 am

David,
I must say that in the loop you need to do a
memset(buff,'\0',sizeof(buff)) before each read.
I had earlier added the changes in the code which was not posted.
Therefore there is no way that it will send the same thing again....

Maxim Yegorushkin

2005-06-23, 6:00 pm

On Thu, 23 Jun 2005 10:01:14 +0400, Rajan <rstalekar@yahoo.com> wrote:

> David,
> I must say that in the loop you need to do a
> memset(buff,'\0',sizeof(buff)) before each read.


Rajan, please see my previous post about an error in your code.

Aside from the error here is more about zeroing out buffers. I once wrote
a proxy server which served about 20k requests per second. Profiling
revealed that the server spent 30% of its user time zeroing out buffers
that were used for reading from sockets.

--
Maxim Yegorushkin
Maxim Yegorushkin

2005-06-24, 7:52 am

On Wed, 22 Jun 2005 20:04:33 +0400, Fernando Barsoba
<fbarsoba@verizon.net> wrote:

> Maxim Yegorushkin wrote:
> That's absolutely true. It's a limitation of TCP. You have buff of 4096
> and then you have to keep reading from buff until emptying it..


It has nothing to do with any TCP limitations. The code is flawed.

--
Maxim Yegorushkin
David Schwartz

2005-06-24, 6:00 pm


"Rajan" <rstalekar@yahoo.com> wrote in message
news:1119506474.952838.211010@g14g2000cwa.googlegroups.com...

> I must say that in the loop you need to do a
> memset(buff,'\0',sizeof(buff)) before each read.
> I had earlier added the changes in the code which was not posted.
> Therefore there is no way that it will send the same thing again....


Yeah, but that's terribly bad coding style and utterly pointless. The
'read' function call returns the number of bytes read, so there's no reason
to zero out the whole structure.

DS


Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com