Server端函数、接口:
Listen函数:
func Listen(network, address string) (Listener, error)
network:选用的协议:TCP、UDP, 如:“tcp”或 “udp”
address:IP地址+端口号, 如:“127.0.0.1:8000”或 “:8000”
}
Listener 接口:
// Listen:指定IP + 端口,创建Accept,也是一个socket,但这个socket不和客户端直接通信,只用于创建要通信的socket
type Listener interface {
Accept() (Conn, error) // 阻塞监听客户端连接,返回一个用于通信的socket
Close() error
Addr() Addr
}
Conn 接口:
type Conn interface {
Read(b []byte) (n int, err error)
Write(b []byte) (n int, err error)
Close() error
LocalAddr() Addr
RemoteAddr() Addr
SetDeadline(t time.Time) error
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error
}
普通版:
TCP-服务端实现:
package main
import (
"fmt"
"net"
"os"
)
func main() {
// 监听该地址的连接请求,当有新的连接请求到来时,创建Accept实现连接
listen, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("net.Listen 出错了!!!", err)
return
}
defer listen.Close()
fmt.Println("服务端启动完毕,等待客户端建立连接...")
// 与用户端进行通信连接
accept, err := listen.Accept()
if err != nil {
fmt.Println("listen.Accept 出错了!!!", err)
return
}
defer accept.Close()
fmt.Println("与客户端建立连接成功...")
// 读取客户端发送的数据
bytes := make([]byte, 4096)
n, err := accept.Read(bytes)
if err != nil {
fmt.Println("accept.Read 出错了!!!", err)
return
}
fmt.Println("服务端读到:", string(bytes[:n]))
// 使用键盘录入向客户端发送数据
buf := make([]byte, 4096)
len, err := os.Stdin.Read(buf)
if err != nil {
fmt.Println("os.Stdin.Read 出错了!!!", err)
return
}
_, err = accept.Write(buf[:len])
if err != nil {
fmt.Println("accept.Write 出错了!!!", err)
return
}
}
TCP-客户端实现:
package main
import (
"fmt"
"net"
"os"
)
func main() {
// 发送建立连接请求,返回一个客户端连接对象
dial, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("net.Dial 出错了!!!", err)
return
}
defer dial.Close()
fmt.Println("与服务端建立连接成功...")
// 使用键盘录入向服务端发送数据
bytes := make([]byte, 4096)
// n:键盘录入的数据字节长度
n, err := os.Stdin.Read(bytes)
if err != nil {
fmt.Println("os.Stdin.Write 出错了!!!", err)
return
}
// 返回值1:发送数据字节长度 = n
_, err = dial.Write(bytes[:n])
if err != nil {
fmt.Println("dial.Write 出错了!!!", err)
return
}
// 读取服务端发送的数据
buf := make([]byte, 4096)
read, err := dial.Read(buf)
if err != nil {
fmt.Println("dial.Read 出错了!!!", err)
return
}
fmt.Println("客户端读到:", string(buf[:read]))
}
效果:只能是连接一次,且要客户端先写,服务端读到以后才能写。
TCP实现并发版:
服务端:
上面都是单机版的客户端通信,如果想要实现并发,需要使用Goroutine+循环实现
- 循环读取客户端发送的数据
- 如果客户端强制关闭连接需要做处理
- 客户端发送exit时
package main
import (
"fmt"
"net"
"os"
)
func main() {
// 监听该地址的连接请求,当有新地连接请求到来时,创建Accept实现连接
listen, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("net.Listen 出错了!!!", err)
return
}
defer listen.Close()
fmt.Println("服务端启动完毕,等待客户端建立连接...")
for {
// 与用户端进行通信连接,Accept负责创建通信的Socket,所以放在循环里,来一个客户端就创建一个Socket
accept, err := listen.Accept()
if err != nil {
fmt.Println("listen.Accept 出错了!!!", err)
return
}
defer accept.Close()
go ReadData(accept)
go WriteData(accept)
}
}
func ReadData(accept net.Conn) {
// 获取连接客户端的网络地址
addr := accept.RemoteAddr()
fmt.Println(addr, "与客户端建立连接成功...")
// 读取客户端发送的数据
bytes := make([]byte, 4096)
for {
n, err := accept.Read(bytes)
if n == 0 || "exit\n" == string(bytes[:n]) || "exit\r\n" == string(bytes[:n]) {
fmt.Println("检测到客户端 ", addr, " 已经断开连接!!!")
return
} else if err != nil {
fmt.Println("accept.Read 出错了!!!", err)
return
}
fmt.Println("收到客户端", addr, "发送的数据:", string(bytes[:n]))
}
}
func WriteData(accept net.Conn) {
// 使用键盘录入向客户端发送数据
bytes := make([]byte, 4096)
for {
n, err := os.Stdin.Read(bytes)
if err != nil {
fmt.Println("os.Stdin.Read 出错了!!!", err)
return
}
_, err = accept.Write(bytes[:n])
if err != nil {
fmt.Println("accept.Write 出错了!!!", err)
return
}
}
}
当客户端退出后,服务端依然可以发送信息,但没有客户端接收,会报错: write: broken pipe
解决:在发送之前判断客户端连接状态,且保证读到状态和发送消息一致,使用条件变量解决
package main
import (
"bufio"
"log"
"net"
"os"
)
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal("net.Listen 出错了!!!", err)
}
defer listen.Close()
log.Println("服务端启动完毕,等待客户端建立连接...")
for {
accept, err := listen.Accept()
if err != nil {
log.Println("listen.Accept 出错了!!!", err)
continue
}
go handleConn(accept)
}
}
func handleConn(conn net.Conn) {
addr := conn.RemoteAddr()
log.Println(addr, "与客户端建立连接成功...")
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
text := scanner.Text()
if text == "exit" {
log.Println("检测到客户端 ", addr, " 已经断开连接!!!")
break
}
log.Println("收到客户端", addr, "发送的数据:", text)
}
if err := scanner.Err(); err != nil {
log.Println("读取客户端数据出错了!!!", err)
}
conn.Close()
}
func handleStdin(conn net.Conn) {
defer conn.Close()
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
text := scanner.Text()
if text == "exit" {
log.Println("退出写入数据到客户端。")
break
}
_, err := conn.Write([]byte(text + "\n"))
if err != nil {
log.Println("写入数据到客户端出错了!!!", err)
break
}
}
}
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal("连接服务端出错了!!!", err)
}
defer conn.Close()
go handleStdin(conn)
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
text := scanner.Text()
log.Println("收到服务端发送的数据:", text)
}
if err := scanner.Err(); err != nil {
log.Fatal("读取服务端数据出错了!!!", err)
}
}
TCP实现并发-客户端:
package main
import (
"fmt"
"net"
"os"
)
func main() {
//主动发送连接请求
dial, err := net.Dial("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Println("et.Dial出错了", err)
return
}
defer dial.Close()
// os.Stdin():获取用户键盘录入,
go func() {
str := make([]byte, 4096)
for {
read, err := os.Stdin.Read(str)
if err != nil {
fmt.Println("os.Stdin.Read出错了", err)
continue
}
// 读到的数据写给服务器,读多少写多少
dial.Write(str[:read])
}
}()
buf := make([]byte, 4096)
// 回显服务器发送的数据,转成大写
for {
read, err := dial.Read(buf)
// read=0的说明对端关闭连接,如果关闭连接这里就不需要往下读数据了
if read == 0 {
fmt.Println("检测到服务端端已经断开连接!")
return
}
if err != nil {
fmt.Println("回显服务器发送的数据dial.Read出错了", err)
return
}
fmt.Println("客户端读到服务器的回显数据", string(buf[:read]))
}
}
UDP实现并发-服务器:
由于UDP是“无连接”的,所以,服务器端不需要额外创建监听套接字,只需要指定好IP和port,然后监听该地址,等待客户端与之建立连接,即可通信。
创建监听地址:
func ResolveUDPAddr(network, address string) (*UDPAddr, error)
创建监听连接:
func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error)
接收udp数据:
func (c *UDPConn) ReadFromUDP(b []byte) (int, *UDPAddr, error)
写出数据到udp:
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error)
演示:
package main
import (
"fmt"
"net"
)
func main() {
// 指定服务器的ip和端口,和TCP协议不一样,需要先写好再传给ListenUDP使用
ServerAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:8000")
if err != nil {
fmt.Println("net.ResolveUDPAddr err:", err)
return
}
fmt.Println("服务器启动成功!")
// 创建用户通信的Socket
udpConnect, err := net.ListenUDP("udp", ServerAddr)
if err != nil {
fmt.Println("net.ListenUDP err:", err)
return
}
defer udpConnect.Close()
fmt.Println("服务器创建Socket成功!")
// 读写客户端的数据
buf := make([]byte, 4096)
count := 0
for {
// 返回值:n int(读到的字节数), addr *UDPAddr(客户端的地址), err error
udpBytes, ConnectAddr, err := udpConnect.ReadFromUDP(buf)
if err != nil {
fmt.Println("udpConnect.ReadFromUDP err:", err)
return
}
count++
// 模拟处理数据
fmt.Printf("服务器读到第%v条数据 %v :%s\n", count, ConnectAddr, string(buf[:udpBytes]))
go func() {
// 回写数据到客户端
udpConnect.WriteToUDP([]byte("回写数据到客户端\n"), ConnectAddr)
}()
}
}
UDP实现并发-客户端:
package main
import (
"fmt"
"net"
"time"
)
func main() {
dial, err := net.Dial("udp", "127.0.0.1:8000")
if err != nil {
fmt.Println("net.Dial出错:", err)
return
}
defer dial.Close()
for {
// 发送数据
dial.Write([]byte("我是客户端"))
// 接收服务器返回的数据
buf := make([]byte, 4096)
read, err := dial.Read(buf)
if err != nil {
fmt.Println("accept.Read出错:", err)
return
}
// 接收数据后处理数据
fmt.Println("客户端获取到:", string(buf[:read]))
time.Sleep(time.Second)
}
}