Skip to main content

socket

网络分层

socket属于传输层(底层) http是应用层的

socket

socket核心逻辑

客户端是Dail建立连接,服务端是监听Accept建立连接

连接都能Write和Read,Read Write 参数和返回值都一样

socket-server 单次处理

只能接受一次请求 发送一次数据

package main

import (
"fmt"
"net"
"strings"
)

func main() {
ip:="127.0.0.1"
port :=8848
address:= fmt.Sprintf("%s:%d",ip,port)
//func Listen(network, address string) (Listener, error)
listener, err:= net.Listen("tcp",address)
//net.Listen("tcp",":8848") //简写, :前面默认本机 127.0.0.1

if err!=nil{
fmt.Println("net.Listen error:",err)
return
}

fmt.Println("监听中...")

//注意:这里的err和上面的err是一样的 go不会重复创建变量
//Accept() (Conn, error)
conn, err:=listener.Accept() //调用Accept()会阻塞
if err!=nil{
fmt.Println("listener.Accept error:",err)
return
}

fmt.Println("建立连接成功")

//创建一个容器,用于接受读取到的数据
buf := make([]byte,1024) //使用make来创建字节片段 byte ==> uint8
cnt,err:=conn.Read(buf) //客户端发来的数据有可能塞不满一个buf
//cnt:真正读取client发过来的数据长度
if err!=nil{
fmt.Println("conn.Read error:",err)
return
}
fmt.Println("client ====> sever, 长度:",cnt,",数据:",string(buf[:cnt]))

upper:=strings.ToUpper(string(buf[:cnt]))

cnt,err=conn.Write([]byte(upper))

if err!=nil{
fmt.Println("conn.Write error:",err)
return
}
fmt.Println("client <===== server, 长度:",cnt,"数据:",upper)

//关闭连接
conn.Close()
}

启动服务:

socket-client 单次请求

package main

import (
"fmt"
"log"
"net"
)

func main() {
conn,err:=net.Dial("tcp","127.0.0.1:8848")
if err!=nil{
log.Println("net.Dial error:",err)
return
}

fmt.Println("client与server连接建立成功,可以发送/接受数据了")

sendData:="hello"

//向服务器发送数据
cnt,err:=conn.Write([]byte(sendData))
if err!=nil{
log.Println("conn.Write error:",err)
return
}
fmt.Println("client ====> sever: 数据长度:",cnt,",数据为: ",sendData)

//接收服务器返回的数据
//创建buf,用于接收服务器返回的数据
buf:=make([]byte,1024)
cnt,err=conn.Read(buf)
if err!=nil{
log.Println("conn.Read error:",err)
return
}
fmt.Println("client <==== sever: 数据长度:",cnt,",数据为: ",string(buf[:cnt]))
//关闭连接
conn.Close()
}

运行看看效果

socket-server 多连接建立

上面的server demo 只能接受一次请求 发送一次数据,并不能处理多次连接 我们来优化一下

package main

import (
"fmt"
"net"
"strings"
)

func main() {
ip:="127.0.0.1"
port :=8848
address:= fmt.Sprintf("%s:%d",ip,port)
//func Listen(network, address string) (Listener, error)
listener, err:= net.Listen("tcp",address)
//net.Listen("tcp",":8848") //简写, :前面默认本机 127.0.0.1

if err!=nil{
fmt.Println("net.Listen error:",err)
return
}

//需求:
//server可以接受多个连接,=》主go程负责监听,子go程序负责数据处理
//每个连接可以接受处理多轮数据请求
for {
fmt.Println("监听中...")
//Accept() (Conn, error)
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener.Accept error:", err)
return
}

fmt.Println("建立连接成功")

go handleFunc(conn) //防止业务卡着这 业务需要单独的go程去启动
}
}

