views:

784

answers:

9

I am using Berkeley sockets (both: Internet domain and Unix domain) and I was wondering if the server can use the same sockets for reading the request and writing a response to the client. Or should the client create an other socket to wait for the replay and the server connect to it after processing the message received.

By the way, I am talking about connection oriented sockets (stream sockets, TCP, ...).

This is the simplified server code (I ommit error checking on system calls here just for simplicity):

int main() {

    int server_socket, connected_socket;
    struct sockaddr_in server_addr;
    char buf[1024];
    char aux[256];
    int bytes_read;

    server_socket = socket(AF_INET, SOCK_STREAM, 0);    

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(1234);
    bind(server_socket, &server_addr, sizeof(server_addr))

    listen(server_socket, 5)

    connected_sodket = accept(server_socket, 0, 0);
    do {
        bzero(buf, sizeof(buf));
        bytes_read = read(connected_socket, buf, sizeof(buf));        
    } while (bytes_read > 0);          

    /* Here I want to use connected_socket to write the reply, can I? */

    close(connected_socket);       

    close(server_socket);

    return (EXIT_SUCCESS);
}

And this is the simplified client code (I ommit error checking on system calls here just for simplicity):

int main() {

    int client_socket;
    struct sockaddr_in server_addr;

    client_socket = socket(AF_INET, SOCK_STREAM, 0);

    hp = gethostbyname("myhost");
    server_addr.sin_family = AF_INET;
    memcpy(&server_addr.sin_addr, hp->h_addr_list[0], hp->h_length);
    server_addr.sin_port = htons(1234);

    connect(client_socket, &server_addr, sizeof(server_addr));

    write(client_socket, MSG, sizeof(MSG));

    /* Here I want to wait for a response from the server using client_socket, can I? */

    close(client_socket);

    return (EXIT_SUCCESS);
}

Can I use connected_socket in the server and client_socket in the client to pass a response message back? Or should I use the client address I get in the server when in "accept" to connect to a socket in the client?

I have tried by using read/wrint in the client/server where the comment is shown but that way both programs keep blocked, it seems to be a dead-lock.

Thanks ins advance! Regards.

+1  A: 

Have you tried it? It looks like it should work when you actually put the code in where indicated

Rowland Shaw
+1  A: 

Yes, SOCK_STREAM sockets are two-way. You should be able to read and write to/from the same socket on each side of the connection. The man page for socket(2) has more detail on this.

MattK
+1  A: 

Yes, you can. TCP sockets are bi-directional. Just use the same read() and write() functions. Also make sure to check for error conditions on all calls to connect(), read(), write(), ... as you can't control what happens on the network.

+3  A: 

Yes, it's possible. Look check out this page for an example of a simple server (and simple client). Note that the server typically passes the "accept"ed file descriptor into a new process so that it can continue listening for more incoming connections

Marc Novakowski
+6  A: 

You should use the same socket!

Your application protocol defines unambiguously when the client and the server should wait for data or send messages to each other; assuming a protocol with only one request from the client and one response from the server, the following should hold:


  • The client establishes a connection with the server;
  • the client sends its request (with send());
  • the client knows, by virtue of the protocol, that the server will reply; therefore it waits for data on the same socket (recv());
  • after validating the response, the client can close the socket.


  • The server accepts a connection from the client;
  • the server knows that the first step is up to the client, hence it waits for data (recv());
  • the server validates the request;
  • the server now knows, from the protocol, that the client is waiting for data; hence it sends its response with send();
  • the server knows, from the protocol, that there are no further steps; hence it can close the socket.
Federico Ramponi
+1  A: 

Sorry; I did not say it but I did try it like this

This code in the server where the comment is:

write(connected_socket, "Ok, I got it", sizeof("Ok, I got it"));

and this code in the client where the comment is:

read(client_socket, buf, sizeof(buf));

Both programs keep blocked and when I kill the client, the server shows the messages it received (I have a printf just after the server calls read).

I try it with send and recv (both with 0 flags) instead of read and write and it did not change.

Toto
your 'buf' is too big. read() blocks until it is completely full. See man read(), and read more about non-blocking IO for different behaviour.
If I leave the code as the original post (no reply from the server) the server unblocks instantly, and it is using a buffer as big as the client's.
Toto
wdu is incorrect - whilst "fread()" will block until the buffer is full, "read" won't. On a socket it'll generally match each write (or packet) to a read.
Alnitak
Well, that fits my description. Always read less or equal than what you have sent, and your calls will not block. read() waits until enough data is received to fill the buffer completely (or until the connection is closed or other error), unless the socket is non-blocking.
That's right Alnitak, I've just re-read the __read__ man page and it says nothing about filling the buffer. Thanks!
Toto
Ok wdu, now I understand what you meant: you were talking about the dead lock mentioned in sth's answer, or something like that.
Toto
no, he wasn't... his comment is unambiguous. It's also incorrect.
Alnitak
+1  A: 

In your current setup the server tries to read until the client closes the socket, while the client doesn't close the socket until the server answered. Therefore you have a kind of "deadlock". The server is not stopping to read in the hope some more data might arrive and the client has no way to tell the server it is done.

You need some way for the server to recognize that the request is complete, while the connection is still open. If you for example terminate your request by a newline, the server can check if it received a full line and then stop reading and send the answer.

sth
Ok, your right, now that you mention it I see the dead lock. However, I did not understand your work-around.
Toto
Say the client sends "list files\n". The server receives "list f" with the first read(), finds no "\n" and continues read()ing. Then it receives the remaining "iles\n" and has now "list files\n" in it's buffer. It sees the "\n", decides that the command is complete and sends the response.
sth
Btw: in your example you zero out the buffer in every iteration of the while-loop, leaving the buffer empty once you reach the point where you plan to send the reply... You probably also need to use recv() instead of read().
sth
Thanks for the example and the comments. However, I solved with the client calling shutdown(client_socket, SHUT_WR) after sending his message as Chris Dodd suggested.
Toto
+4  A: 

You can use the same socket BUT your program is set up to have the server read EVERYTHING the client sends before attempting to reply. So the loop in the server won't complete until the client closes the write side of its socket so the server gets an EOF (0 bytes read), and thus the server will never send back its response.

There are a couple of ways you can deal with this.

  1. You can break the loop in the server after its seen the whole request, rather than reading until EOF. This requires that the data sent by the client be self-delimiting somehow, so the server can know when its read it all.
  2. You can use a second connection for the reply. Probably not the best.
  3. You can use asymmetric shutdown of the socket. Have the client do shutdown(client_socket, SHUT_WR) to half-close the socket. The server will then see the EOF (and the loop will finish), but the other direction on the socket will still be open for the reply.
Chris Dodd
shutdown(client_socket, SHUT_WR); seems the most elegant way to do it. AND IT WORKED!. Thanks!
Toto
Historically, many Internet protocols are "line based" in that when the server receives a newline (or CRLF) it will process the current line of input and transmit a response back to the client.
Marc Novakowski
How do I "breack the loop in the server" as option 1 says?
Toto
When the server sees the ending delimiter for the request, it would break out of the loop and send a reply, rather than waiting for the EOF
Chris Dodd
+1  A: 

Not only should you use the same socket (as Federico says), you actually have to in order to get your return packets through firewalls.

Firewalls know about TCP connections, and automatically allow the return data to pass through if a machine inside the firewall initiated the connection. If instead you tried to create a new TCP socket from the outside the firewall would block it unless that traffic was specifically permitted.

Alnitak