TCP实现http请求

1.建立与服务器的TCP链接

2.在TCP的链接下 socket的基础下发送http格式的协议请求

3.服务器在tcp链接socket 返回http协议的reponse

首先需要将域名翻译成ip然后通过tcp链接ip地址和端口最后发送http协议

关于文中的 hostent结构体的定义

	struct hostent
	{
		char *h_name;         //正式主机名
		char **h_aliases;     //主机别名
		int h_addrtype;       //主机IP地址类型:IPV4-AF_INET
		int h_length;		  //主机IP地址字节长度,对于IPv4是四字节,即32位
		char **h_addr_list;	  //主机的IP地址列表
	};
	
	#define h_addr h_addr_list[0]   //保存的是IP地址

关于struct sockaddr_in结构体内容定义

struct sockaddr_in{
	sa_family_t   sin_family;   //地址族
	uint16_t      sin_port;     //端口号
	struct in_addr    sin_addr;  //32位IP地址
	char     sin_zero;      //预留未使用
};
struct in_addr{
	In_addr_t  s_addr;    //32位IPv4地址
};

阻塞和非阻塞

如果socket是阻塞 那read()去读取数据的时候 里面没有数据 那也会挂起无法读取到数据便一直等待数据

如果socket是非阻塞 那read()去读数据的时候 就算没有数据也会立刻返回 就不会挂起

做服务器的时候会优先选择非阻塞的io

关于组合报文头部(格式非常重要!!! 

	sprintf(buffer,"GET %s %s\r\n\
Host: %s\r\n\
%s\r\n\
\r\n",
	resource, HTTP_VERSION,
	hostname,
	CONNETION_TYPE
);
//注意 Host,%s,\r前面不能有空格!!!!出现空格格式就会不符合报文规范服务器就会无法识别

下列是本文中实现的tcp链接 服务器请求资源的源码


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

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include <netdb.h>
#include <fcntl.h>

#define HTTP_VERSION		"HTTP/1.1"
#define CONNETION_TYPE		"Connection: close\r\n"

#define BUFFER_SIZE		4096

char* host_ip(const char *hostname) 
{
	struct  hostent *host_entry = gethostbyname(hostname); //这个功能就是上一篇文章中讲述的DNS域名转换成ip的操作
	
	//192.168.1.126 ->
	//inet_ntoa() 将无符号int 转化为 char*
	//0x12121212 -> xxx.xxx.xxx.xxx
	if (host_entry)
	{
		return inet_ntoa(*(struct in_addr*)*host_entry->h_addr_list);
	}
	return NULL;
}

int http_create_socket(char *ip) 
{
	int sockfd = socket(AF_INET,SOCK_STREAM,0);

	struct sockaddr_in sin = { 0 };
	sin.sin_family = AF_INET;
	sin.sin_port = htons(80);  //端口号
	sin.sin_addr.s_addr = inet_addr(ip);   //存储IP  这个inet_addr是和inet_ntoa想法的函数

	if (0 != connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)))
	{
		return -1;
	}
	fcntl(sockfd, F_SETFL, O_NONBLOCK);//设置非阻塞的io

	return sockfd;
}

//hostname 是域名  resource 是域名后面的/后面跟着的
char * http_send_request(const char *hostname , const char *resource)
{
	//域名转ip
	char* ip = host_ip(hostname);

	//与服务器建立链接
	int sockfd = http_create_socket(ip);
	char buffer[BUFFER_SIZE] = {0};

	//组合请求报文头部数据
	sprintf(buffer,"GET %s %s\r\n\
Host: %s\r\n\
%s\r\n\
\r\n",
	resource, HTTP_VERSION,
	hostname,
	CONNETION_TYPE
);

	//发送数据
	send(sockfd,buffer,strlen(buffer),0);

	//接收数据 select
	//select监测网络io中有没有可读数据
	fd_set fdread;  //fd_set是一个集合将所有的网络io放在一个集合中去一起判断有无数据 有数据置1无置0

	FD_ZERO(&fdread);  //先初始化将fd置空 
	FD_SET(sockfd, &fdread); //sockfd 需要监测的网络io ,fd_set集合

	struct timeval tv;
	tv.tv_sec = 5;
	tv.tv_usec = 0;

	char* result = malloc(sizeof(int));
    //使用malloc分配空间时一定要用memset清空 防止指针是无效数据 可能这片内存存放其它数据
	memset(result, 0, sizeof(int));

	while (1) {

		int selection = select(sockfd + 1, &fdread, NULL, NULL, &tv); //select(maxfd+1,&rset 哪个io可读,&wset 哪个io可写.&errorset 哪个io出错,&timeval指select多长时间去遍历一次io
			
		if (!selection || !FD_ISSET(sockfd, &fdread)) { //FD_ISSET用来判断fdread中的数据是否和sockfd中的一致
			break;
		}
		else {
			//清空并重新初始化buffer
			memset(buffer, 0, BUFFER_SIZE);
			int len = recv(sockfd, buffer, BUFFER_SIZE, 0);
			if (len == 0) { // disconnect recv返回0 对方关闭了服务器 导致链接失败
				break;
			}	
			//可能会多次recv去读数据 因为可能一次buffer存放不完	
			result = realloc(result, (strlen(result) + len + 1) * sizeof(char)); //重新分配空间
			strncat(result, buffer, len);
		}

	}
	return result;
}

int main(int argc, char* argv[])
{
	if (argc < 3) return -1;

	char* response = http_send_request(argv[1], argv[2]);
	printf("response : %s\n", response);

	free(response);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值