第二十五篇:死循环以及Socket服务端支持浏览器输出

一:引言

  上篇我们写的程序,服务端并没有做任何的循环监听,一旦两者交互完成,那么服务端和客户端就会同时结束,

  所以上篇服务端是一次性的服务端,今天我们将其改造为死循环,可以让我们的服务端一直监听客户端,

二:先简化并注释上篇服务端的代码

package main

import (
	"fmt"
	"net"
)

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("创建监听成功,等待客户端连接")
	client,err:=lis.Accept()//服务端阻塞在这里,等待我们的客户端连接;
	if err !=nil{
		fmt.Println(err.Error())
		return
	}
	defer client.Close()
		//简化下我们上篇的代码,并注释;
		//把for 循环去掉,先写成一次性可以读取完客户端发送的数据
		buf:=make([]byte,4096)
		//客户端一旦有连接,我们就读取客户端发送过来的内容;
		n,err:=client.Read(buf)
		if err !=nil{
			fmt.Println(err.Error())
			return
		}
		/*虽然定义的切片容量大小为4096,但是我们发送的为n,
		所以我们将切片转化为字符串写成buf[0:n]或者把0去掉
		写成buf[:n]*/
		fmt.Printf(string(buf[0:n]))
}

 三:改造服务端代码为启动后一直监听客户端(死循环)

package main

import (
	"fmt"
	"net"
)

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
		}
	/*	把下面内容放到一个函数里面,可以使用协程,
		先整一个普通的函数,由于这个函数可能需要放到别的包里面,
		所以可以先将监听到的客户端连接的信息给传进去,代码块中的
		Close()和Read()函数也要修改成c.Close()和c.Read();
		这是个普通函数,并未加go协程来进行处理,所以还是个单进程单线程
		的程序来处理我们的连接;只不过把它放到了一个匿名函数里面,使用
		for 来做一个死循环,所以服务端开启后,一旦有客户端进行请求连接,
		服务端就会处理客户端发送的数据,而且服务端不会自动停止,但是客户端
		发送一次请求后就会停止运行;
	*/
		func(c net.Conn){
			defer c.Close()
			buf:=make([]byte,4096)
			n,err:=c.Read(buf)
			if err !=nil{
				fmt.Println(err.Error())
				return
			}
			fmt.Printf(string(buf[0:n]))
		}(client)
	}
}

 四:支持浏览器输出

到这里有的同学可能会问到:在我们这浏览器是否能访问到,其实浏览器和服务端
进行连接,首先也是建立在我们TCP协议上的,也就是传输层协议,而HTTP协议是建立在
传输层协议之上的应用层协议,接下来演示如何使用浏览器支持一个最简单的连接;
我们不使用包,先来看底层的实现方法;
(a):服务端代码:
package main

import (
	"fmt"
	"net"
)

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
			}
			fmt.Printf(string(buf[0:n]))
			//服务端发送
			c.Write([]byte("abc"))
		}(client)
	}
}

 (b):客户端代码

package main

import (
	"fmt"
	"net"
)

func main()  {
	conn,err:=net.Dial("tcp","127.0.0.1:8099")
	if err!=nil{
		fmt.Println(err.Error())
		return
	}
	defer conn.Close()
	//客户端写(发送)
	conn.Write([]byte("I am pizixu"))
	//客户端读
	buf:=make([]byte,4096)
	n,err:=conn.Read(buf)
	if err !=nil{
		fmt.Println(err.Error())
		return
	}
	fmt.Printf(string(buf[0:n]))
}

 启动服务端,客户端请求:

 

 

 

谷歌浏览器请求如下:

 

跟浏览器有关系,比如火狐浏览器可以正常响应

 

浏览器和服务端有相关约定即协议请求格式(http,tcp等),浏览器会发送一大堆切片数据(转为字符串形式展示)到服务端,服务端接收后,做一定的处理再返回给客户端一堆字符串;

 浏览器请求后,服务端接收的数据如下:

 

 

服务端代码如下:

package main

import (
	"fmt"
	"net"
)
//浏览器访问返回响应头的格式
//使用多行文本,多行文本使用``这个f反单引号符号;
//body内容要和头内容加两个换行符
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
			}
			fmt.Printf(string(buf[0:n]))
			//服务端发送
			c.Write([]byte(response()))
		}(client)
	}
}

 

如下谷歌浏览器就可以搜索到了

 

 以上为服务端为单进单线程的处理客户端(浏览器请求)的代码,思考如何使用协程来处理不同的客户端?

 

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