HTTP - TCP实现HTTP GET请求
Posted zj510
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HTTP - TCP实现HTTP GET请求相关的知识,希望对你有一定的参考价值。
我们尝试用TCP来实现HTTP GET
基本TCP通信
首先写一段TCP通信的例子。这是一些测试代码,不要看代码质量,我们目的只是验证。
服务器:
先写个服务器,这里用了select。
// SelectServer.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <winsock2.h>
#include "boost/timer.hpp"
#pragma comment(lib,"ws2_32.lib")
#define PORT 1688
bool InitAndListen(SOCKET &sListen)
WSADATA wsaData;
sockaddr_in local;
WORD version=MAKEWORD(2,0);
int ret=WSAStartup(version,&wsaData);
if(ret != 0)
printf("WASStarup failed/n");
return 0;
local.sin_family=AF_INET;
local.sin_addr.s_addr=INADDR_ANY;
local.sin_port=htons((u_short)PORT);
//Initial socket
sListen=socket(AF_INET,SOCK_STREAM,0);
if(sListen == INVALID_SOCKET)
printf("Initial socket failed/n");
return 0;
//Bind socket
if(bind(sListen,(sockaddr*)&local,sizeof(local))!=0)
printf("Bind socket failed/n");
return 0;
//Listen socket
if(listen(sListen,10)!=0)
printf("Listen socket failed");
return 0;
return 1;
void RunService()
SOCKET sListen;
if(InitAndListen(sListen) == 0)
return;
printf("Server wait for client connect...\\n");
fd_set fdSocket;
FD_ZERO(&fdSocket);
//Add the listen socket to FD_set : fdSocket
FD_SET(sListen,&fdSocket);
while (true)
//assign the fdSocket to fdRead to select
fd_set fdRead = fdSocket;
timeval t;
t.tv_sec = 2;
t.tv_usec = 1;
boost::timer elapsed;
int nRet = select(NULL,&fdRead,NULL,NULL,NULL);//设置timeout为NULL,那么select会一直等下去,最多支持64个
printf("select elapsed: %f s\\n", elapsed.elapsed());
if (nRet <= 0)
printf("select failed/n");
break;
for(int i=0;i<(int)fdSocket.fd_count;i++)
//check whether the socket is set
if(FD_ISSET(fdSocket.fd_array[i],&fdRead))
//New connect come
if(fdSocket.fd_array[i] == sListen)
sockaddr_in addrRemote;
int nAddrLen=sizeof(addrRemote);
SOCKET sNew=::accept(sListen,(sockaddr*)&addrRemote,&nAddrLen);
FD_SET(sNew,&fdSocket);//Put it to fdSocket sets
printf("Client %s connected\\n",inet_ntoa(addrRemote.sin_addr));
else
char buffer[1024];
memset(buffer,0,1024);
int nRecev = recv(fdSocket.fd_array[i],buffer,
1024,0);
if (nRecev > 0)
printf("Received Client Msg:%s\\n",buffer);
//echo back
::send(fdSocket.fd_array[i],buffer,
strlen(buffer),0);
else
printf("client disconnected\\n");
//Close the socket and clear from the sets
closesocket(fdSocket.fd_array[i]);
FD_CLR(fdSocket.fd_array[i],&fdSocket);
int _tmain(int argc, _TCHAR* argv[])
RunService();
return 0;
这个服务器很简单,就是收到客户连接,如果客户有数据发过来,就再发回去。
客户端:
int Test1()
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR)
wprintf(L"WSAStartup function failed with error: %d\\n", iResult);
return 1;
//----------------------
// Create a SOCKET for connecting to server
SOCKET ConnectSocket;
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET)
wprintf(L"socket function failed with error: %ld\\n", WSAGetLastError());
WSACleanup();
return 1;
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
clientService.sin_port = htons(1688);
//----------------------
// Connect to server.
iResult = connect(ConnectSocket, (SOCKADDR *) & clientService, sizeof (clientService));
if (iResult == SOCKET_ERROR)
wprintf(L"connect function failed with error: %ld\\n", WSAGetLastError());
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\\n", WSAGetLastError());
WSACleanup();
return 1;
wprintf(L"Connected to server.\\n");
char buf[] = "hello world.abc";
send(ConnectSocket, buf, sizeof(buf), 0);
char received[100] = 0;
int r = recv(ConnectSocket, received, 100, 0);
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\\n", WSAGetLastError());
WSACleanup();
return 1;
WSACleanup();
很简单的客户端代码,往服务器发一些数据,再接收服务器的数据。
先运行服务端,在运行客户端,就会得到一下结果:
我们可以看到客户端收到了服务端的数据,其实就是客户端自己发给服务端的数据hello world.abc
这就是一个很简单的TCP通信例子。
如果配合前面介绍的,我们可以知道,当客户端发送数据"hello world.abc"的时候,send函数内部会加上TCP头,然后网络层会加上IP头,然后数据链路层会加上以太网头。
最终在物理网路上传播的数据类似:以太网头 + IP头 + TCP头 + 用户层数据(hello world.abc)。当然如果用户层数据多的话,一帧放不下,还会分片传播。
当服务器从网卡上收到数据的时候,就是反过来,从数据链路层=》网络层=》传输层=》应用层,一步一步把头给去掉,最终得到数据hello world.abc
应用层协议
到底什么是应用层协议呢?
我们上面的例子是没有什么意义的。假如我们的场景是用户登录,那么客户端就需要发送用户名和密码。怎么发呢?
简单点,我们约定好,客户端发上来的就是格式: 用户名;密码。也就是中间用分号隔开。
那么当服务器收到的时候就知道用分号来分割字符串,得到用户名和密码。
这就是一个自定义协议。
所谓协议,其实就是通信双方事先约定好的一些规则。
当然上面的协议只是一个例子,很傻的。
实际应用不太会用这么啥的办法,协议总是要尽可能定义的完善,通用。比如HTTP, FTP等,都是常见的协议。
HTTP协议
有关HTTP协议,可以查看RFC文档,里面有具体的定义,这里只是用TCP自己模拟一下HTTP访问。
以百度为例,先抓包看看浏览器访问百度的时候到底发送了什么数据包。我这里使用soapui来看:
可以很清楚的看到左边的HTTP GET 请求。
那么我们可以把刚才的代码改一下:
int Test2()
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR)
wprintf(L"WSAStartup function failed with error: %d\\n", iResult);
return 1;
//----------------------
// Create a SOCKET for connecting to server
SOCKET ConnectSocket;
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET)
wprintf(L"socket function failed with error: %ld\\n", WSAGetLastError());
WSACleanup();
return 1;
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr("115.239.211.112");
clientService.sin_port = htons(80);
//----------------------
// Connect to server.
iResult = connect(ConnectSocket, (SOCKADDR *) & clientService, sizeof (clientService));
if (iResult == SOCKET_ERROR)
wprintf(L"connect function failed with error: %ld\\n", WSAGetLastError());
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\\n", WSAGetLastError());
WSACleanup();
return 1;
wprintf(L"Connected to server.\\n");
char buf[] = "GET / HTTP/1.1\\r\\n"
"Accept-Encoding: gzip,deflate\\r\\n"
"Host: www.baidu.com\\r\\n"
"Connection: Keep-Alive\\r\\n"
"User-Agent: Apache-HttpClient/4.1.1 (java 1.5)\\r\\n\\r\\n"
;
send(ConnectSocket, buf, sizeof(buf), 0);
char received[1000] = 0;
int r = recv(ConnectSocket, received, 1000, 0);
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\\n", WSAGetLastError());
WSACleanup();
return 1;
WSACleanup();
这里没有写DNS解析,所以直接用百度的ip(ping一下就知道)。
115.239.211.112
上面这个就是ping得到的百度ip,那么大家都知道HTTP服务的监听端口是80,所以改成80.
然后发送的数据是:
GET http://www.baidu.com/ HTTP/1.1
Accept-Encoding: gzip,deflate
Host: www.baidu.com
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
而这个就是HTTP协议的header。这个就是实现约定好的规则,服务器和客户端都遵守。
运行一下:
得到了服务器的返回。当然这里只是傻傻的收固定的字节,实际上是应该根据HTTP协议把所有的数据收完的。这里只是例子,无所谓。
所以说,HTTP是应用层协议,它是基于TCP协议实现(听说也有基于UDP的,但是少见)。
HTTP一般是无状态的,也就是收到服务器的响应就断开连接了,实际上这也只是HTTP协议的一种规定而已。
实际上,我们不太可能自己用TCP去实现HTTP,像这种通用的协议,别人早就实现好了,拿来用就行,我们所要知道的是HTTP到底是怎么回事,怎么来的。
至于HTTP的具体细节,查看RFC文档就行了,像什么GET, POST, PUT, DELETE, KEEP-ALIVE, CHUNKED等等。
附
服务器代码:
// SelectServer.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <winsock2.h>
#include "boost/timer.hpp"
#pragma comment(lib,"ws2_32.lib")
#define PORT 1688
bool InitAndListen(SOCKET &sListen)
WSADATA wsaData;
sockaddr_in local;
WORD version=MAKEWORD(2,0);
int ret=WSAStartup(version,&wsaData);
if(ret != 0)
printf("WASStarup failed/n");
return 0;
local.sin_family=AF_INET;
local.sin_addr.s_addr=INADDR_ANY;
local.sin_port=htons((u_short)PORT);
//Initial socket
sListen=socket(AF_INET,SOCK_STREAM,0);
if(sListen == INVALID_SOCKET)
printf("Initial socket failed/n");
return 0;
//Bind socket
if(bind(sListen,(sockaddr*)&local,sizeof(local))!=0)
printf("Bind socket failed/n");
return 0;
//Listen socket
if(listen(sListen,10)!=0)
printf("Listen socket failed");
return 0;
return 1;
void RunService()
SOCKET sListen;
if(InitAndListen(sListen) == 0)
return;
printf("Server wait for client connect...\\n");
fd_set fdSocket;
FD_ZERO(&fdSocket);
//Add the listen socket to FD_set : fdSocket
FD_SET(sListen,&fdSocket);
while (true)
//assign the fdSocket to fdRead to select
fd_set fdRead = fdSocket;
timeval t;
t.tv_sec = 2;
t.tv_usec = 1;
boost::timer elapsed;
int nRet = select(NULL,&fdRead,NULL,NULL,NULL);//设置timeout为NULL,那么select会一直等下去,最多支持64个
printf("select elapsed: %f s\\n", elapsed.elapsed());
if (nRet <= 0)
printf("select failed/n");
break;
for(int i=0;i<(int)fdSocket.fd_count;i++)
//check whether the socket is set
if(FD_ISSET(fdSocket.fd_array[i],&fdRead))
//New connect come
if(fdSocket.fd_array[i] == sListen)
sockaddr_in addrRemote;
int nAddrLen=sizeof(addrRemote);
SOCKET sNew=::accept(sListen,(sockaddr*)&addrRemote,&nAddrLen);
FD_SET(sNew,&fdSocket);//Put it to fdSocket sets
printf("Client %s connected\\n",inet_ntoa(addrRemote.sin_addr));
else
char buffer[1024];
memset(buffer,0,1024);
int nRecev = recv(fdSocket.fd_array[i],buffer,
1024,0);
if (nRecev > 0)
printf("Received Client Msg:%s\\n",buffer);
//echo back
::send(fdSocket.fd_array[i],buffer,
strlen(buffer),0);
else
printf("client disconnected\\n");
//Close the socket and clear from the sets
closesocket(fdSocket.fd_array[i]);
FD_CLR(fdSocket.fd_array[i],&fdSocket);
int _tmain(int argc, _TCHAR* argv[])
RunService();
return 0;
客户端代码:
// TestSocket.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <string>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int Test1()
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR)
wprintf(L"WSAStartup function failed with error: %d\\n", iResult);
return 1;
//----------------------
// Create a SOCKET for connecting to server
SOCKET ConnectSocket;
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET)
wprintf(L"socket function failed with error: %ld\\n", WSAGetLastError());
WSACleanup();
return 1;
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
clientService.sin_port = htons(1688);
//----------------------
// Connect to server.
iResult = connect(ConnectSocket, (SOCKADDR *) & clientService, sizeof (clientService));
if (iResult == SOCKET_ERROR)
wprintf(L"connect function failed with error: %ld\\n", WSAGetLastError());
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\\n", WSAGetLastError());
WSACleanup();
return 1;
wprintf(L"Connected to server.\\n");
char buf[] = "hello world.abc";
send(ConnectSocket, buf, sizeof(buf), 0);
char received[100] = 0;
int r = recv(ConnectSocket, received, 100, 0);
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\\n", WSAGetLastError());
WSACleanup();
return 1;
WSACleanup();
int Test2()
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR)
wprintf(L"WSAStartup function failed with error: %d\\n", iResult);
return 1;
//----------------------
// Create a SOCKET for connecting to server
SOCKET ConnectSocket;
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET)
wprintf(L"socket function failed with error: %ld\\n", WSAGetLastError());
WSACleanup();
return 1;
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr("115.239.211.112");
clientService.sin_port = htons(80);
//----------------------
// Connect to server.
iResult = connect(ConnectSocket, (SOCKADDR *) & clientService, sizeof (clientService));
if (iResult == SOCKET_ERROR)
wprintf(L"connect function failed with error: %ld\\n", WSAGetLastError());
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\\n", WSAGetLastError());
WSACleanup();
return 1;
wprintf(L"Connected to server.\\n");
char buf[] = "GET / HTTP/1.1\\r\\n"
"Accept-Encoding: gzip,deflate\\r\\n"
"Host: www.baidu.com\\r\\n"
"Connection: Keep-Alive\\r\\n"
"User-Agent: Apache-HttpClient/4.1.1 (java 1.5)\\r\\n\\r\\n"
;
send(ConnectSocket, buf, sizeof(buf), 0);
char received[2000] = 0;
int r = recv(ConnectSocket, received, 2000, 0);
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\\n", WSAGetLastError());
WSACleanup();
return 1;
WSACleanup();
int main(int argc, char **argv)
Test2();
return 0;
以上是关于HTTP - TCP实现HTTP GET请求的主要内容,如果未能解决你的问题,请参考以下文章