Samuel
Samuel

Reputation: 13

Socket Programming in C

I'm trying to write a web server but with the code I have I'm getting 'open failed'. The html document (ht.html) which is supposed to be opened in the browser.

The code I have is:

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

#define SERVER_PORT 12345
#define BUF_SIZE 4096
#define QUEUE_SIZE 10


int main(int argc, char *argv[])
{
  int s, b, l, fd, sa, bytes, on = 1;
  char buf[BUF_SIZE]; //buffer for outgoing file
  struct hostent *h; //info about server
  struct sockaddr_in channel; //holds IP address

  //Build address structure to bind to socket
  memset(&channel, 0, sizeof(channel)); //zero channel
  channel.sin_family = AF_INET;
  channel.sin_addr.s_addr = htonl(INADDR_ANY);
  channel.sin_port = htons(SERVER_PORT);


  //Passive open. Wait for connection
  s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* create socket */
  if (s < 0) fatal("socket failed");
  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));


  b = bind(s, (struct sockaddr *) &channel, sizeof(channel));
  if (b < 0) fatal("bind failed");

  l = listen(s, QUEUE_SIZE);        /* specify queue size */
  if (l < 0) fatal("listen failed");


  /* Socket is now set up and bound. Wait for connection and process it. */
  while(1) {
    sa = accept(s, 0, 0);       /* block for connection request */
    if (sa < 0) fatal("accept failed");

    read(sa, buf, BUF_SIZE);    /* read file name from socket */

    /* Get and return the file. */

    fd = open(buf, O_RDONLY);   /* open the file to be sent back */
    if (fd < 0) fatal("open failed");

    while(1){
      bytes = read(fd, buf, BUF_SIZE); /* read from file */
      if (bytes <= 0) break;         /* check for end of file */
      write(sa, buf, bytes);              /*write bytes to socket*/
    }

    close(fd); //close file
    close(sa); //close connection

  }
}

fatal(char*string)
{
  printf("%s", string);
  exit(1);
}

Where's my error? Or what has to be added?

Upvotes: 1

Views: 1396

Answers (4)

Ed Heal
Ed Heal

Reputation: 59987

The stuff you are reading from the browser is a HTTP request.

You will need to decode this - so read the spec for HTTP.

Example of HTTP requests can be found here

Upvotes: 0

emil
emil

Reputation: 1700

Maybe you can start with outputting the data received from the socket, or at least run in a debugger, otherwise everything will be run in the dark, without you knowing what is going on. In the code below I have added a printf to print what we get from the web browser.

Also like others have pointed out, it is good to know what errno is trying to tell us. It is a bit awkward/annoying to use perror + exit, so in Linux and BSD you can use err(3) and warn(3). err will print errno message and then exit, while warn will just print errno message and not exit, I replaced your fatal function with these.

The web browser most likely will send GET /ht.html HTTP/1.1\r\n and this is what you are attempting to open. In order to open the file we need to extract the ht.html part. I have updated your code below and now strchr(3) and strstr(3) are used to extract ht.html.

We also need to send a HTTP response code and tell the web browser we want to send HTML, that is why the HTTP/1.1 200 OK is sent. Remember all HTTP headers need to be separated by \r\n (carriage return - newline). You will find more info about the HTTP protocol in RFC 2616.

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

#define SERVER_PORT 12345
#define BUF_SIZE 4096
#define QUEUE_SIZE 10

int main(int argc, char *argv[])
{
  int s, b, l, fd, sa, bytes, on = 1;
  char buf[BUF_SIZE]; /* buffer for outgoing file */
  char *p, *endp, *cp;

  struct sockaddr_in channel; /* holds IP address */

  /* Build address structure to bind to socket */
  memset(&channel, 0, sizeof(channel)); /* zero channel */
  channel.sin_family = AF_INET; /* ipv4 */
  channel.sin_addr.s_addr = htonl(INADDR_ANY); /* 0.0.0.0 */
  channel.sin_port = htons(SERVER_PORT);


  /* Passive open. Wait for connection */
  s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* create socket */
  if (s < 0) err(1, "socket failed");
  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

  b = bind(s, (struct sockaddr *) &channel, sizeof(channel));
  if (b < 0) err(1, "bind failed");

  l = listen(s, QUEUE_SIZE);        /* specify queue size */
  if (l < 0) err(1, "listen failed");


  /* Socket is now set up and bound. Wait for connection and process it. */
  while(1) {
    sa = accept(s, NULL, NULL);       /* block for connection request */
    if(sa < 0) {
      warn("accept failed");
      continue;
    }

    bytes = 0;
    endp = buf + sizeof(buf); /* pointer to end of buf */
    cp = NULL;
    buf[0] = '\0';
    /* read first line from socket */
    /* should be "GET /[file] HTTP/1.1" */
    for(p = buf; (bytes = read(sa, p, endp - p)) > 0; p += bytes) {
      p[bytes] = '\0'; /* read(2) doesn't NUL terminate buf */
      if((cp = strchr(p, '\r')) != NULL) /* break at first carriage return */
        break;
    }
    printf("incoming request %lu bytes:\n%s\n", strlen(buf), buf);
    /* no carrige return or no "GET /" was found */
    if(cp == NULL || strstr(buf, "GET /") != buf) { 
      warnx("incomplete request");
      close(sa);
      continue;
    }
    *cp = '\0'; /* replace '\r' with '\0' */
    p = buf + sizeof("GET /") - 1; /* point to after "GET /" */
    cp = strchr(p, ' '); /* find " HTTP/1.1" */
    if(cp == NULL) {
      warnx("HTTP version was not found");
      close(sa);
      continue;
    }
    *cp = '\0'; /* replace ' ' with '\0' */


    /* Get and return the file. */
    fd = open(p, O_RDONLY);   /* open the file to be sent back */
    if(fd < 0) {
      warn("open failed: %s", p);
      close(fd);
      close(sa);
      continue;
    }

    /* Send HTTP header */
    /* Should probably also send Content-Length: <sizeof file>, */
    /* this can be checked using fstat(2) */
    write(sa, "HTTP/1.1 200 OK\r\n" \
      "Content-Type: text/html;charset=UTF-8\r\n\r\n", 58);
    while(1) {
      bytes = read(fd, buf, sizeof(buf)); /* read from file */
      if (bytes <= 0) break;         /* check for end of file */
      write(sa, buf, bytes);              /*write bytes to socket*/
    }

    close(fd); /* close file */
    close(sa); /* close connection */
  }
  return 0;
}

To connect from your web browser to your HTTP server go to: http://127.0.0.1:12345/ht.html.

Upvotes: 1

someuser
someuser

Reputation: 189

  1. Try to look ERRNO.

    if (fd < 0) perror("open failed");
    
  2. Try to look buf.

    if (fd < 0){ 
      printf("%s\n", buf);
      perror("open failed");
    }
    
  3. Try to look buf this way:

    if (fd < 0){
      for(i=0;i<strlen(buf);i++) 
         printf("%d", buf[i]);
      perror("open failed");
    }
    

This will be enough to understand the error because your application simply does not open the file.

Upvotes: 0

eyalm
eyalm

Reputation: 3366

Try to add some debug messages or run with a debugger.

I think that the problem relies in the buffer passed to open statement. It looks like buf is not initialized with zeroes and also not NULL terminated by "read".

n = read(sa, buf, BUF_SIZE);
buf[n] = '\0';

In general, when working with read, it should be called in a loop until 0 or -1 returned. It might fill only a fraction of the buffer.

Upvotes: 0

Related Questions