HTTP协议是基于TCP/IP协议栈的,并且是一个面向普通文本的协议。原则上,使用任何一个文本编辑器,都可以写出一个完整的HTTP请求报文。只要搞清楚了请求报文的头部(header、请求头)和主体(body、请求体)应该包含的内容。
如果只是访问基于HTTP协议的网络服务,那么使用net/http包中的程序实体会非常方便。
调用http.Get函数,只需要传递给它一个URL即可:
package main
import (
"fmt"
"net/http"
"os"
)
func main() {
resp, err := http.Get("http://baidu.com")
if err != nil {
fmt.Fprintf(os.Stderr, "request sending error: %v\n", err)
return
}
defer resp.Body.Close()
line := resp.Proto + " " + resp.Status
fmt.Println("返回的第一行的内容:", line)
}
http.Get函数会返回两个结果:
http.Get函数会在内部使用缺省的HTTP客户端,并且调用它的Get方法来完成功能。这个缺省的HTTP客户端就是net/http包中的公开变量DefaultClient,源码中是这样的:
// 源码中提供的缺省的客户端
var DefaultClient = &Client{}
// 使用缺省的客户端调用Get方法
func Get(url string) () {
return DefaultClient.Get(url)
}
所以下面的这两行代码:
var httpClient http.Client
resp, err := httpClient.Get(utl)
与示例中的这一行代码:
resp, err := http.Get(url)
是等价的。这里只是不使用DefaultClient而是自己创建了一个客户端。
http.Client是一个结构体,并且它包含的字段都是公开的:
type Client struct {
Transport RoundTripper
CheckRedirect func(req *Request, via []*Request) error
Jar CookieJar
Timeout time.Duration
}
该类型是开箱即用的,因为它的所有字段,要么存在相应的缺省值,要么其零值直接就可以使用,并且代表着特定的含义。
主要看下Transport字段,该字段向网络服务发送HTTP请求,并从网络服务接收HTTP响应。该字段的方法RoundTrip应该实现单次HTTP事务(或者说基于HTTP协议的单次交互)需要的所有步骤。这个字段是一个接口:
type RoundTripper interface {
RoundTrip(*Request) (*Response, error)
}
并且该字段有一个由http.DefaultTransport变量的缺省值:
func (c *Client) transport() RoundTripper {
if c.Transport != nil {
return c.Transport
}
return DefaultTransport
}
在初始化http.Client类型的时候,如果没有显式的为该字段赋值,这个Client字段就会直接使用DefaultTransport。
该字段是单次HTTP事务的超时时间,它是time.Duration类型。它的零值是可用的,用于表示没有设置超时时间。
http.Transport类型是一个结构体,该类型包含的字段很多。这里通过http.Client结构体中的Transport字段的缺省值DefaultTransport,来深入了解一下。DefaultTransport是一个*http.Transport的结构体,做了一些默认的设置:
var DefaultTransport RoundTripper = &Transport{
Proxy: ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
这里Transport结构体的指针就是就是RoundTripper接口的默认实现:
func (t *Transport) RoundTrip(req *Request) (*Response, error) {
return t.roundTrip(req)
}
这个类型是可以被复用的,并且也推荐被复用。同时它也是并发安全的。所以http.Client类型也是一样,推荐复用,并且并发安全。
看上面的默认设置,http.Transport类型,内部的DialContext字段会使用net.Dialer类型的值,并且把Timeout设置为30秒。仔细看,该值是一个方法,这里把Dialer值的DialContext方法赋值给了DefaultTransport里的同名字段,并且已经设置好了调用该方法时的结构体。
http.Transport类型还包含了很多其他的字段,其中有一些字段是关于操作超时的:
TLS 是 Transport Layer Security 的缩写,可以被翻译为传输层安全。
此外,还有一些与IdleConnTimeout相关的字段值也值得关注:
MaxIdleConns
无论当前访问了多少个网络服务,MaxIdleConns字段只会对空闲连接的总数做限定。
MaxIdleConnsPerHost
而MaxIdleConnsPerHost字段限定的是,每一个网络服务的大空闲连接数。每一个网络服务都有自己的网络地址,可能会使用不同的网络协议,对于一些HTTP请求也可能会用到代理。地址、协议、代理,通脱这三个方面的具体情况来鉴别不同的网络服务。
MaxIdleConnsPerHost是有缺省值的,由常量http.DefaultMaxIdleConnsPerHost表示,值为2:
const DefaultMaxIdleConnsPerHost = 2
func (t *Transport) maxIdleConnsPerHost() int {
if v := t.MaxIdleConnsPerHost; v != 0 {
return v
}
return DefaultMaxIdleConnsPerHost
}
在默认情况下,每一个网络服务,它的空闲连接数最多只能由2个。
MaxConnsPerHost
MaxConnsPerHost字段限制针对每一个网络服务的大连接数,不论这些链接是否是空闲的。并且,该字段没有相应的缺省值,零值就是不做限制。
小结
不限制连接数,默认也不限制每一个网络服务的连接数。要限制整体的空闲连接数以及严格限制对每一个网络服务的空闲连接数。
简单说明一下,为什么会出现空闲的连接。
HTTP协议的请求头里有一个Connection。在HTTP协议的1.1版本中,默认值是“keep-alive”。在这种情况下的网络连接是持久连接的,它们会在当前的HTTP事务完成后仍然保持着连通性,因此是可以被复用的。
既然连接可以被复用,就会有两种可能:
另外,如果分配给某一个网络服务的连接过多的话,也可能会导致空闲连接的产生。因为没一个HTTP请求只会使用一个空闲的连接。所以,在大多数情况下,都需要限制空闲连接数。
关闭keep-alive
另外,请求头的Connection还可以设置为“close”,这样就彻彻底杜绝了空闲连接的生成。这会告诉网络服务,这个网络连接不必保持,当前的HTTP事务完成后就可以断开它了。做法是在初始化Transport值的时候,将DisableKeepAlives字段设置为true。
这么做的话,每次提交HTTP请求,就会产生一个新的网络连接。这样会明显的加重网络服务以及客户端的负载,并会让每个HTTP事务都耗费更多的时间。所以默认不设置这个DisableKeepAlives字段。
http.Transport类型,内部的DialContext字段会使用net.Dialer类型的值。在net.Dialer类型中,也有一个KeepAlive字段。该字段是直接作用在底层的socket上的。
它的背后是一种针对网络连接(更确切的是说,是TCP连接)的存活探测机制。它的值用于表示每间隔多长时间发送一次探测包。当该值不大于0是,则表示不开启这种机制。
DefaultTransport会把这个字段设置为30秒。
自定义Client和Transport使用的示例:
package main
import (
"fmt"
"io/ioutil"
"net"
"net/http"
"strings"
"sync"
"time"
)
var domains = []string{
"baidu.com",
"sina.com.cn",
"www.baidu.com",
"www.sina.com.cn",
"tieba.baidu.com",
"news.baidu.com",
"news.sina.com.cn",
}
func main() {
myTransport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 15 * time.Second,
KeepAlive: 15 * time.Second,
DualStack: true,
}).DialContext,
MaxConnsPerHost: 2,
MaxIdleConns: 10,
MaxIdleConnsPerHost: 2,
IdleConnTimeout: 30 * time.Second,
ResponseHeaderTimeout: 0,
ExpectContinueTimeout: 1 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
}
myClient := http.Client{
Transport: myTransport,
Timeout: 20 * time.Second,
}
var wg sync.WaitGroup
for _, domain := range domains {
wg.Add(1)
go func(domain string) {
var logBuf strings.Builder
var diff time.Duration
defer func() {
logBuf.WriteString(fmt.Sprintf("持续时间: %s\n", diff))
fmt.Println(logBuf.String())
wg.Done()
}()
url := "https://" + domain
logBuf.WriteString(fmt.Sprintf("发送请求: %s\n", url))
tStart := time.Now()
resp, err := myClient.Get(url)
diff = time.Now().Sub(tStart)
if err != nil {
logBuf.WriteString(fmt.Sprintf("request get error: %v\n", err))
return
}
defer resp.Body.Close()
line := resp.Proto + " " + resp.Status
logBuf.WriteString(fmt.Sprintf("response: %s\n", line))
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
logBuf.WriteString(fmt.Sprintf("get data error: %v\n", err))
return
}
index1 := strings.Index(string(data), "")
index2 := strings.Index(string(data), " ")
if index1 > 0 && index2 > 0 {
logBuf.WriteString(fmt.Sprintf("title: %s\n", string(data)[index1+len(""):index2]))
}
}(domain)
}
wg.Wait()
fmt.Println("All Done")
}</code></pre><h2>http.Server类型</h2><p>http.Server类型与http.Client是相对应的。http.Server代表的是基于HTTP协议的服务端,或者说网络服务。</p><h3>ListenAndServe方法</h3><p>http.Server类型的ListenAndServe方法的功能是:监听一个基于TCP协议的网络地址,并对接收到的HTTP请求进行处理。这个方法会默认开启针对网络连接的存活探测机制,以保证连接是持久的。同时,该方法会一直执行,直到有严重的错误发生或者被外界关掉。当被外界关掉时,它会返回一个由http.ErrServerClosed变量代表的错误值。<br/>这个ListenAndServe方法主要会做以下几件事情:</p><ol><li>检查当前的http.Server类型的值的Addr字段。Addr是当前的网络服务需要使用的网络地址,即:IP地址和端口号。如果这个字段的值为空字符串,那么就用":http"代替。也就是说,使用任何可以代表本机的域名和IP地址,并且端口号为80。</li><li>通过调用net.Listen函数在已确定的网络地址上启动基于TCP协议的监听。</li><li>检查net.Listen函数返回的错误值。如果该错误值不为nil,那么就直接返回该错误值。否则,通过调用当前http.Server值的Serve方法准备接受和处理将要到来的HTP请求。</li></ol><p>这里又牵出两个问题:</p><ol><li>net.Listen函数</li><li>http.Server类型的Serve方法</li></ol><h3>net.Listen函数</h3><p>net.Listen函数的作用:</p><ul><li>解析参数值中包含的网络地址隐含的IP地址和端口号</li><li>根据给定的网络协议,确定监听的方法,并开始进行监听</li></ul><p>再往下深入的话,就会涉及到net.socket函数以及相关的socket知识。就此打住。</p><h3>http.Server类型的Serve方法</h3><p>在一个for循环中,网络监听器Accept方法会不断地调用,该方法的源码如下:</p><pre><code>type tcpKeepAliveListener struct {
*net.TCPListener
}
func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
tc, err := ln.AcceptTCP()
if err != nil {
return nil, err
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}</code></pre><p>Accept方法会返回两个结果值:</p><ul><li>net.Conn : 代表包含了新到来的HTTP请求的网络连接</li><li>error : 代表了可能发生的错误的error的类型值</li></ul><p>当错误值不为nil时,如果此时是一个暂时性的错误,那么循环的下一次迭代将会在一段时间之后开始执行。否则,循环会被终止。<br/>如果没有错误,返回的错误值就是nil。那么这里的程序将会把它的第一个结果值包装成一个*http.conn类型的值,然后通过在新的goroutine中调用这个conn值的serve方法,来对当前的HTTP请求进行处理。</p><p>上面最后说的处理的细节还是很多的:</p><ul><li>conn值的各种状态,各状态代表的处理阶段</li><li>处理过程中会用到的读取器和写入器,及其作用</li><li>让程序调用自定义的处理函数</li></ul><p>这些都没有一一说明,建议去看下源码。</p><h3>Server示例</h3><p>在下面的示例中,启动了3个Server。启动后,可以用浏览器访问进行验证:</p><pre><code>package main
import (
"fmt"
"net/http"
"os"
"sync"
)
var wg sync.WaitGroup
// 一般没有这么用的,http.Server的Handler字段
// 要么是nil,就用包里的http.DefaultServeMux
// 要么用NewServeMux()来创建一个*http.ServeMux
// 我这里按照http.Handler接口的要求实现了一个,赋值给Handler字段
// 这个自定义的Handler不支持路由
func startServer1() {
defer wg.Done()
var httpServer http.Server
httpServer.Addr = "127.0.0.1:8001"
httpServer.Handler = http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
fmt.Println(*r)
fmt.Fprint(w, "Hello World")
},
)
fmt.Println("启动服务,访问: http://127.0.0.1:8001")
if err := httpServer.ListenAndServe(); err != nil {
if err == http.ErrServerClosed {
fmt.Println("HTTP Server1 Closed.")
} else {
fmt.Fprintf(os.Stderr, "HTTP Server1 Error: %v\n", err)
}
}
}
// 这个最简单,都是调用http包里的函数。本质上还是要调用方法的,都会用默认的或是零值
func startServer2() {
defer wg.Done()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World\nThis is Server2")
})
fmt.Println("启动服务,访问: http://127.0.0.1:8002")
// 第二个参数传nil,就是用包里的http.DefaultServeMux,或者也可以自己创建一个传给第二个参数
if err := http.ListenAndServe("127.0.0.1:8002", nil); err != nil {
if err == http.ErrServerClosed {
fmt.Println("HTTP Server2 Closed.")
} else {
fmt.Fprintf(os.Stderr, "HTTP Server2 Error: %v\n", err)
}
}
}
// 这个例子里用到了解析Get请求的参数,并且还设置了2个路由
func startServer3() {
defer wg.Done()
mux := http.NewServeMux()
mux.HandleFunc("/hi", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/hi" {
// 这个分支应该是进不来的,因为要进入这个分支,路径应该必须是"/hi"
fmt.Println("Server3 hi 404")
http.NotFound(w, r)
return
}
name := r.FormValue("name")
if name == "" {
fmt.Fprint(w, "Hi!")
} else {
fmt.Fprintf(w, "Hi, %s!", name)
}
})
mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World\nThis is Server3")
})
// 如果只是定义http.Server的下面2个字段,完全可以使用http.ListenAndServe函数来启动服务
// 这样的用法可以对http.Server里更多的字段进行自定义
httpServer := http.Server{
Addr: "127.0.0.1:8003",
Handler: mux,
}
fmt.Println("启动服务,访问: http://127.0.0.1:8003/hi?name=Adam")
if err := httpServer.ListenAndServe(); err != nil {
if err == http.ErrServerClosed {
fmt.Println("HTTP Server3 Closed.")
} else {
fmt.Fprintf(os.Stderr, "HTTP Server3 Error: %v\n", err)
}
}
}
func main() {
wg.Add(1)
go startServer1()
wg.Add(1)
go startServer2()
wg.Add(1)
go startServer3()
wg.Wait()
}</code></pre><h3>补充-优雅的停止HTTP服务</h3><p>包里还提供了一个Shutdown方法,可以优雅的停止HTTP服务:</p><pre><code>func (srv *Server) Shutdown(ctx context.Context) error {
// 内容省略
}</code></pre><p>我们要做的就是在需要的时候,可以调用该Shutdown方法。<br/>这里的问题是,调用了ListenAndServe方法之后,就进入了无限循环的流程。这里最好是用一个goroutine来启动ListenAndServe方法,在goroutine外声明http.Server。然后在主线程里等待一个信号,比如是从通道接收值。这样就可以在主线程里调用这个Shutdown方法执行了。</p><p>创新互联www.cdcxhl.cn,专业提供香港、美国云服务器,动态BGP最优骨干路由自动选择,持续稳定高效的网络助力业务部署。公司持有工信部办法的idc、isp许可证, 机房独有T级流量清洗系统配攻击溯源,准确进行流量调度,确保服务器高可用性。佳节活动现已开启,新人活动云服务器买多久送多久。</p> <br>
分享标题:Go36-47-基于HTTP协议的网络服务(net/http)-创新互联 <br>
文章地址:<a href="http://cxhlcq.com/article/depcgj.html">http://cxhlcq.com/article/depcgj.html</a>
</div>
</div>
</div>
<div class="other container">
<h3>其他资讯</h3>
<ul>
<li>
<a href="/article/eieeee.html">直播代播公司有哪些值得选择的,如何选择靠谱的直播代播公司</a>
</li><li>
<a href="/article/eieedd.html">惠州短视频运营获客</a>
</li><li>
<a href="/article/eieeic.html">官网超链接交互样式设计与实现方式</a>
</li><li>
<a href="/article/eieesp.html">山东大安抖音代运营公司</a>
</li><li>
<a href="/article/eieees.html">制作网页的工具软件</a>
</li> </ul>
</div>
<div class="footer">
<div class="foota container">
<div class="foot_nav fl col-lg-8 col-md-8 col-sm-12 col-xs-12">
<ul>
<li class="col-lg-3 col-md-3 col-sm-3 col-xs-6">
<h3>网站制作</h3>
<a href="https://www.cdxwcx.com/" target="_blank">成都网站制作</a><a href="http://m.cdcxhl.com/" target="_blank">成都网站制作</a><a href="http://www.cxjianzhan.com/" target="_blank">网站制作</a><a href="http://m.cdcxhl.cn/dingzhi/" target="_blank">定制网站制作</a><a href="http://www.cdxwcx.cn/" target="_blank">成都网站制作</a><a href="https://www.cdcxhl.com/" target="_blank">成都网站制作</a> </li>
<li class="col-lg-3 col-md-3 col-sm-3 col-xs-6">
<h3>企业服务</h3>
<a href="https://www.cdcxhl.com/service/licence.html" target="_blank">药房许可证</a><a href="https://www.cdcxhl.com/ruanwen/yingxiao/" target="_blank">软文发稿</a><a href="https://www.cdcxhl.com/service/400.html" target="_blank">400电话</a><a href="https://www.cdcxhl.com/shoulu/" target="_blank">免费收录</a><a href="https://www.cdcxhl.com/mianfei/jianshe/chengdu.html" target="_blank">免费网站建设</a><a href="https://www.cdcxhl.com/ruanwen/" target="_blank">软文发布</a> </li>
<li class="col-lg-3 col-md-3 col-sm-3 col-xs-6">
<h3>网站建设</h3>
<a href="http://www.cdkjz.cn/fangan/jtss/" target="_blank">上市企业网站建设方案</a><a href="http://www.kswcd.com/service/" target="_blank">高端网站建设</a><a href="https://www.cdxwcx.com/city/nanchong/" target="_blank">南充网站建设</a><a href="http://chengdu.kswjz.com/" target="_blank">成都网站建设</a><a href="http://www.cqcxhl.com/" target="_blank">重庆网站建设</a><a href="http://chengdu.cdxwcx.cn/" target="_blank">成都网站建设</a> </li>
<li class="col-lg-3 col-md-3 col-sm-3 col-xs-6">
<h3>服务器托管</h3>
<a href="https://www.cdxwcx.com/jifang/yaan.html" target="_blank">雅安服务器托管</a><a href="https://www.cdcxhl.com/idc/gysx.html" target="_blank">贵阳三线机房</a><a href="https://www.cdcxhl.com/idc/yaan.html" target="_blank">雅安服务器托管</a><a href="https://www.cdcxhl.com/tuoguan/zongshu/" target="_blank">成都棕树机房</a><a href="https://www.cdcxhl.com/jigui/" target="_blank">服务器机柜租用</a><a href="https://www.cdcxhl.com/jigui/" target="_blank">成都机柜租用</a> </li>
</ul>
</div>
<div class="footar fl col-lg-4 col-md-4 col-sm-12 col-xs-12">
<p>全国免费咨询:</p>
<b>400-028-6601</b>
<p>业务咨询:028-86922220 / 13518219792</p>
<p>节假值班:18980820575 / 13518219792</p>
<p>联系地址:成都市太升南路288号锦天国际A幢1002号</p>
</div>
</div>
<div class="footb">
<div class="copy container">
<div class="fl">Copyright © 成都创新互联科技有限公司重庆分公司 <a href="https://beian.miit.gov.cn/" target="_blank">渝ICP备2021005571号</a></div>
<!--<div class="fr"><a href="https://www.cdxwcx.com/" target="_blank">成都网站建设</a>:<a href="https://www.cdcxhl.com/" target="_blank">创新互联</a></div>-->
</div>
</div>
<div class="link">
<div class="container">
友情链接::
<a href="https://www.cdcxhl.com/" target="_blank">成都网站建设</a>
<a href="https://www.cdcxhl.com/city/chongqing.html" target="_blank">重庆网站建设</a>
<a href="">四川网站建设</a>
<a href="">重庆建设网站</a>
<a href="https://www.cdxwcx.com/jifang/xiyun.html" target="_blank">移动服务器托管</a>
<a href="http://www.cdfuwuqi.com/" target="_blank">成都服务器托管</a>
<a href="https://www.cdcxhl.cn/" target="_blank">云服务器</a>
<a href="http://www.cdhuace.com/" target="_blank">广告设计制作</a>
<a href="https://www.cdcxhl.com/sheji/chongqing.html" target="_blank">重庆网页设计</a>
<a href="https://www.cdcxhl.com/zuo/chongqing.html" target="_blank">重庆做网站</a>
<a href="https://www.cdcxhl.com/zhizuo/chongqing.html" target="_blank">重庆网站制作</a>
<a href="">重庆网站建设</a>
<a href="">重庆网站公司</a>
<a href="">渝中网站制作</a>
<a href="">重庆网站设计</a>
</div>
</div>
</div>
<div class="foot">
<ul class="public-celan">
<li>
<a href="https://p.qiao.baidu.com/cps3/chatIndex?siteToken=6ce441ff9e2d6bedbdfc2a4138de449e&speedLogId=162260383240185e3_1622603832401_02407&eid=6256368&reqParam=%7B%22from%22%3A1%2C%22sessionid%22%3A%22-100%22%2C%22siteId%22%3A%2211284691%22%2C%22tid%22%3A%22-1%22%2C%22userId%22%3A%226256368%22%2C%22ttype%22%3A1%2C%22pageId%22%3A0%7D" target="_blank" class="a1 db tc">
<img src="/Public/Home/img/icon-23.png" alt="" class="db auto">
<span class="span-txt">在线咨询</span>
</a>
</li>
<li>
<a href="tel:18980820575" class="a1 db tc">
<img src="/Public/Home/img/icon-24.png" alt="" class="db auto">
<span class="span-txt">电话咨询</span>
</a>
</li>
<li>
<a target="_blank" href="tencent://message/?uin=1683211881&Site=&Menu=yes" class="a1 db tc">
<img src="/Public/Home/img/icon-25.png" alt="" class="db auto">
<span class="span-txt">QQ咨询</span>
</a>
</li>
<li>
<a target="_blank" href="tencent://message/?uin=532337155&Site=&Menu=yes" class="a1 db tc public-yuyue-up">
<img src="/Public/Home/img/icon-26.png" alt="" class="db auto">
<span class="span-txt">预约顾问</span>
</a>
</li>
</ul>
</div>
<div class="customer">
<dl class="icon1">
<dt>
<a href="tencent://message/?uin=1683211881&Site=&Menu=yes">
<i class="iconT"><img src="/Public/Home/img/QQ.png" alt=""></i>
<p>在线咨询</p>
</a>
</dt>
</dl>
<dl class="icon2">
<dt><i><img src="/Public/Home/img/weixin.png" alt=""></i><p>微信咨询</p></dt>
<dd><img src="/Public/Home/img/ewm.png"></dd>
</dl>
<dl class="icon3">
<dt><i><img src="/Public/Home/img/dianhua.png" alt=""></i><p>电话咨询</p></dt>
<dd>
<p>028-86922220(工作日)</p>
<p>18980820575(7×24)</p>
</dd>
</dl>
<dl class="icon4">
<dt class="sShow">
<a href="tencent://message/?uin=244261566&Site=&Menu=yes">
<i><img src="/Public/Home/img/dengji.png" alt=""></i><p>提交需求</p>
</a>
</dt>
</dl>
<dl class="icon5">
<dt class="gotop">
<a href="#top">
<i><img src="/Public/Home/img/top.png" alt=""></i><p>返回顶部</p>
</a>
</dt>
</dl>
</div>
</body>
</html>
<script>
$(".con img").each(function(){
var src = $(this).attr("src"); //获取图片地址
var str=new RegExp("http");
var result=str.test(src);
if(result==false){
var url = "https://www.cdcxhl.com"+src; //绝对路径
$(this).attr("src",url);
}
});
window.onload=function(){
document.oncontextmenu=function(){
return false;
}
}
</script>