一个简单的http服务器的实现
tkorays(tkorays@hotmail.com)
道理本身不重要,重要的是道理的实践。
想要写一个http服务器,其实也没有那么复杂?下面就来介绍一个简单的实现(windows平台),大致能够说明基本原理。
简单实现
如果你写过tcp的应用的话,你会发现,其实它就是一个tcp服务器。因此我们完全可以把tcp的一些代码直接复制过来,下面的代码是一个简单的例子,大致步骤:
- 创建服务器端socket
- 创建服务器地址数据结构sockaddr_in
- 绑定端口,开始监听
- 循环,接收客户端的连接,从连接中获取客户端的socket以及地址sockaddr_in
- 接受数据,之后关闭socket等待下一次连接
具体代码如下:
#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
int main(int argc, char** argv) {
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
// 服务器的socket
SOCKET server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in sv_sock_addr; // 服务器的地址
sv_sock_addr.sin_family = AF_INET;
sv_sock_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 服务器地址
sv_sock_addr.sin_port = ntohs(8899); // 服务器的端口
bind(server_sock, (sockaddr*)&sv_sock_addr, sizeof(sockaddr_in)); // 绑定
listen(server_sock, 1); // 监听
int addr_len = sizeof(sockaddr_in);
sockaddr_in cl_sock_addr; // 客户端地址
char buf[2048];
while (true) {
// 客户端的socket
SOCKET client_sock = accept(server_sock, (sockaddr*)&cl_sock_addr, &addr_len);
ZeroMemory(buf, 2048);
recv(client_sock, buf, 2048, 0); // 接收数据
printf("%s", buf);
closesocket(client_sock);
}
closesocket(server_sock);
WSACleanup();
return 0;
}
运行,打开浏览器,输入http://127.0.0.1:8899就可以看到控制台里面输出浏览器的请求:
而浏览器里面却访问错误?
添加响应
浏览器没有任何东西,这是因为,我们对于浏览器的请求并没有做出回应,因此,我们可以添加一些代码。
在使用http协议时,注意下http响应的写法(可以参考http标准),下面的代码中简单地返回了带有响应头的字符串。这里没有解析接受的请求。
int addr_len = sizeof(sockaddr_in);
sockaddr_in cl_sock_addr; // 客户端地址
char buf[2048];
char retstr[] = "HTTP/1.1 200 OK\r\nServer:tkorays/0.0.1\r\nContent-Type: text/html\r\n\r\nhello world!";
while (true) {
// 客户端的socket
SOCKET client_sock = accept(server_sock, (sockaddr*)&cl_sock_addr, &addr_len);
ZeroMemory(buf, 2048);
recv(client_sock, buf, 2048, 0); // 接收数据
printf("%s", buf);
send(client_sock, retstr, strlen(retstr), 0);
closesocket(client_sock);
}
closesocket(server_sock);
运行后再访问刚才的地址,发现浏览器里面出现了:
如果是用火狐的开发者工具查看响应,你会看到下面的:
就是我们刚才发送的响应头,响应的内容为“hello world!”
缺点&&下一步如何做?
上面简单地实现了一个http服务器,但是缺点也是不少的。
- 它没有解析请求,GET / 和GET /abc返回的都是一样的。
- 返回的东西已经写死,不能根据请求动态地返回,因此这里可以实现根据请求加载html文件。
- 响应头没有实现,对于浏览器的访问,服务器不是所有时候都是200 ok的。
- 每次连接后都会关闭客户端socket,完全可以保持连接状态,等待超时后再断开连接。
- 不支持并发,这里使用循环,如果同一时间还有其他的人访问服务器,那么他必须等待其他人访问完才行。