有关套接子和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. 服务器CPU很高-怎么办(Windbg使用坑点)

    最近,碰到了一个线上CPU服务器很高的问题,并且也相当紧急,接到这个任务后,我便想到C#性能分析利器,Windbg. 终于在折腾半天之后,找出了问题,成功解决,这里就和大家分享一下碰到的问题. 问题1 ...

  2. Nginx平滑升级的详细操作方法

    转至:https://blog.csdn.net/midion9/article/details/49684325 一.平滑升级概述Nginx方便地帮助我们实现了平滑升级.其原理简单概括,就是:(1) ...

  3. vue项目部署到IIS服务器上

    前端Vue项目需要部署到IIS服务器上: 准备工作: 1:部署IIS服务器 2:项目npm run build打包生成需要部署的文件(dist文件夹)我的是manage文件夹 开始部署: 1:复制文件 ...

  4. c# winform中TabControl中给每个选项卡添加不同的图标(图片)

    在一个TabControl控件中,有几个选项卡,现在在每个选项卡上显示不同的图标. 1:你要现在form窗体中通过工具箱加入一个imagelist,名字为imagelist1,如下图: 2:然后在里面 ...

  5. Win10系统设置开机自启动

    有时候,我们想设置某些软件开机时自动启动,操作步骤如下: 1. win+R 同时按住键盘上的win和R键打开运行窗口 2. shell:startup 输入shell:startup后回车 3. 添加 ...

  6. 【python】人脸识别

    #coding:utf-8# from __future__ import print_functionfrom time import time #有些步骤要计时,看每个步骤花多长时间import ...

  7. js-表格

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  8. linux 配置redis密码

    1.打开redis配置文件 vi /usr/local/redis/etc/redis.conf 添加requirepass 密码    将bind 127.0.0.1 ::1前的#去掉 保存退出 2 ...

  9. Mysql-关系型数据库与非关系型数据库

    一.什么是数据库 数据库是数据的仓库. 与普通的"数据仓库"不同的是,数据库依据"数据结构"来组织数据,因为"数据结构",所以我们看到的数据 ...

  10. BSOJ6388题解

    看上去就很神秘...考虑建出图论模型. 我们将一张牌的两面 \(a,b\) 连一条边. 考虑一个连通块的意义是什么. 边是一张牌,容易发现,如果连通块是一棵树,那么选择一个根节点相当于可以打出除了根节 ...