您的位置:首页 > 理论基础 > 计算机网络

HTTP - TCP实现HTTP GET请求 (2)

2016-08-22 17:13 211 查看
我们尝试用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;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: