文章目录
- 1 - 处理HTTP请求
- 1-1 处理POST请求
- 1-2 处理GET请求
1 - 处理HTTP请求
这里只处理两种简单的HTTP请求,POST和GET
首先来了解一下GET与POST
- GET是想获取server数据,将请求的数据添加到URL中,以
?
分割URL和传输数据,参数值之间以&
相连,因此GET不安全。GET产生一个TCP数据包,浏览器将HTTP header和data一起发送给server,server响应状态码200(请求正常处理完毕)(返回数据) - POST是想修改server数据,将数据放在HTTP的包体内(request body)。POST产生2个TCP数据包,浏览器先发送header,server响应状态码100(请求正在处理)后,浏览器才会发送data,然后server返回状态码200
下面介绍一下HTTP响应的基本知识:
- 状态码(Status Code):描述了响应的状态。可以用来检查是否成功的完成了请求。请求失败的情况下,状态码可用来找出失败的原因。
- HTTP头部(HTTP Header):它们包含了更多关于响应的信息。比如:头部可以指定认为响应过期的过期日期,或者是指定用来给用户安全的传输实体内容的编码格式。
- 主体(Body):它包含了响应的内容。它可以包含HTML代码,图片,等等。主体是由传输在HTTP消息中紧跟在头部后面的数据字节组成的。
参考:HTTP响应的结构
1-1 处理POST请求
int requestData::analysisRequest()
{
if (method == METHOD_POST)
{
//get content
char header[MAX_BUFF];//响应header信息:包含响应行、响应头
sprintf(header, "HTTP/1.1 %d %s\r\n", 200, "OK");//向响应行中写入数据,200:请求成功
//server根据请求头获取client信息
// 查看是否需要持久连接。(HTTP 1.1默认进行持久连接)
if(headers.find("Connection") != headers.end() && headers["Connection"] == "keep-alive")
{
keep_alive = true;
sprintf(header, "%sConnection: keep-alive\r\n", header);
sprintf(header, "%sKeep-Alive: timeout=%d\r\n", header, EPOLL_WAIT_TIME);
}
//cout << "content=" << content << endl;
// test char*
//响应体
char *send_content = "I have receiced this.";
//向header中写入body长度
sprintf(header, "%sContent-length: %zu\r\n", header, strlen(send_content));
sprintf(header, "%s\r\n", header);
//发送header
size_t send_len = (size_t)writen(fd, header, strlen(header));
if(send_len != strlen(header))
{
perror("Send header failed");
return ANALYSIS_ERROR;
}
//发送响应体
send_len = (size_t)writen(fd, send_content, strlen(send_content));
if(send_len != strlen(send_content))
{
perror("Send content failed");
return ANALYSIS_ERROR;
}
cout << "content size ==" << content.size() << endl;
vector<char> data(content.begin(), content.end());
//opencv
Mat test = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);
imwrite("receive.bmp", test);
return ANALYSIS_SUCCESS;
}
//处理其他请求
}
处理POST请求的步骤在代码中已经注释,关于opencv再来解释一下:
Mat类 (Matrix的缩写) 是OpenCV用于处理图像而引入的一个封装类
imencode和imdecode,用于网络传输图片,使用流程为:(1)程序首先读入一个图片。然后encode,之后把encode后的内容写入文件(实际应用可以发送到网络)。(2)从文件读取encode的内容。然后解码decode。转换为mat格式,显示出来。
opencv3中的imwrite函数是用来输出图像到文件,相当于保存图片
关于向socket中写数据,用到了一个writen函数,该函数封装write函数,一直向socket中写数据,直到写完所有数据,过程中可能多次调用write函数
///封装write函数,一直向socket中写数据,直到写完所有数据,过程中可能多次调用write函数
ssize_t writen(int fd, void *buff, size_t n)
{
size_t nleft = n;
ssize_t nwritten = 0;
ssize_t writeSum = 0;
char *ptr = (char*)buff;
while (nleft > 0)
{
if ((nwritten = write(fd, ptr, nleft)) <= 0)
{
if (nwritten < 0)
{
if (errno == EINTR || errno == EAGAIN)
{
nwritten = 0;
continue;
}
else
return -1;
}
}
writeSum += nwritten;
nleft -= nwritten;
ptr += nwritten;
}
return writeSum;
}
1-2 处理GET请求
因为GET请求用来从server获取数据,因此server需要解析GET请求的具体资源类型(以MIME区分)
int requestData::analysisRequest()
{
//处理POST请求
else if (method == METHOD_GET)
{
char header[MAX_BUFF];
sprintf(header, "HTTP/1.1 %d %s\r\n", 200, "OK");
if(headers.find("Connection") != headers.end() && headers["Connection"] == "keep-alive")
{
keep_alive = true;
sprintf(header, "%sConnection: keep-alive\r\n", header);
sprintf(header, "%sKeep-Alive: timeout=%d\r\n", header, EPOLL_WAIT_TIME);
}
int dot_pos = file_name.find('.');
const char* filetype;//文件类型,以文件后缀区分
if (dot_pos < 0)
filetype = MimeType::getMime("default").c_str();
else
filetype = MimeType::getMime(file_name.substr(dot_pos)).c_str();
struct stat sbuf;
if (stat(file_name.c_str(), &sbuf) < 0)
{
handleError(fd, 404, "Not Found!");
return ANALYSIS_ERROR;
}
//将包体信息写入首部
sprintf(header, "%sContent-type: %s\r\n", header, filetype);
// 通过Content-length返回文件大小
sprintf(header, "%sContent-length: %ld\r\n", header, sbuf.st_size);
sprintf(header, "%s\r\n", header);
//发送响应报文首部
size_t send_len = (size_t)writen(fd, header, strlen(header));
if(send_len != strlen(header))
{
perror("Send header failed");
return ANALYSIS_ERROR;
}
//取出GET方法请求的文件
int src_fd = open(file_name.c_str(), O_RDONLY, 0);
char *src_addr = static_cast<char*>(mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0));
close(src_fd);
// 发送文件并校验完整性
send_len = writen(fd, src_addr, sbuf.st_size);
if(send_len != sbuf.st_size)
{
perror("Send file failed");
return ANALYSIS_ERROR;
}
munmap(src_addr, sbuf.st_size);
return ANALYSIS_SUCCESS;
}
else
return ANALYSIS_ERROR;
}
介绍一下MIME:
MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
常见的MIME类型有:
文件后缀MIME类型普通文本 .txttext/plain超文本标记语言文本 .htmltext/htmlPDF文档.pdf application/pdfPNG图像 .pngimage/png
使用MimeType::getMime函数根据传入的文件后缀获取对应的MIME类型
std::string MimeType::getMime(const std::string &suffix)
{
if (mime.size() == 0)
{
pthread_mutex_lock(&lock);
if (mime.size() == 0)
{
mime[".html"] = "text/html";
mime[".avi"] = "video/x-msvideo";
mime[".bmp"] = "image/bmp";
mime[".c"] = "text/plain";
mime[".doc"] = "application/msword";
mime[".gif"] = "image/gif";
mime[".gz"] = "application/x-gzip";
mime[".htm"] = "text/html";
mime[".ico"] = "application/x-ico";
mime[".jpg"] = "image/jpeg";
mime[".png"] = "image/png";
mime[".txt"] = "text/plain";
mime[".mp3"] = "audio/mp3";
mime["default"] = "text/html";
}
pthread_mutex_unlock(&lock);
}
if (mime.find(suffix) == mime.end())
return mime["default"];
else
return mime[suffix];
}
stat函数声明为:
///通过文件名filename获取文件信息,并保存在buf所指的结构体stat中
///执行成功则返回0,失败返回-1,错误代码存于errno
int stat(const char *file_name, struct stat *buf);
如果server没有GET请求的资源的话,使用handleError函数处理,并返回状态码404(Not Found)表示server没有该资源
void requestData::handleError(int fd, int err_num, string short_msg)
{
short_msg = " " + short_msg;
char send_buff[MAX_BUFF];
string body_buff, header_buff;
body_buff += "<html><title>TKeed Error</title>";
body_buff += "<body bgcolor=\"ffffff\">";
body_buff += to_string(err_num) + short_msg;
body_buff += "<hr><em> LinYa's Web Server</em>\n</body></html>";
header_buff += "HTTP/1.1 " + to_string(err_num) + short_msg + "\r\n";
header_buff += "Content-type: text/html\r\n";
header_buff += "Connection: close\r\n";
header_buff += "Content-length: " + to_string(body_buff.size()) + "\r\n";
header_buff += "\r\n";
sprintf(send_buff, "%s", header_buff.c_str());
writen(fd, send_buff, strlen(send_buff));
sprintf(send_buff, "%s", body_buff.c_str());
writen(fd, send_buff, strlen(send_buff));
}