第二十六篇:正则入门、分析请求path、Socket服务端开启协程支持

引言:上篇我们对Socket服务端进行了一个死循环,不管客户端连接了多少次,都不会让我们的服务端停止,并且实现了浏览器的基本输出;

    如果浏览器不支持响应,需要添加响应的http格式;那我们的浏览器就会正常的响应;

     今天我们要做的内容是在上节课的基础上加上协程支持;并分析请求path;

首先在上篇浏览器连接的时候尼,发送了一段HTTP请求;其它我们都不看,我们先只看第一行;

 

 

GET / HTTP/1.1

GET:代表GET请求;

/:代表请求的path到底是什么

HTTP/1.1:协议以及版本

 

一:go里面正则接触下

  先来演示套路,具体细节以后再说;

  所属包:regexp

1.1:先触碰几个方法,对我们的字符串进行处理;

1、r,err:=regexp.Compile(expr string) (*Regexp, error) 将正则表达式编译成一个正则对象
2、r:=regexp.MustCompile(str string) *Regexp 同上,如果正则写错了,会把异常panic出来
3、r. MatchString(s string) bool  判断能否匹配到(一般做判断用,判断如果没有,就不做下面的步骤)
4、r. FindStringSubmatch和r.FindAllStringSubmatch。前者查找匹配到的第一个匹配结果及分组内容,后者支持多个匹配结果,n参数(查找多少次,负数为不限)

package main

import (
"fmt"
"regexp"
)

func GetRqPath(rq string) string{
/* 正则匹配的时候,如果有些内容需要转义,就要使用双引号;
GET /abc.php HTTP/1.1 中GET后面是一个空格,在我们正则里面就是\s,
因为\s中\需要转义,因此要加上转义符\,即"^GET\\s"
如果不想转义,想要原始输出,可以使用反单引号``,即:`GET\s`;

因为要匹配abc.php这部分内容,所以我们写个分组,分组内容:
(.*?),再匹配后面的空格和HTTP即(.*?)\sHTTP,
r:=regexp.MustCompile(`GET\s(.*?)\sHTTP`) 这个正则取出来以后
就可以在字符串str中进行匹配,字符串的匹配有以下方法:
(1):r.MatchString(s string) bool 判断能否匹配到
一般做判断用,判断如果没有,就不做下面的步骤
(2):r.FindStringSubmatch和r.FindAllStringSubmatch。
前者查找匹配到的第一个匹配结果及分组内容,
后者支持多个匹配结果,n参数(查找多少次,负数为不限)
*/
//课后思索如何使用分组名的方式进行匹配?
r:=regexp.MustCompile(`GET\s(.*?)\sHTTP`)
if r.MatchString(rq){
/* 如果匹配到匹配到的结果为:GET /abc.php HTTP
匹配内容为/abc.php 这里要取出第二个内容即/abc.php
*/
return r.FindStringSubmatch(rq)[1]
}else {
return "/"
}
}

func main() {
/* 如下为正则需要匹配的内容;
只要取/abc.php这一部分内容就行了;
只看path部分,看path部分是否有我们设定的一些字符;
如果有,
*/
str:=`GET /abc.php HTTP/1.1
Host: localhost:8099
Connection: keep-alive
Upgrade-Insecure-Requests: 1`
fmt.Println(GetRqPath(str))///abc.php
}

 二:分析请求path

将上述匹配代码块放到我们的服务端的一个函数中:

 

 服务端代码如下:

package main
import (
	"fmt"
	"net"
	"time"
)
func response()string{
	str:=`HTTP/1.1 200 OK
	Server:myServer
	Content-Type:text/html


`+`this is body`
	return  str
}
func main()  {
	lis,err:=net.Listen("tcp","127.0.0.1:8099")
	if err !=nil{
		fmt.Println(err.Error())
		return
	}
	defer lis.Close()
	fmt.Println("创建监听成功,等待客户端连接")
	for {
		client,err:=lis.Accept()

		if err !=nil{
			fmt.Println(err.Error())
			return
		}
		func(c net.Conn){
			defer c.Close()
			buf:=make([]byte,4096)
			//服务端读
			n,err:=c.Read(buf)
			if err !=nil{
				fmt.Println(err.Error())
				return
			}
		     if GetRequestPath(string(buf[0:n]))=="/delay"{
               time.Sleep(time.Second*5)
               }
//服务端发送 c.Write([]byte(response())) }(client) } }

 执行结果:由于服务端的代码是单进程单线程,所以当http://localhost:8099/delay执行还没结束的时候,去执行http://localhost:8099/也会延迟;这也是我们Socket服务端处理能力

比较单一的问题;

 

 

为了解决上述问题,我们接下来使用协程来处理:

 

 

 

改造代码:

 

package main
import (
	"fmt"
	"net"
	"time"
)
func response()string{
	str:=`HTTP/1.1 200 OK
	Server:myServer
	Content-Type:text/html


`+`this is body`
	return  str
}
func main()  {
	lis,err:=net.Listen("tcp","127.0.0.1:8099")
	if err !=nil{
		fmt.Println(err.Error())
		return
	}
	defer lis.Close()
	fmt.Println("创建监听成功,等待客户端连接")
	for {
		client,err:=lis.Accept()

		if err !=nil{
			fmt.Println(err.Error())
			return
		}
/*		每当有客户端连接的时候,就开一个协程
		改造代码很简单,直接加上 go 即可以;*/
		go func(c net.Conn){
			defer c.Close()
			buf:=make([]byte,4096)
			//服务端读
			n,err:=c.Read(buf)
			if err !=nil{
				fmt.Println(err.Error())
				return
			}
			if GetRequestPath(string(buf[0:n]))=="/delay"{
				time.Sleep(time.Second*5)
			}
			//服务端发送
			c.Write([]byte(response()))
		}(client)
	}
}

 执行结果:如此便解决了上述问题,http://localhost:8099/delay执行还没结束的时候,去执行http://localhost:8099/也不会延迟;

 

补充思考:每有一个客户端连接,就会开辟一个协程,如果有成千上万个客户端同时连接, 就会开辟成千上万个协程,因此会有性能损耗,如何解决这个问题?

可以使用协程池来解决,先思考或者查阅资料,后面的篇幅会讲起;

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2020-06-16 17:53  痞子胥  阅读(259)  评论(0)    收藏  举报