2

I am totally new to socket programming. Right now I'm just trying out random stuff I found. So I have this program (code below) that uses csapp.h and csapp.c, in particular the open_clientfd(hostname, port) function, and RIO IO to print out whatever you enter.

https://github.com/tlozoot/cs-262-bank-protocol/blob/master/csapp.c

Well, when I tried running the program ./prog www.google.com 80 (I'm not even sure about the hostname and port), it is printing out weird stuff and then stop.

Expected output:

Enter message:abc
Echo:abc

Actual output:

Enter message:abc
Echo:HTTP/1.0 400 Bad Request

Below is my code:

int main(int argc, char **argv)
{
   int clientfd, port;
   char *host, buf[MAXLINE];
   rio_t rio;
   host = argv[1];  port = atoi(argv[2]);

   clientfd = Open_clientfd(host, port);

   Rio_readinitb(&rio, clientfd);
   printf("Enter message:"); fflush(stdout);

   while (Fgets(buf, MAXLINE, stdin) != NULL) {
      Rio_writen(clientfd, buf, strlen(buf));
      Rio_readlineb(&rio, buf, MAXLINE);

      printf("Echo:");

      Fputs(buf, stdout);
      printf("Enter message:");

      fflush(stdout);
   }

   Close(clientfd);
   exit(0);
}

And I have another question. How do I send something like this to a socket?

GET /index.html HTTP/1.1\r\n
Host: www.google.com\r\n
\r\n
1
  • does it have to be in C? There are libraries to do a webrequest. Libwww comes to mind but there are most likely others as well. The point being that if you use the sockets yourself, you have to create a valid http request yourself and parse the results as well. This is all text but still, quite cumbersome and error prone and implemented by others already. Commented Aug 5, 2015 at 18:39

4 Answers 4

2

If I understand your code and your intent with command correctly, then your code is fine as it is...but not for google.com server. You see, google.com server is a HTTP server and thus responds to HTTP requests like the one you placed in your second question. Sending that request would yield you response - index.html contents and response header.

If you were to, say, code a simple server app with the library you're using, that echoes to client whatever it sends, then you would see the output you wanted.

Here is full code of simple server that forks on accept and echoes what you send to it (courtesy of Beej's guide to network programming, slightly modified by me)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

#define PORT "3490"  // the port users will be connecting to

#define BACKLOG 10     // how many pending connections queue will hold

void sigchld_handler(int s)
{
    while(waitpid(-1, NULL, WNOHANG) > 0);
}

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void)
{
    int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_storage their_addr; // connector's address information
    socklen_t sin_size;
    struct sigaction sa;
    int yes=1;
    char s[INET6_ADDRSTRLEN];
    char buf[1024];
    int rv;

    memset(&buf, 0, sizeof(buf));
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE; // use my IP

    if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and bind to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("server: socket");
            continue;
        }

        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
                sizeof(int)) == -1) {
            perror("setsockopt");
            exit(1);
        }

        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("server: bind");
            continue;
        }

        break;
    }

    if (p == NULL)  {
        fprintf(stderr, "server: failed to bind\n");
        return 2;
    }

    freeaddrinfo(servinfo); // all done with this structure

    if (listen(sockfd, BACKLOG) == -1) {
        perror("listen");
        exit(1);
    }

    sa.sa_handler = sigchld_handler; // reap all dead processes
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    printf("server: waiting for connections...\n");

    while(1) {  // main accept() loop
        sin_size = sizeof their_addr;
        new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
        if (new_fd == -1) {
            perror("accept");
            continue;
        }

        inet_ntop(their_addr.ss_family,
            get_in_addr((struct sockaddr *)&their_addr),
            s, sizeof s);
        printf("server: got connection from %s\n", s);

        if (!fork()) { // this is the child process
            close(sockfd); // child doesn't need the listener
            if (recv(new_fd, buf, sizeof(buf), 0) == -1){
                perror("recv");
            }

            if (send(new_fd, buf, sizeof(buf), 0) == -1)
                perror("send");
            close(new_fd);
            exit(0);
        }
        close(new_fd);  // parent doesn't need this
    }

    return 0;
}

That should get it done. Hope I helped. Btw, look at this link: [Beej's Guide to Network Programming1. It's very nice for beginning.

Edit: As for second question, you may create:

char* index_get_request=
"GET /index.html HTTP/1.1\r\n"
"Host: www.google.com\r\n"
"\r\n";

And then use:

send(sock_fd, index_get_request, strlen(index_get_request), 0);
Sign up to request clarification or add additional context in comments.

Comments

2

Looks like you are doing it rght:

telnet www.google.com 80
Trying 173.194.123.19...
Connected to www.google.com.
Escape character is '^]'.
message abc
HTTP/1.0 400 Bad Request

Comments

1

The HTTP protocol is quite complex in practice (the core protocol is simple, but there are lots of corner cases). I would suggest to use some HTTP client library (e.g. libcurl ...) or some HTTP server library (like libonion)

If you don't want to use HTTP or some well defined protocol but e.g. some simpler protocol (you might find server & client libraries for many other protocols like SMTP, JSON-RPC, etc etc....) that you have to design and define unambiguously you need to code both server and client side (and test on your machine(s), either two different one, or the same if using localhost i.e. 127.0.0.1 in IPV4)

BTW, sockets are not in standard C99 (or C11). You need them to be supplied, often by your operating system. POSIX has sockets inspired by the BSD socket API.

Beware than tcp(7) sockets are just a stream of bytes. You need to buffer on both sending & receiving side. There is no guarantee that a single send(2) (or write) on the emitter side corresponds to a single recv(2) (or read) of the same data size. Very often, you need to poll(2) and buffer (for partial application "messages").

Comments

1

The results look correct, it just looks like you aren't speaking proper HTTP to the port...

jason@io ~ $ nc -v www.google.com 80
nc: sea09s15-in-f20.1e100.net (173.194.33.84) 80 [http] open
nc: using stream socket
message:abc
HTTP/1.0 400 Bad Request
Content-Type: text/html; charset=UTF-8
Content-Length: 1419
Date: Wed, 05 Aug 2015 23:00:52 GMT
Server: GFE/2.0

<!DOCTYPE html>
<html lang=en>
  <meta charset=utf-8>
  <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
  <title>Error 400 (Bad Request)!!1</title>
  <style>
    *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/errors/logo_sm_2.png) no-repeat}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/errors/logo_sm_2_hr.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/errors/logo_sm_2_hr.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/errors/logo_sm_2_hr.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:55px;width:150px}
  </style>
  <a href=//www.google.com/><span id=logo aria-label=Google></span></a>
  <p><b>400.</b> <ins>That’s an error.</ins>
  <p>Your client has issued a malformed or illegal request.  <ins>That’s all we know.</ins>

If you want to know more about the HTTP text protocol, it's described in the RFC. The FTP protocol might be a bit more interesting though since it's a mix of text and binary. One of the first pieces of network software I wrote was an FTP server.

If I remember correctly, RIO is Microsoft's API for low latency I/O. You probably want to avoid that unless you have a genuine need for it. If you're tied to using Windows, you might want to use something like boost::asio instead since it's a bit higher level. Another option might be libuv. The performance using the libraries is generally comparable to using raw sockets, depending on your software design. Otherwise, if you can use OSX/Linux/FreeBSD/NetBSD/Solaris/etc... I'd recommend using the standard POSIX API.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.