Reputation: 844
I'm am writing a HTTP web server when I send a text file with the equivalent content of the HTML file to the browser the browser shows it correctly but when I send the HTML file itself browser shows the HTML page for a second and then the "the connection was reset" error shows up.
I have noticed that the text file is bigger than the HTML file but I have no Idea why
text size = 286 byte
HTML size = 142 byte
and this is the HTML code:
<!DOCTYPE html>
<html>
<body>
<p>This is a paragraph.</p>
<p>This is a paragraph.</p>
<p>This is a paragraph.</p>
</body>
</html>
this is my code:
char sendBuffer[500];
FILE *sendFile = fopen("foo.html", "r");
fseek(sendFile, 0L, SEEK_END);
int sz = ftell(sendFile);
fseek(sendFile, 0L, SEEK_SET);
string s1;
s1="HTTP/1.1 200 OK\nContent-length: " + to_string(sz) + "\n";
std::vector<char> writable(s1.begin(), s1.end());
writable.push_back('\0');
strcpy(sendBuffer,(const char *)&writable[0]);
int c=send(connected,(const char*)&sendBuffer,strlen(&writable[0]),0);
printf("\nSent : %s\n",sendBuffer);
strcpy(sendBuffer,"Content-Type: text/html\n\n");
c=send(connected,(const char*)&sendBuffer,strlen("Content-Type: text/html\n\n"),0);
printf("\nSent : %s\n",sendBuffer);
char send_buffer[300];
while( !feof(sendFile) )
{
int numread = fread(send_buffer, sizeof(unsigned char), 300, sendFile);
if( numread < 1 ) break; // EOF or error
char *send_buffer_ptr = send_buffer;
do {
int numsent = send(connected, send_buffer_ptr, numread, 0);
if( numsent < 1 ) // 0 if disconnected, otherwise error
{
if( numsent < 0 ) {
if( WSAGetLastError() == WSAEWOULDBLOCK )
{
fd_set wfd;
FD_ZERO(&wfd);
FD_SET(connected, &wfd);
timeval tm;
tm.tv_sec = 10;
tm.tv_usec = 0;
if( select(0, NULL, &wfd, NULL, &tm) > 0 )
continue;
}
}
break; // timeout or error
}
send_buffer_ptr += numsent;
numread -= numsent;
}
while( numread > 0 );
}
Here is the other part of code that is used just before the code above:
int sock, connected, bytes_recieved , _true = 1 , portNumber;
char send_data [1024] , recv_data[1024];
struct sockaddr_in server_addr,client_addr;
int sin_size;
time_t t = time(NULL);
struct tm tm = *localtime(&t);
char date[50];
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Unable to create the Socket");
exit(1);
}
if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(const char*)&_true,sizeof(int)) == -1) {
perror("Unable to Setsockopt");
exit(1);
}
char *server_address="127.1.1.1";
portNumber=8080;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(portNumber);
server_addr.sin_addr.s_addr = inet_addr("127.1.1.1");//inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr);//INADDR_ANY;
string host=server_address+':'+to_string(portNumber);
memset(&(server_addr.sin_zero),0,8);//sockaddr_in zero padding is needed
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))==-1) //bind the socket to a local address
{
perror("Unable to bind");
exit(1);
}
if (listen(sock, 5) == -1) //listen to the socket with the specified waiting queue size
{
perror(" Listen");
exit(1);
}
cout << "MyHTTPServer waiting on port 8080" << endl;
fflush(stdout);
sin_size = sizeof(struct sockaddr_in);
connected = accept(sock, (struct sockaddr *)&client_addr,&sin_size);
cout<< "I got a connection from (" << inet_ntoa(client_addr.sin_addr) << "," << ntohs(client_addr.sin_port) << ')' << endl;
Upvotes: 4
Views: 3801
Reputation: 4093
A half way answer. First off, even if “Using \n
works” it is breaching the standard. One should use CRLF. Use CRLF. Period.
For the rest of the code. I doubt this is going to change many things, but I would have re-structured the code a bit. It is to much going on in the send function.
Have separated out the sending of data to own function. You could also consider even separating out send header to it's own function – if you find a good way to structure it. When you expand to send text or html or etc. and so on and so forth, you definitively should separate out header to own function. Doing it at an early stage would be helpful.
Only meant as a crude start.
int send_data(int soc, const char *buf, size_t len)
{
ssize_t sent;
do {
/* Use iharob code or similar here */
/* Return something <> 0 on error. */
sent = send(soc, buf, len, 0);
buf += sent;
len -= sent;
} while (len > 0);
return 0;
}
int send_file(int soc, const char *fn)
{
char buf[500];
FILE *fh;
long sz;
size_t len;
int err = 0;
if (!(fh = fopen(fn, "r"))) {
perror("fopen");
return 1;
}
fseek(fh, 0L, SEEK_END);
sz = ftell(fh);
fseek(fh, 0L, SEEK_SET);
/* Consider adding Date + Server here. */
len = sprintf(buf,
"HTTP/1.1 200 OK\r\n"
"Content-length: %ld\r\n"
"Content-Type: text/html\r\n"
"Server: FooBar/0.0.1\r\n"
"\r\n", sz
);
if (len < 0) {
err = 3;
fprintf(stderr, "Error writing header.\n");
goto fine;
}
/* Debug print. */
fprintf(stderr, "Header[%d]:\n'%s'\n", len, buf);
if ((err = send_data(soc, buf, len)) != 0) {
fprintf(stderr, "Error sending header.\n");
goto fine;
}
while (!feof(fh)) {
len = fread(buf, sizeof(char), 500, fh);
if (len < 1)
break;
if ((err = send_data(soc, buf, len))) {
fprintf(stderr, "Error sending file.\n");
goto fine;
}
}
if ((err = ferror(fh))) {
fprintf(stderr, "Error reading file.\n");
perror("fread");
}
fine:
fclose(fh);
return err;
}
Upvotes: 1
Reputation: 53016
You have two important problems I can see
Your are passing send
paremeters wrong, this line (very important)
int c=send(connected,(const char*)&sendBuffer,strlen(&writable[0]),0);
should be
int c=send(connected,(const char*) sendBuffer,strlen(&writable[0]),0);
/* ^
* No ampersand
*/
since the sendBuffer
array decays to a pointer and you don't need that.
You are passing the first parameter of select
wrong too from the manual
nfds is the highest-numbered file descriptor in any of the three sets, plus 1
so in your case it should be
if (select(connected + 1, NULL, &wfd, NULL, &tm) > 0)
and you are using it after you call send
you must call it before to see if it is possible to write to the file descriptor.
Your code is a little bit too complicated for the task it's designed to so I propose the following solution with the mentions problems fixed and some other ones improved
string text;
stringstream stream;
FILE *sendFile = fopen("foo.html", "r");
if (sendFile == NULL) /* check it the file was opened */
return;
fseek(sendFile, 0L, SEEK_END);
/* you can use a stringstream, it's cleaner */
stream << "HTTP/1.1 200 OK\nContent-length: " << ftell(sendFile) << "\n";
fseek(sendFile, 0L, SEEK_SET);
text = stream.str();
/* you don't need a vector and strcpy to a char array, just call the .c_str() member
* of the string class and the .length() member for it's length
*/
send(connected, text.c_str(), text.length(), 0);
std::cout << "Sent : " << text << std::endl;
text = "Content-Type: text/html\n\n";
send(connected, text.c_str(), text.length(), 0);
std::cout << "Sent : %s" << text << std::endl;
while (feof(sendFile) == 0)
{
int numread;
char sendBuffer[500];
numread = fread(sendBuffer, sizeof(unsigned char), 300, sendFile);
if (numread > 0)
{
char *sendBuffer_ptr;
sendBuffer_ptr = sendBuffer;
do {
fd_set wfd;
timeval tm;
FD_ZERO(&wfd);
FD_SET(connected, &wfd);
tm.tv_sec = 10;
tm.tv_usec = 0;
/* first call select, and if the descriptor is writeable, call send */
if (select(1 + connected, NULL, &wfd, NULL, &tm) > 0)
{
int numsent;
numsent = send(connected, sendBuffer_ptr, numread, 0);
if (numsent == -1)
return;
sendBuffer_ptr += numsent;
numread -= numsent;
}
} while (numread > 0);
}
}
/* don't forget to close the file. */
fclose(sendFile);
Upvotes: 2