2

I'm trying to write a client program and a server program in which when the client connects to the server, the server sends a random string from a file back to it. Here is what I have so far (reading from file omitted):

server.c

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

int listfd;
int connfd;

int main(int argc, char *argv[]){

    /*
    * Create Sockets
    */
    listfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if(listfd == -1)
        exit(-1);

    struct sockaddr saddr = {AF_UNIX, "server"};
    socklen_t saddrlen = sizeof(struct sockaddr) + 6;
    bind(listfd, &saddr, saddrlen);

    listen(listfd, 10);

    fflush(stdout);
    printf("Running...\n");

    /*
    * Listen for connections
    * and send random phrase on accept
    */
    while(1){
        connfd = accept(listfd, NULL, NULL);

        int r = rand() % num_of_lines; //Pick random phrase/hint pair
        write(connfd, phrases[r], strlen(phrases[r]));

        close(connfd);
        sleep(1);
    }

    exit(0);
}

client.c

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

int main(int argc, char *argv[])
{
    int sock;
    int conn;

    struct sockaddr saddr = {AF_UNIX, "server"};
    socklen_t saddrlen = sizeof(struct sockaddr) + 6;

    sock = socket(AF_UNIX, SOCK_STREAM, 0);

    conn = connect(sock, &saddr, saddrlen);

    char BUFF[1024];

    read(sock, BUFF, 1024);

    printf("%s", BUFF);

    return 0;
}

My problem arises when I try to print in the client. I run the server, but when I run the client, it only prints garbage characters, and I'm not entirely sure what's causing this.

Any help would be much appreciated, thank you!

EDIT:

I figured out my problem. Because the server socket was bound to "server", which was also the name of the executable, this caused a lot of issues.

Renaming the sockaddr.sa_data field fixed my problem.

jorapi
  • 41
  • 1
  • 2
  • 4
  • 2
    At least...zero initialize the BUFF before receving data. – moeCake Dec 11 '13 at 03:37
  • Try sending really big responses (>64bytes) and see what happens. – alk Dec 11 '13 at 07:22
  • 1
    Closely read the man-pages for read()/write() and learn that at least for sockets those two functions do not necessarily receive/send as much bytes as they were told to, but few. So looping around such calls counting until all data had been received/sent is a good idea, not to say an essential necessity. – alk Dec 11 '13 at 07:24
  • @alk - you copy/paste that in from your 'standardTCPcockups.txt' file? :) – Martin James Dec 11 '13 at 12:33
  • @MartinJames: After having created several comments saying essentially the same, it converged to the current version, the one above. – alk Dec 11 '13 at 14:45

2 Answers2

7

You have a number of problems. This is wrong:

struct sockaddr saddr = {AF_UNIX, "server"};
socklen_t saddrlen = sizeof(struct sockaddr) + 6;

You're telling the kernel "Here's a bag of bytes representing the socket address. It's sizeof(struct sockaddr) + 6 bytes long", but you're only passing in sizeof(struct sockaddr) bytes. Result: the kernel reads your address out of bounds, at best causing the syscall to fail with EFAULT, and at worst causing the kernel to read in garbage data.

The correct way to setup an AF_UNIX socket address is with the socket address type intended for AF_UNIX sockets, namely struct sockaddr_un (see unix(7)):

struct sockaddr_un saddr = {AF_UNIX, "/socket/path"};
bind(listfd, (struct sockaddr *)&saddr, sizeof(saddr));

Likewise the client code which calls connect(2) should also set up the address in the same way.

Next, you need to check for errors. Pretty much any system call here could fail, so you need to check for failures after every call and handle it appropriately (typically close the socket and/or terminate the process with an appropriate error message).

As you mentioned, you also need to choose an appropriate path name for the socket. Named AF_UNIX sockets live in the file system and so cannot share the same name with any other file, including your server and client programs.

Your call to read(2) needs to use the return value to determine the number of bytes read. It does NOT null-terminate its output—if it reads 4 bytes, those 4 bytes will be in your buffer, and everything else afterwards will be indeterminate. Since printf expects its input to be null-terminated, you need to either explicitly null-terminate it yourself, or pass in the length, e.g.:

int n = read(sock, BUFF, sizeof(BUFF));
if (n < 0) { /* Handle error */ }
printf("%.*s", n, BUFF); 
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
1
read(sock, BUFF, 1024);

does not NULL terminate the buffer. You need to do it explicitly. It returns the number of bytes read and copies the many bytes to the buffer.

 readlen = read(sock, BUFF, 1024);
 BUFF[readlen] = '\0';

Check the return value of connect and read for any error. Check what server sends.

doptimusprime
  • 9,115
  • 6
  • 52
  • 90
  • 3
    This is only part of the answer. You can't expect a complete read, so you really need to loop until the full message is read and only null-terminate after that. – R.. GitHub STOP HELPING ICE Dec 11 '13 at 03:44
  • @R..: You are right. Client needs to loop but keep track of what has been read so far to place NULL character. – doptimusprime Dec 11 '13 at 03:48
  • Wouldn't it still print the characters up until the null terminating character, or does printf prevent that if there's no null terminator? – jorapi Dec 11 '13 at 03:48
  • `printf` continues to print until there is NULL character. If no NULL character, it will continue to search for NULL character and behaviour of what is undefined. – doptimusprime Dec 11 '13 at 03:49
  • @dbasic, exactly. Currently the client only prints garbage. Even when I have the server write a single character and have the client try to read said character, it only prints garbage. I suspect my sockets are the culprit, I just can't, for the life of me, figure out what the issue is with them – jorapi Dec 11 '13 at 03:53
  • @jorapi Check what data you send. Print that data before send. – doptimusprime Dec 11 '13 at 04:10
  • @dbasic: There is no "`NULL`" character. However there is `NUL` which equals `'\0'` which equals `0`. – alk Dec 11 '13 at 14:41
  • 1
    @alk: "Null character" is proper C terminology. `NUL` is ASCII terminology. `NULL` is a macro identifier for an implementation-defined null pointer constant, and its use in this context is misleading I agree. – R.. GitHub STOP HELPING ICE Dec 11 '13 at 15:22