基于TCP的SOCKET编程模型如下:
客户端我们不用管,直接使用浏览器做测试
服务器通过bind(),listen(),accept确定链接,然后用recv()处理浏览器发送的GET请求
GET request 格式如下:(不同浏览器请求不一样,可以全部打出来自己研究)
GET /filename HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1 …
其中最重要的就是filename,他决定了访问服务器的哪个文件,我们需要利用字符串操作把这部分取出来。如果浏览器中没有指定访问哪个文件,这部分会是空值,我们需要自己指定默认访问哪个文件。因此,我写了如下的函数
void GetPage(const char* recv_buff, const int length, string& filename)
{
string buff = recv_buff;
int location = buff.find(" HTTP", 5); //filename starts at 5th byte of buffer
filename = string(buff, 5, location - 5);
if (filename == "") //default page
filename = "1.HTML";
}
作为服务器,我们也需要返回GET response, 需要填充 response 头,response 头 格式如下:
HTTP/1.1 CODE CODEDESCRIPTION \r\n
Content-Type: text/html\r\n
Accept-Ranges: bytes\r\n
Content-Length: %d\r\n\r\n;
CODE和CODEDESCRIPTION中最常见的就是 200 OK和 404 NOT FOUND
因此我们准备如下2个头
const char OKHeadFormat[] =
"HTTP/1.1 200 OK \r\n"
"Content-Type: text/html\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %d\r\n\r\n";
const char NotFoundHeadFormat[] =
"HTTP/1.1 404 NOT FOUND \r\n"
"Content-Type: text/html\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 0\r\n\r\n";
其中OKHead需要继续填充发送的长度,使用时需要调用sprintf函数填充
然后还需要一点文件操作,把HTML文件打开后读入buffer中,通过上面Getpage函数读到的文件名去找,如果找到了返回1,并引用写入buffer,填充并发送200头,如果找不到返回0,发送404头
int FileToBuffer(const string filename, char * &buffer, int &bufferlength)
{
FILE* pFile = NULL;
fopen_s(&pFile, filename.data(), "r");
if (pFile == NULL)
return 0;
fseek(pFile, 0, SEEK_END);//move fseek to EOF
bufferlength = ftell(pFile); //This is the length of file
bufferlength++; // place for'\0'
fseek(pFile, 0, SEEK_SET); //move back
fread(buffer, bufferlength, 1, pFile); //read file to buff
fclose(pFile);
return 1;
}
测试:
我写了1.HTML和2.HTML放在server.CPP所在目录中,端口设置为8888,打开Chrome进行访问
访问默认页面:(chrome中端口后的/可以省略)
访问指定页面:
访问不存在的页面:
完整代码如下:
//Server.cpp
//Author MaverickY
//version 00
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <WinSock2.h>
#include <cstring>
#include <string>
#pragma comment (lib, "Ws2_32.lib")
using namespace std;
const char OKHeadFormat[] =
"HTTP/1.1 200 OK \r\n"
"Content-Type: text/html\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %d\r\n\r\n";
const char NotFoundHeadFormat[] =
"HTTP/1.1 404 NOT FOUND \r\n"
"Content-Type: text/html\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 0\r\n\r\n";
const int PORT = 8888;
const int BUFFER_MAX = 5000;
int FileToBuffer(const string filename, char*& buffer, int& bufferlength);
void GetPage(const char* recv_buff, const int length, string& page);
int main()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serveraddr;
memset((void*)& serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
serveraddr.sin_port = htons(PORT);
bind(s, (struct sockaddr*) & serveraddr, sizeof(serveraddr));
listen(s, SOMAXCONN);
while (1)
{
SOCKET ss = accept(s, NULL, NULL);
cout << "accept one\n" << endl;
string filename;
char recvbuf[BUFFER_MAX];
recv(ss, recvbuf, sizeof(recvbuf), 0);
GetPage(recvbuf,strlen(recvbuf),filename);
char Head[BUFFER_MAX];
int send_buffer_length = BUFFER_MAX;
char* send_buffer = (CHAR*)malloc(send_buffer_length);
memset(send_buffer, 0, send_buffer_length); // set pszBuff all to 0
if (FileToBuffer(filename, send_buffer, send_buffer_length))
{
//send head
sprintf(Head, OKHeadFormat, send_buffer_length);
int byte_sent = send(ss, Head, strlen(Head), 0);
cout << "200 head sent" << endl;
//send content
byte_sent = send(ss, send_buffer, send_buffer_length, 0);
cout << byte_sent << "bytes have been sent" << endl;
}
else // return 404
{
//send head only
sprintf(Head, NotFoundHeadFormat);
int byte_sent = send(ss, Head, strlen(Head), 0);
cout << "404 head sent" << endl;
}
closesocket(ss);
}
int Err = WSAGetLastError();
cout << Err << endl;
WSACleanup();
return 0;
}
int FileToBuffer(const string filename, char * &buffer, int &bufferlength)
{
FILE* pFile = NULL;
fopen_s(&pFile, filename.data(), "r");
if (pFile == NULL)
return 0;
fseek(pFile, 0, SEEK_END);//move fseek to EOF
bufferlength = ftell(pFile); //This is the length of file
bufferlength++; // place for'\0'
fseek(pFile, 0, SEEK_SET); //move back
fread(buffer, bufferlength, 1, pFile); //read file to buff
fclose(pFile);
return 1;
}
void GetPage(const char* recv_buff, const int length, string& filename)
{
string buff = recv_buff;
int location = buff.find(" HTTP", 5);//filename starts at 5th byte of buffer
filename = string(buff, 5, location - 5);
if (filename == "")//default page
filename = "1.HTML";
/*
NOTE:Get head is in this format
GET /filename HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1 ...
*/
}
觉得有帮助请点个赞,谢谢