第二十五篇:死循环以及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)
}
}
如下谷歌浏览器就可以搜索到了

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

浙公网安备 33010602011771号