我正在编写一个HTTP Web服务器,当我向浏览器发送带有HTML文件等效内容的文本文件时,浏览器会正确显示它,但当我发送HTML文件时,浏览器会显示HTML页面一秒钟然后出现“连接已重置”错误。
我注意到文本文件比HTML文件大,但我不知道为什么
文字大小= 286字节
HTML大小= 142字节
这是HTML代码:
<!DOCTYPE html>
<html>
<body>
<p>This is a paragraph.</p>
<p>This is a paragraph.</p>
<p>This is a paragraph.</p>
</body>
</html>
这是我的代码:
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 );
}
以下是在上面的代码之前使用的代码的另一部分:
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;
答案 0 :(得分:1)
我有两个可以看到的重要问题
您的send
参数传递错误,此行(非常重要)
int c=send(connected,(const char*)&sendBuffer,strlen(&writable[0]),0);
应该是
int c=send(connected,(const char*) sendBuffer,strlen(&writable[0]),0);
/* ^
* No ampersand
*/
因为sendBuffer
数组衰减到指针而你不需要它。
您正在通过手册
传递select
的第一个参数错误
nfds是三组中任何一组中编号最高的文件描述符,加上1
所以在你的情况下它应该是
if (select(connected + 1, NULL, &wfd, NULL, &tm) > 0)
并且在调用send
之后使用它,您必须先调用它,看看是否可以写入文件描述符。
您的代码对于它设计的任务来说有点过于复杂,所以我提出了以下解决方案,其中提到的问题已修复,其他一些已经改进
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);
答案 1 :(得分:1)
中途回答。首先,即使“使用\n
工作”,它也违反了标准。一个人应该使用CRLF。使用CRLF。周期。
其余代码。我怀疑这会改变很多东西,但我会重新构造一下代码。它在发送功能中发生了很多事情。
已将数据发送分离到自己的功能。你甚至可以考虑将发送标题分离出它自己的功能 - 如果你找到了一种很好的方法来构建它。当您展开以发送文本或html等等等时,您最终应该将标题与自己的功能分开。在早期阶段做这件事会很有帮助。
只是一个粗略的开始。
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;
}