Linux套接子(c语言)模拟http请求、应答
有关套接子和http请求报文的博客在CSDN有很多比如,点这里查看,这里我就不再做过多赘述了,下面我们直接实战,模拟http请求。
要求:浏览器访问本地的localhost,在浏览器页面打印出 Hello World
首先:ping 一下百度的网址得到一个百度的ip,我们可以利用这个ip来查看http应答报头
39.156.69.79这是我们得到的百度的ip,事实上我下面用到的代码是另一个ip(220.181.112.244 是 baidu.com 另一个 ip 地址)。代码呈上
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(){
int sockfd;
int len;
struct sockaddr_in address;
int result;
char *strings="GET / HTTP/1.1\r\nHost: 220.181.112.244\r\nConnection: Close\r\n\r\n";
char ch;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("220.181.112.244");
address.sin_port = htons(80);
len = sizeof(address);
result = connect(sockfd, (struct sockaddr *)&address, len);
if(result == -1){
perror("oops: client1");
return 1;
}
write(sockfd,strings,strlen(strings));
while(read(sockfd,&ch, 1)){
printf("%c", ch);
}
close(sockfd);
return 0;
}
通过上面这段代码我们可以得到百度的http应答格式,以便于我们模仿这种格式写出我们自己的服务端代码。浏览器返回的信息如下所示:
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: no-cache
Content-Length: 14615
Content-Type: text/html
Date: Thu, 24 Oct 2019 18:06:08 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Pragma: no-cache
Server: BWS/1.1
Set-Cookie: BAIDUID=0F88738275DFC145AFA84697D48B8087:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=0F88738275DFC145AFA84697D48B8087; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1571940368; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BAIDUID=0F88738275DFC1456A9A4114F710FF71:FG=1; max-age=31536000; expires=Fri, 23-Oct-20 18:06:08 GMT; domain=.baidu.com; path=/; version=1; comment=bd
Traceid: 1571940368268139802612049320315244439823
Vary: Accept-Encoding
X-Ua-Compatible: IE=Edge,chrome=1
Connection: close<!DOCTYPE html><!--STATUS OK-->
<html>
<head>…… 这里省略一大堆内容 ……
</body></html>
以上我们可以得到:
- HTTP/1.1 200 OK //1.1表示http协议版本 200为状态码 OK为状态描述
- Accept-Ranges: bytes //标识自身支持范围请求 更多
- Cache-Control: no-cache //指定请求和响应遵循的缓存机制
- Content-Length: 14615 //要发送的内容长度
- Connection: close //短连接,表示服务器给客户端发送信息之后就断开了
其次:是服务器端的代码,这里我使用了多线程,以便实现多个客户端(页面)同时访问
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
//套接子通讯服务端,全双工
//多线程
void thread_fun(void* arg)
{
int c = (int)arg;
//保存http应答头部信息
char Http[2048] = "HTTP/1.1 200 OK\r\nAccept-Ranges: bytes\r\n\
Content-Length: 13\r\nConnection: close\r\n\\r\n";
//客户端要接收的内容,与头部中的Content-Length对应
char buff[128] = "hello world!";//发送的数据
strcpy(Http, buff);
char tmp[1024] = {0};//接受客户端请求
int n = recv(c,tmp,1023,0);
printf("%s\n",tmp);//打印客户端请求
send(c,Http,strlen(Http),0);//向客户端应答
close(c);//每个客户端只接收一次就关闭
printf("client close\n");
}
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);//文件标识符
assert( sockfd != -1);
struct sockaddr_in saddr,caddr;//ipv4地址结构
memset(&saddr, 0 , sizeof(saddr));
saddr.sin_family = AF_INET;//地址族
saddr.sin_port = htons(80);//大端,网络字节序列
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");//自己主机ip
int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));/*绑定监听的ip地址和端口*//*端口或ip错误会导致绑定失败*/
assert( res != -1);
listen(sockfd, 5);//创建监听队列,开始监听,不会阻塞
//循环执行多次响应 注:如果不使用多线程或多进程的方式处理,只能同时响应一个客户端,其他客户端在监听队列排队,直到上一个客户端关闭,这里使用了多线程
while(1)
{
int len = sizeof(caddr);
int c = accept(sockfd, (struct sockaddr*)&caddr, &len);//c为监听套接子
if( c < 0)
{
continue;//如果接收失败,重新接收
}
//printf("accept c = %d\n",c);//打印文件描述符,表示这是第几个客户端,其中0、1、2号不可用,为标准输入、输出、错误输出,3号为sockfd,因此客户端从4开始
printf("accept(ip:%s,port:%d) c = %d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),c);
//多线程
pthread_t id;
pthread_create(&id,NULL,thread_fun,(void*)c);//这里没有传递c的地址,防止c多次使用时值被改变
}
}
至此,服务端就完成了,由于我们使用了80号端口,所以运行时请用管理员权限。另外,服务端关闭后服务端的状态不会马上成为CLOSED状态,此时服务端处于TIME_WAIT状态,两分钟左右后才会完全关闭,服务端才能再次运行,具体原理请查资料TCP状态转移图 。用到的命令主要有以下几个:
编译: $gcc -o ser_http ser_http.c -pthread
运行: $sudo ./ser_http
$netstat -natp 此命令可查看使用TCP网络资源的进程
最后:我们根据服务端接受的来自网页的请求报文,还可以自己写一个客户端的代码
步骤:运行服务端,浏览器输入 localhost 回车,在服务端上已经打印出了来自浏览器的请求报文,如下所示:
accept(ip:127.0.0.1,port:37022) c = 4
GET / HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0
Accept: image/webp,*/*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
以上我们可以得到:
- GET / HTTP/1.1 //用GET方式请求,http协议,版本1.1
- Host: localhost //本地主机
- User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0 //用户代理:操作系统、浏览器等的信息
- Connection: keep-alive //长链接
根据以上信息模拟客户端的http请求,代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM, 0);
assert( sockfd != -1);
struct sockaddr_in saddr;
memset(&saddr, 0 , sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(80);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = connect(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
assert( res != -1);
//模拟http请求报头
char str[1024] = "GET / HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: Close\r\n\r\n";
send(sockfd, str, strlen(str), 0);//发送请求
memset(str, 0, 1024);
recv(sockfd,str,1023,0);//接受
printf("%s\n",str);
close(sockfd);
}
以上只实现了固定格式的http的应答方式,也就是说不管客户端发送的请求内容是什么,我们服务端都回复相同的信息,但真实的服务器是会根据请求内容的不同向客户端发送不同的内容,因此我们可以重新设计服务端实现动态的根据客户端的请求内容进行相应的响应应答。模拟web服务器http请求应答
Linux套接子(c语言)模拟http请求、应答的更多相关文章
- 模拟web服务器http请求应答
我们在浏览器打开网页,其实是向远端服务器提出页面发送请求,远端服务器在接到请求后,就开始执行请求页面的程序文件,然后将执行结果通过html格式,发送到你的浏览器,再显示出来.以下用百度(www.bai ...
- 在Linux上利用curl 命令模拟 HTTP GET/POST 请求
本文系转载,原文地址:https://www.cnblogs.com/alfred0311/p/7988648.html 序言 在 Linux 操作系统上对后端程序进行测试的时候,需要进行模拟连接或者 ...
- Linux用C语言模拟‘ls‘命令
原理 在linux下使用C语言,通过调用Linux系统的目录访问API来实现一个类似于ls命令功能的小程序,主要是可以练习程序对命令的解析和目录API函数的使用. 实现代码 #include < ...
- 第五十九节,模拟浏览器请求Python结合html基本格式
模拟浏览器请求Python结合html基本格式 用Python模拟一个客户端,结合打开一个HTML页面 创建客户端 #!/usr/bin/env python # -*- coding:utf8 -* ...
- linux 套接字编程入门--Hello World
下述代码是linux套接字编程的入门代码.分为服务端和客户端源码. 服务端代码的主要流程是绑定ip地址和端口号建立套接字,等待客户端发起访问.接受客户端请求之后,向客户端发送字符串"hell ...
- socket - Linux 套接字
总览 #include <sys/socket.h> mysocket = socket(int socket_family, int socket_type, int protocol) ...
- 使用 jQuery Mockjax 插件模拟 Ajax 请求
在实际的开发过程中,前端后台协商好了统一的接口,就各自开始自己的任务了.这时候我有这么一个 Ajax 请求需要从后台获取数据: $.ajax({ url: '/products/' }).done(f ...
- Linux 内核 链表 的简单模拟(1)
第零章:扯扯淡 出一个有意思的题目:用一个宏定义FIND求一个结构体struct里某个变量相对struc的编移量,如 struct student { int a; //FIND(struct stu ...
- Linux 套接字编程中的 5 个隐患(转)
本文转自IBM博文Linux 套接字编程中的 5 个隐患. “在异构环境中开发可靠的网络应用程序”. Socket API 是网络应用程序开发中实际应用的标准 API.尽管该 API 简单,但是开发新 ...
随机推荐
- oj教程--排序算法(Java)
import java.util.ArrayList; import java.util.List; /** * 排序算法主类 * * @author eric */ class SortArray ...
- Java基础--Eclipse关联Java源码
打开Eclipse,Window->Preferences->Java 点Edit按钮后弹出: 点Source Attachment后弹出: 选择Java安装路径下的src.zip文件即可 ...
- tensorflow源码解析之framework-tensor
目录 什么是tensor tensor继承体系 与Eigen3库的关系 什么是tensor_reference tensor_shape tensor_slice 其它结构 关系图 涉及的文件 迭代记 ...
- 对于处理datetime数据类型的一些常用方法:
datetime数据类型常用方法: 在项目中从数据库中取出数据后通常需要先绘制图像进行数据的观察,此过程中使用到的方法: 1.时间数据类似于 2022-03-23 14:21:45 可以先转换为dat ...
- Git 常见错误 之 error:error: src refspec main does not match any/ error: failed to push some refs to 简单解决
错误产生的原因:Github 工程默认名为了 main 由于受到"Black Lives Matter"运动的影响,GitHub 从今年 10 月 1 日起,在该平台上创建的所有新 ...
- VuePress 博客优化之增加 Valine 评论功能
前言 在 <一篇带你用 VuePress + Github Pages 搭建博客>中,我们使用 VuePress 搭建了一个博客,最终的效果查看:TypeScript 中文文档. 本篇讲讲 ...
- 4.监控Redis--单节点
prometheus监控redis需要用到redis_exporter. redis_exporter 项目地址:https://github.com/oliver006/redis_exporter ...
- 修复ST-LINK V2下载器 | ST-LINK V2下载器烧录DAPLink固件
前言 某宝上的STLINK V2下载器偶尔会坏掉,我们尝试修复一下 1.材料 (1)完好的STLINK V2下载器和坏掉的下载器各1个: (2)固件:https://gitee.com/Cai-Zi/ ...
- Spark—GraphX编程指南
Spark系列面试题 Spark面试题(一) Spark面试题(二) Spark面试题(三) Spark面试题(四) Spark面试题(五)--数据倾斜调优 Spark面试题(六)--Spark资源调 ...
- 解决Hadoop集群hdfs无法启动DataNode的问题
问题描述: 在hadoop启动hdfs的之后,使用jps命令查看运行情况时发现hdfs的DataNode并没有打开. 笔者出现此情况前曾使用hdfs namenode -format格式化了hdfs ...