这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战
想必大家在面试过程中一定或多或少会问到网络相关的知识,其中出现频率最高的热词无疑是TCP、UDP、http,https等内容,为了更好的理解这些概念、了解webserve的工作原理和了解一些c语言在linux上的接口的使用,所以我决定自己做一个基于socket可以解析http请求的httpserve,话不多说开始做!
首先我们必须要理解什么是socket?
socket-
1.套接字
-
2. n. (电源)插座;(电器)插口,插孔;(人体的)窝,槽;(高尔夫插球杆的)棒头承口;(用以插入某物使其转动的)承窝,轴孔
网络套接字在计算机科学中是电脑网络中进程间资料流的端点。使用以网际协议为通信基础的网络套接字,称为网际套接字。因为网际协议的流行,现代绝大多数的网络套接字,都是属于网际套接字。 socket是一种操作系统提供的进程间通信机制。 在操作系统中,通常会为应用程序提供一组应用程序接口,称为套接字接口。 维基百科
通俗理解就是如果两个进程如果想要进行数据交换就必须建立连接,socket就是解决这个问题的,socket这个名字也很形象哈,想要传输数据就要连接起来,就需要接口(socket)
想要建立socket连接,咱也要知道和谁建立连接才可以啊。
这就需要 ip部分的知识了。
这部分知识就不多说了 接下来我会再写文章总结
我们现在只需要知道一个ip和一个端口和ipv4/ipv6的协议版本就ok
找到了连接对象以后怎么发信息呢?
tcp和udp 就是我们的信息快递员,他们负责把我们的信息送到我们建立连接的两端 这部分知识在这里也不详细展开
好了到这里 我们一切工作都做好了
但是 怎么实现httpserve呢?
信息 发送过去了 我们要分析信息里的内容啊,如果我们想要实现httpserve,那肯定要对http的报文结构有详细的了解,每一行的信息的内容内涵,报文的结构,
针对着报文结构 我们可以读取我们想要的信息
下边是源码。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>
#include <iostream>
using namespace std;
void *thread_fun(void *arg){
int fdc = *(int *)arg;
// 5. 开始通信
while (1)
{
char buff[128*1024];
memset(buff,0,sizeof(buff));
int len = read(fdc,buff,sizeof(buff));
char ip[30] = { 0 };
if(len>0){
printf("客户端:%s\n",buff);
string http_body = "<h1 >hello web</h1><h1>";
// 解析报文 生成 http_body
int i=0;
while (1)
{
if(buff[i] != '\r' && buff[i+1] != '\n' && buff[i] != '\n'){
http_body.push_back(buff[i]);
}else{
http_body.append("</h1><h1>");
}
i++;
if(buff[i]=='\r' && buff[i+1]=='\n' && buff[i+2]=='\r' && buff[i+3]=='\n')
{
break;
}
}
http_body.append("/h1");
// HTTP 报文 第一部分 协议状态码 和 协议版本
string http_message = "HTTP/1.1 200 \r\n";
// HTTP 报文 第二部分 header
http_message += "Content-Type: text/html;charset=UTF-8 \r\n";
http_message += "\r\n\r\n";
// HTTP 报文 第三部分 body
http_message += http_body;
int len2 = http_message.length();
write(fdc,http_message.c_str(),len2);
close(fdc);
}
if(len == 0){
printf("客户端链接已经断开!!");
break;
}
}
pthread_exit(NULL);
}
int main()
{
// 1. 创建监听套接字
int fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(fd == -1){
perror("fail to socket");
exit(1);
}
// 2. 绑定本地的ip和端口
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr)); //每个字节都用0填充
addr.sin_family = AF_INET;
// addr.sin_addr.s_addr = inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr.s_addr);
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
addr.sin_port = htons(9988);
int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr));
if(ret == -1){
perror("fail to bind");
exit(1);
}
// 3. 监听
ret = listen(fd,128);
if(ret == -1){
perror("listen fail");
exit(-1);
}
while (1)
{
// 4. 接收accept(阻塞,等待链接到达)
struct sockaddr_in caddr;
socklen_t caddrlen = sizeof(caddr);
int fdc = accept(fd,(struct sockaddr *)&caddr,&caddrlen);
if(fdc == -1){
perror("accept fail");
exit(1);
}
//创建线程
pthread_t thread;
pthread_create(&thread,NULL,thread_fun,&fdc);
pthread_detach(thread);
}
// 6. 关闭套接字
close(fd);
return 0;
}