HTTP与SOCKET

1. 前言:

    最近对HTTP与SOCKET很好奇。所以详细探究了下,探究过程整理如下:

2. 概念

    SOCKET 对TCP/IP协议封装的接口,非协议。有TCP、UDP(后续再详谈)的区别,分长连接和短连接。

    HTTP 基于TCP/IP协议的协议,协议。只分长短链接(HTTP1.1协议支持长连接后续再谈

    我一般都认为HTTP是一种特殊的SOCEKT。其特殊性,请看下面的代码(win10 64位,vs2013,C++代码示例)。

3. 客户端代码

    3.1 SOCKET TCP 代码

//
//\
//\				socket TCP client
//\
//

#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>

#include <winsock2.h>
#include <windows.h>

#pragma comment(lib, "ws2_32.lib")  /* WinSock使用的库函数 */

using namespace std;

int main()
{
	// 初始化WSA  
	WSADATA ws;
	WSAStartup(MAKEWORD(2, 0), &ws);

	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(9999);		// SOCKET服务端监听端口
	addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");		// socket服务端IP

	// 创建socket
	SOCKET fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (fd == INVALID_SOCKET)
	{
		printf("socket error!\n");
		return -1;
	}

	// 链接服务端
	int ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
	if (ret == SOCKET_ERROR)
	{
		printf("connect failed!\n");
		// 断开socket
		closesocket(fd);
		return -1;
	}

	// 发送信息
	char *data = "hello server! I'm socket client!\n";
	ret = send(fd, data, strlen(data), 0);
	if (ret <= 0)
	{
		printf("send error!\n");
		// 断开socket
		closesocket(fd);
		return -1;
	}

	// 接收服务端信息
	char buf[1024] = { 0 };
	ret = recv(fd, buf, 1024, 0);
	if (ret <= 0)
	{
		printf("recv error!\n");
		// 断开socket
		closesocket(fd);
		return -1;
	}
	printf("recv[%s]\n", buf);
	// 断开socket
	closesocket(fd);
	// 逆初始化wsa
	WSACleanup();
	return 0;
}

3.2 HTTP 代码

//
//\
//\				HTTP client
//\
//
#include <iostream>
#include <string>
#include <string.h>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>

#include <winsock2.h>
#include <windows.h>

#pragma comment(lib, "ws2_32.lib")  /* WinSock使用的库函数 */

using namespace std;

// 通过url解析出IP和端口
bool ParseUrl(string &url, string &ip, int &port)
{
	// 去除http://
	size_t pos = url.find("//");
	if (pos == string::npos)
	{
		printf("url error! [url:%s]\n", url.c_str());
		return false;
	}

	string tmp = url.substr(pos + 2);
	pos = tmp.find(":");

	ip = tmp.substr(0, pos);
	port = atoi(tmp.substr(pos + 1).c_str());
	return true;
}

void PostData(string &content, string &url)
{
	// 解析IP和PORT
	string ip = "";
	int port = 0;
	if (ParseUrl(url, ip, port) == false)
		return;

	SOCKET s_fd;
	sockaddr_in c_addr;
	c_addr.sin_family = AF_INET;
	c_addr.sin_port = htons(port);
	c_addr.sin_addr.S_un.S_addr = inet_addr(ip.c_str());

	s_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	int ret = connect(s_fd, (struct sockaddr *)&c_addr, sizeof(c_addr));
	if (ret == SOCKET_ERROR)
	{
		printf("connect error!\n");
		closesocket(s_fd);
		return;
	}

	// HTTP 头部必须有POST(或者同类别的其他字段),POST是body可以为空
    // 但是必须有,body必须与header间隔两个回车换行
    // POST 后面紧跟的/是路径
	stringstream str("");
    // 若url为 http://192.168.0.151:8080/test则应该修改为: POST /test HTTP/1.1\r\n
	str << "POST / HTTP/1.1\r\n";
	str << "Host:" << url << "\r\n";
	str << "Content-Length:" << content.length() << "\r\n\r\n";
	str << content;

	int len = send(s_fd, str.str().c_str(), str.str().length(), 0);
	if (len <= 0)
	{
		printf("Post error!\n");
		closesocket(s_fd);
		return;
	}

	char buf[1025] = { 0 };
	int recv_len = 0;
	do
	{
		recv_len = recv(s_fd, buf, 1024, 0);
		if (recv_len > 0)
		{
			/* 在屏幕上输出 */
			buf[recv_len] = 0;
			printf("%s", buf);
		}

		if (recv_len < 1024)
			break;
	} while (recv_len > 0);

	printf("\n");
	// 发送完毕后,断开socket
	closesocket(s_fd);
}

int main()
{
	WSADATA ws;
	WSAStartup(MAKEWORD(2, 0), &ws);

	string test = "1 2 3 4";		// HTTP body
	string url = "http://192.168.0.151:8080";
	PostData(test, url);

	WSACleanup();
	return 0;
}

4. 服务端代码

  

#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <winsock2.h> 
#include <windows.h>

#pragma comment(lib,"ws2_32.lib") 

using namespace std;

string response = "HTTP/1.1 200 OK\r\n\Content-Length: 14\r\n\Content-Type : text / plain\r\n\r\n\{ \"status\":200 }";

int main()
{
	//初始化WSA  
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	if (WSAStartup(sockVersion, &wsaData) != 0)
	{
		return 0;
	}

	// 创建套接字
	SOCKET s_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (s_fd == INVALID_SOCKET)
	{
		printf("socket error!\n");
		return -1;
	}

	// 绑定IP和端口
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(8090);
	sin.sin_addr.s_addr = inet_addr("127.0.0.1");
	if (bind(s_fd, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
	{
		printf("bind error!\n");
		return 0;
	}

	// 开始监听
	if (listen(s_fd, 5) == SOCKET_ERROR)
	{
		printf("listen error!\n");
		return 0;
	}

	// 循环接收数据
	SOCKET c_fd;
	sockaddr_in client;
	int len = sizeof(client);
	char tmp[1024] = { 0 };
	while (true)
	{
		printf("等待链接...\n");
		c_fd = accept(s_fd, (SOCKADDR *)&client, &len);
		if (c_fd == INVALID_SOCKET)
		{
			printf("connect error!\n");
			continue;
		}

		printf("接收到一个链接: %s \n", inet_ntoa(client.sin_addr));

		int ret = recv(c_fd, tmp, 1024, 0);
		if (ret > 0)
		{
			printf("接收内容为: [tmp:%s]\n", tmp);
		}

		// 回复
		send(c_fd, response.c_str(), response.length(), 0);
		memset(tmp, 0, 1024);
		// 关闭句柄
		closesocket(c_fd);
	}

	closesocket(s_fd);
	WSACleanup();
	return 0;
}

5. 总结

    HTTP通信方式与SOCKET短连接一样,只不过在发送的消息格式以及内容上有些要求。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值