有关套接子和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请求、应答的更多相关文章

  1. 模拟web服务器http请求应答

    我们在浏览器打开网页,其实是向远端服务器提出页面发送请求,远端服务器在接到请求后,就开始执行请求页面的程序文件,然后将执行结果通过html格式,发送到你的浏览器,再显示出来.以下用百度(www.bai ...

  2. 在Linux上利用curl 命令模拟 HTTP GET/POST 请求

    本文系转载,原文地址:https://www.cnblogs.com/alfred0311/p/7988648.html 序言 在 Linux 操作系统上对后端程序进行测试的时候,需要进行模拟连接或者 ...

  3. Linux用C语言模拟‘ls‘命令

    原理 在linux下使用C语言,通过调用Linux系统的目录访问API来实现一个类似于ls命令功能的小程序,主要是可以练习程序对命令的解析和目录API函数的使用. 实现代码 #include < ...

  4. 第五十九节,模拟浏览器请求Python结合html基本格式

    模拟浏览器请求Python结合html基本格式 用Python模拟一个客户端,结合打开一个HTML页面 创建客户端 #!/usr/bin/env python # -*- coding:utf8 -* ...

  5. linux 套接字编程入门--Hello World

    下述代码是linux套接字编程的入门代码.分为服务端和客户端源码. 服务端代码的主要流程是绑定ip地址和端口号建立套接字,等待客户端发起访问.接受客户端请求之后,向客户端发送字符串"hell ...

  6. socket - Linux 套接字

    总览 #include <sys/socket.h> mysocket = socket(int socket_family, int socket_type, int protocol) ...

  7. 使用 jQuery Mockjax 插件模拟 Ajax 请求

    在实际的开发过程中,前端后台协商好了统一的接口,就各自开始自己的任务了.这时候我有这么一个 Ajax 请求需要从后台获取数据: $.ajax({ url: '/products/' }).done(f ...

  8. Linux 内核 链表 的简单模拟(1)

    第零章:扯扯淡 出一个有意思的题目:用一个宏定义FIND求一个结构体struct里某个变量相对struc的编移量,如 struct student { int a; //FIND(struct stu ...

  9. Linux 套接字编程中的 5 个隐患(转)

    本文转自IBM博文Linux 套接字编程中的 5 个隐患. “在异构环境中开发可靠的网络应用程序”. Socket API 是网络应用程序开发中实际应用的标准 API.尽管该 API 简单,但是开发新 ...

随机推荐

  1. nginx之版本升级方法一

    转至:https://www.cnblogs.com/sblack/p/12809034.html 非线上业务,纯属测试升级,线上业务不要采用此方法,可以采用官方推荐的平滑升级. 环境 Linux c ...

  2. DatePicker去掉头布局的两种方法

    5.0+ private void hideDatePickerHeader() { ViewGroup rootView = (ViewGroup) datePicker.getChildAt(0) ...

  3. JZ-069-在 O(1) 时间内删除链表节点

    在 O(1) 时间内删除链表节点 题目描述 在 O(1) 时间内删除链表节点. 方案:如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,然后令该节点指向下下个节点,再删除下一个节点,时间复 ...

  4. LeetCode-074-搜索二维矩阵

    搜索二维矩阵 题目描述:编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值.该矩阵具有如下特性: 每行中的整数从左到右按升序排列. 每行的第一个整数大于前一行的最后一个整数. 示例说明请 ...

  5. java 注释与标识符

    JAVA基础 注释与标识符 注释 书写注释是一个非常好的习惯 三种注释: 单行,多行,文档 .单行 ://注释 .多行:/* 注释 / .文档** 注释 */ 标识符 1. 关键字 2.标识符注意点 ...

  6. laravel 框架登录 参考

    一.登录功能1.书写登录路由Route::view('login','login');2.书写登录页面  视图层<form action="{{route('loginDo')}}&q ...

  7. tensorflow源码解析之framework-resource

    目录 什么是resource 如何使用resource 如何管理resource 常用resource 其它结构 关系图 涉及的文件 迭代记录 1. 什么是resource 我们知道,TF的计算是由设 ...

  8. Django基础三之路由、视图、模板

    Django基础三之路由.视图.模板 目录 Django基础三之路由.视图.模板 1. Django 请求和返回周期 1.1 路由层之路由匹配 1.2 有名分组 1.3 无名分组 2. 反射解析 3. ...

  9. knative入门指南

    尽管Knative自2018年以来一直由社区维护,但最近一直有关于该项目的传言,因为谷歌最近将Knative提交给了云原生计算基金会(CNCF),作为一个孵化项目考虑. 太酷了!但Knative到底是 ...

  10. Linux环境下安装Maven

    最近在搞虚拟机,记录下虚拟机内Maven环境的搭建流程 一.选择合适的版本(由于Maven运行需要依赖于JDK,所以安装之前需要保证当前虚拟机下已经安装并配置好JDK环境,安装流程参见) https: ...