有关套接子和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. C++二维动态数组

    //创建 int **a=new int *[n]; for(i=0;i<n;i++) a[i]=new int[n]; // -- // 删除 for(i=0;i<n;i++) dele ...

  2. 字符串格式化String.Format

    //给变量赋值字符串00002 string s = String.Format( "{0:d5}", 2);

  3. props配置

    配置项props 功能:让组件接收外部传过来的数据[相当于微信转账:有一个转账人转钱给接收者,接收者需要确认接收] (1)传递数据: <Demo name="xxx"> ...

  4. rsyn的使用

    以下是rsync的语法: Local: rsync [OPTION...] SRC... [DEST] Access via remote shell: Pull: rsync [OPTION...] ...

  5. VUE3 之 状态动画 - 这个系列的教程通俗易懂,适合新手

    1. 概述 老话说的好:不用羡慕别人,每个人都有属于自己的人生道路,重要的是在前进道路上遇见阻碍时,如何去积极的面对并解决. 言归正传,今天我们来聊聊 VUE 的状态动画. 2. 状态动画 2.1 数 ...

  6. 一篇文章扒掉“桥梁Handler”的底裤

    Android跨进程要掌握的是Binder, 而同一进程中最重要的应该就是Handler 消息通信机制了.我这么说,大家不知道是否认同,如果认同,还希望能给一个关注哈. 什么是Handler? Han ...

  7. Python函数-5 生成器

    生成器有时候,序列或集合内的元素的个数非常巨大,如果全制造出来并放入内存,对计算机的压力是非常大的.比如,假设需要获取一个10**20次方如此巨大的数据序列,把每一个数都生成出来,并放在一个内存的列表 ...

  8. 5月9日 python学习总结 外键、表之间的关联关系、修改表、清空表内容、复制表

    一.外键foreign key    外键约束: 1.必须先创建被关联表才能创建关联表 2.插入记录时,必须先插入被关联表的记录,才能插入关联表(要用到被关联表)的记录 3.若不设置同步更新和同步删除 ...

  9. 超详细GoodSync11.2.7.8单机、两个服务器之间的文件同步使用教程

    GoodSync安装教程 第一步:双机GoodSync_v11.2.7.8.exe文件 链接:https://pan.baidu.com/s/16FVater4f9vu07QiGGIK9A 提取码:b ...

  10. python练习册 每天一个小程序 第0004题

    1 #-*-coding:utf-8-*- 2 __author__ = 'Deen' 3 ''' 4 题目描述:任一个英文的纯文本文件,统计其中的单词出现的个数. 5 参考学习链接: 6 re ht ...