C++: Network programming / HTTP Server problem

Started by
6 comments, last by Adamski 18 years, 5 months ago
Hi, I'm trying to write a very basic HTTP server. (Basic code has been given from my lecturer) At the moment it will just handle 1 request at once. I've managed to get the connection to be accepted and stored in a char array. What I am currently trying to do is echo the 2nd argument in the request that is given.. IE. if it was a proper HTTP request then i want to echo the document name (eg index.html). I've used the sscanf function to pick out the 3 arguments given from the request and put them into seperate arrays. However when i try to write out the 2nd argument (arg2), I get extra characters. It's probably something simple, but I can't work it out. I'm getting confused with the "rc" paramater I have and how it's being used, and why... Here's a bit of the code. This part handle the requests:

stat = bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr));
 if(stat < 0) { cerr << "cant bind\n"; exit(1);}

 listen(sock,5); // make ready for accept
 cout << "ready to accept connections\n";
 while(true) {
   cliaddrlen = sizeof(struct sockaddr_in);
   newsock=accept(sock,(struct sockaddr *) &cliaddr,&cliaddrlen);
   if(newsock < 0) { cerr << "accept error\n"; exit(1);}
   if(inet_ntop(AF_INET,&cliaddr.sin_addr,ip,64)==0)
       cerr << "cannot find IP address\n";
   else
       cerr << "connect from IP " << ip << "\n";

   char arg1[16], arg2[256], arg3[16];
   int reqargs;
   rc = read(newsock,buffer,2048);
   while(rc > 0) {
     reqargs = sscanf(buffer, "%s %s %s", arg1, arg2, arg3);
     write(newsock,arg2,rc);
     rc = read(newsock,buffer,2048);
   }
   close(newsock);
 }
}



Any help would be great! Thanks [Edited by - Adamski on November 16, 2005 5:09:21 AM]
Advertisement
Probably you're just not making sure that the char arrays are null terminated. bzero or memset is useful for that.

Also, while that will probably work, technically using send/recv instead of write/read with sockets is probably better.

rc in this case is the return value for read. For recv() anyways, a value >0 is the number of bytes read. Negative is an error.
Thanks for that. I've managed to get it to do a bit more... I've managed to sort out the arguments, and even ignore all the lines of the header except the 1st one, until a \n is received.

The problem now is that it just hangs when it actually gets a request... You can send it a request from IE/Firefox, it recognises it and prints the header to the console and then stops where it does the checks for protocol version, method etc, and doesn't return anything to the sender.

Again, here is my code. I've inlcuded the lot, but where the "#####.." is, is where it's stopping.

#include <iostream>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <fcntl.h>#include <unistd.h>#include <cstdlib>#include <stdio.h>#define SERVER_ROOT "/home/adamski/htdocs/"using namespace std;int main(int argc, char *argv[]) {   int stat, fd, fd2, port, fin;   unsigned int cliaddrlen;   struct sockaddr_in svr, cliaddr;   //char req[2048], response[4096], arg2[256], file[4096], floc[256];   static char* badreq_resp =     "HTTP/1.1 400 Bad Request\n"     "Content-type: text/html\n"     "\n"     "<html>\n"     " <body>\n"     " <h1>Bad Request</h1>\n"     " <p>This server did not understand your request.</p>\n"     " </body>\n"     "</html>\n";   static char* standresp =   "HTTP/1.1 200 OK\n"   "Content-type: text/html\n"   "Connection: close\n"   "\n"   "<html>\n"   " <body>\n"   " <h1>OK</h1>\n"   " <p>This server understood your request.</p>\n"   " </body>\n"   "</html>\n";   static char* notimpl_resp =   "HTTP/1.1 501 Method Not Implemented\n"   "Content-type: text/html\n"   "\n"   "<html>\n"   " <body>\n"   " <h1>Method Not Implemented</h1>\n"   " <p>The method %s is not implemented by this server.</p>\n"   " </body>\n"   "</html>\n";   // Open Socket   fd = socket(AF_INET, SOCK_STREAM, 0);   if (fd<0) {     cerr << "Cant open socket\n"; exit(1);   }   svr.sin_family = AF_INET;   svr.sin_addr.s_addr = htonl(INADDR_ANY);   port = 8080;   svr.sin_port = htons(port);   // Bind   stat = bind(fd,(struct sockaddr *)&svr, sizeof(svr));   if (stat<0) {     cerr << "Cant bind\n"; exit(1);    }   listen(fd,5); // Make ready to accept   // Handle incoming requests   while(true) {     cliaddrlen = sizeof(struct sockaddr_in);     fd2 = accept(fd,(struct sockaddr *)&cliaddr,&cliaddrlen);     char req[1024];     ssize_t bytes_read;     bytes_read = read(fd2,req,sizeof(req)-1);     while(bytes_read > 0) {       char method[sizeof(req)], file[sizeof(req)], version[sizeof(req)];       // Get arguments from 1st line       req[bytes_read] = '\0';       sscanf(req,"%s %s %s", method, file, version);       cout << "Arguments: " << req << "\n";       // Read to end of header       while (strstr (req, "\r\n\r\n") == NULL)         bytes_read = read(fd2,req,sizeof(req));       if (bytes_read == -1) {         close(fd2);       }       cout << sizeof(req) << "After strstr\n";###################### Prints above and STOPS HERE #########################       // Check protocol version. Accept HTTP/1.1       if (strcmp(version, "HTTP/1.1")) {         // If above matches then 0 is returned (false).         // So protocol isnt matched. Rejet request         write(fd2,badreq_resp,sizeof(badreq_resp));         cout << "Bad Request";       }       else if (strcmp(method, "GET")) {         // Not an implemented command. So return 501         write(fd2,notimpl_resp,sizeof(notimpl_resp));         cout << "Not Implemented";       }       else { // Return requested file         write(fd2,standresp,sizeof(standresp));         cout << "Request OK";       }       bytes_read = read(fd2,req,sizeof(req));     }     close(fd2);     if (bytes_read < 0) {       cerr << "read failed\n";     }   }}