//处理具体的业务逻辑,需要将conn传递进来,每一新连接,conn都是不同的
func handleFunc(conn net.Conn) {
//for循环持续接收客户端发来的数据
for {//这个for循环 保证每一个连接可以多次接收处理客户端请求
//创建一个容器,用于接受读取到的数据
buf := make([]byte, 1024) //使用make来创建字节片段 byte ==> uint8

fmt.Println("准备读取客户端发来的数据...")

//如果客户端不发数据过来Read()函数会一直阻塞
cnt, err := conn.Read(buf) //客户端发来的数据有可能塞不满一个buf
//cnt:真正读取client发过来的数据长度
if err != nil {
fmt.Println("conn.Read error:", err)
return
}
fmt.Println("client ====> sever, 长度:", cnt, ",数据:", string(buf[:cnt]))

upper := strings.ToUpper(string(buf[:cnt]))

cnt, err = conn.Write([]byte(upper))

if err != nil {
fmt.Println("conn.Write error:", err)
return
}
fmt.Println("client <===== server, 长度:", cnt, "数据:", upper)
}

//关闭连接
_=conn.Close() //_不是新变量 不用加:
}

客户端的代码不变

我们来测试一下:

服务端先启动

客户端开始向服务器发送第一次数据

服务端第1次响应

客户端再次发送数据,服务端第2次响应

可以看到现在我们的服务端已经很高级了,可以持续处理数据了

socket-client 多次发送数据

当前我们的客户端只能发送一次数据,如果想发送多个数据怎么办?

建立连接之后 我们让它不断的去写就好了

package main

import (
"fmt"
"log"
"net"
"time"
)

func main() {
conn,err:=net.Dial("tcp","127.0.0.1:8848")
if err!=nil{
log.Println("net.Dial error:",err)
return
}

fmt.Println("client与server连接建立成功,可以发送/接受数据了")

sendData:="hello"

//向服务器发送数据
for { //保证同一个连接可以多次向服务端发送数据
cnt, err := conn.Write([]byte(sendData))
if err != nil {
log.Println("conn.Write error:", err)
return
}
fmt.Println("client ====> sever: 数据长度:", cnt, ",数据为: ", sendData)

//接收服务器返回的数据
//创建buf,用于接收服务器返回的数据
buf := make([]byte, 1024)
cnt, err = conn.Read(buf)
if err != nil {
log.Println("conn.Read error:", err)
return
}
fmt.Println("client <==== sever: 数据长度:", cnt, ",数据为: ", string(buf[:cnt]))

//sleep一下,防止发送太快
time.Sleep(time.Second)
}
//关闭连接
conn.Close()
}

启动服务,我们来看一下效果,客户端不停的向服务端发送"hello",服务端不停的回复大写的”HELLO“

当客户端断主动开 服务端会报错

如果把handleFunc(conn net.Conn)里面的for循环注销,会发生什么?

//处理具体的业务逻辑,需要将conn传递进来,每一新连接,conn都是不同的
func handleFunc(conn net.Conn) {
//for循环持续接收客户端发来的数据
//for {
//创建一个容器,用于接受读取到的数据
buf := make([]byte, 1024) //使用make来创建字节片段 byte ==> uint8

fmt.Println("准备读取客户端发来的数据...")

//如果客户端不发数据过来Read()函数会一直阻塞
cnt, err := conn.Read(buf) //客户端发来的数据有可能塞不满一个buf
//cnt:真正读取client发过来的数据长度
if err != nil {
fmt.Println("conn.Read error:", err)
return
}
fmt.Println("client ====> sever, 长度:", cnt, ",数据:", string(buf[:cnt]))

upper := strings.ToUpper(string(buf[:cnt]))

cnt, err = conn.Write([]byte(upper))

if err != nil {
fmt.Println("conn.Write error:", err)
return
}
fmt.Println("client <===== server, 长度:", cnt, "数据:", upper)
//}

//关闭连接
_=conn.Close() //_不是新变量 不用加:
}

再次启动服务:

客户端第一次发送数据:

服务端第一次响应:

客户端第二次发送数据:

服务端第二次响应:

警告

上述示例中服务端的两个for循环不能随便删: 第一个for循环是用来处理多个连接的;第二个for循环是为了处理单个连接的多次请求