For the moment I'm just wanting it to handle 1 connection at a time... Just so I can get it to work. Then I will incorporate code into functions and add threads etc.

Thanks for any help.
Eh, well, what does your debugger tell you?

I can't imagine it actually -stops- there, what is the last function triggered?
Hi.
I'm not sure how to use a debugger... Or even if I have one. I've just been writting it all in emacs and compiling it.

What happens is. I start up the server. If i enter localhost:8080 in a browser, the server recognises the connection, collects the header, and sends it back to the terminal. It then prints out the size of the req array, then prints out the line after the strstr function. At this stage it stops. If you then press stop on the browser and refresh the page, the server says "Request OK" in the terminal, and goes back to the top of the while loop to accept the connection.

It seems that it stops and then doesnt do anything more until you send the request again, but then stops at the same point again..

I'm confused :(
Using debugger is pretty simple. Simplest use is (from your post I see that you use some unix variant):
gdb [path to executable]
(....)
run
(....here program crashes/whatever and you return to gdb prompt....)
bt
(....now you have stack backtrace....)
Making breakpoints is slighty more tricky. I don't quiet remember how to do it thought. (I have almost never used it =))
Warning: for gdb to work you must compile your app with debugging symbols (probably "-O0 -ggdb3" for gcc).

You might want to look at my uber simple and uber crappy http server which I wrote few years ago. (that's why it is NOT best programming example, and if it doesn't compile then I'm sorry, like I said I haven't looked in it's code in few years =))

HTH
If you're using emacs to write it, you're likely using gcc to compile it?

Anyways, assuming that, you likely have gdb on your system. When compiling, add the -g2 flag [or enable debugging from emacs... I've never used emacs myself]. Once compiled, use the shell to cd to your compile directory, and call gdb <command_name>.

This will bring you to a little prompt which will allow you to do debugging. The command list will display the program's code for you. break [line#] will stop your program when it reachs the line specified so you can do more stuff. run will start the program so it will run until a break [or crash, or exit...]. display [variable_name] will display the value of a variable as you debug. next will go through your code 1 line at a time. step will enter any user-made functions, one line at a time.

Likely one of the read() or write() calls is blocking for some reason [actually, a quick look shows what's wrong] but learning to use a debugger is useful. See if you can't find the problem with the debugger. I can help a little if you can't get it working nicely, or can't find the error.
Thanks. I've now changed the bytes_read but at the very bottom of the loop to bytes_read == 0, and it seems to be working a bit better now (with a few other changes). But it still doesn't print out my couts until another request is sent.

I'll take a look at your http server Kiput. Thanks

I'll also see what I can do with the debugger

Thanks

This topic is closed to new replies.

Advertisement