内容简介:go-shadowsocks是一个非常好的学习golang网络编程的例子。在本篇博文中重点阐述golang网络通信几个各种不同协议下服务端和客户端的实现。网络通信协议主要有以下两种外加一种增加的协议
title: golang 网络编程
golang 网络编程
go-shadowsocks是一个非常好的学习golang网络编程的例子。在本篇博文中重点
阐述golang网络通信几个各种不同协议下服务端和客户端的实现。
网络通信协议主要有以下两种外加一种增加的协议
- golang tcp
- golang upd
- golang kcp:在upd上进行增强的协议,KCP 是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。
golang的一大优势就是多核、并行、网络编程。通过goroutine与channel可以很方便地协程,协程比线程更轻量级,占用资源更小,可以更好地适用与并行计算。
下面对这三种分别进行说明。
golang tcp
下面根据两种golang tcp server和client的例子进行说明
单connection的tcp server与client 端
tcp server
package main
import (
"net"
"fmt"
)
func main(){
// tcp 监听并接受端口
l, err := net.Listen("tcp", "127.0.0.1:65535")
if err != nil {
fmt.Println(err)
return
}
//最后关闭
defer l.Close()
fmt.Println("tcp服务端开始监听65535端口...")
// 使用循环一直接受连接
for {
fmt.Println("loop test")
//Listener.Accept() 接受连接
//conn 是双方的。和长度为1的channel有些类似。
c, err := l.Accept()
if err!= nil {
return
}
//处理tcp请求
go handleConnection(c)
}
}
func handleConnection(c net.Conn) {
//一些代码逻辑...
fmt.Println("tcp服务端开始处理请求...")
//读取
buffer := make([]byte, 1024)
//如果客户端无数据则会阻塞,服务端阻塞,直到等待客户端传递数据。
c.Read(buffer)
//服务端成功从阻塞状态走出,读取客户端的数据,并根据自身的接口输出buffer
c.Write(buffer)
fmt.Println("tcp服务端开始处理请求完毕...")
}
tcp client
package main
import (
"net"
"fmt"
)
func main() {
//net.dial 拨号 获取tcp连接
conn, err := net.Dial("tcp", "127.0.0.1:65535")
if err != nil {
fmt.Println(err)
return
}
fmt.Println("获取127.0.0.1:65535的tcp连接成功...")
defer conn.Close()
//客户端这里不用使用协程。使用协程的话main函数退出,所有 go 协程全部死掉。
conn.Write([]byte("echo data to server ,then to client!!!"))
fmt.Println("test server")
//读取到buffer
buffer := make([]byte, 1024)
//如果服务端没有把数据传递过来,那么客户端阻塞,直到服务端向其中写入了数据。
conn.Read(buffer)
fmt.Println(string(buffer))
}
net utils
- net.Dial(客户端调用,拨号)
- net.Listen(服务端调用,监听接口)
- TCPListener.Accept(服务端调用,接受,创建连接。)
- conn.Read(客户端服务端都会调用,读取conn中数据)
- conn.write(客户端服务端都会调用,读取conn中数据)
其它net 提供的函数可以查看API
为了能够让服务端处理多个连接,使用了协程来处理来自多个客户端的连接请求。
客户端的实现没有用到协程。
另外tcp server的for函数非常有趣,在上面的测试例子中不会无限打印 loop test
,说明存在阻塞。只有每次有新连接过来才会放开。
双connection的tcp server与client 端
在上面的例子中,存在着以下的问题:
- server与client之间只存在一条连接。可能会出现这样的情况:服务器需要向客户端推送一些数据。而客户端建立的连接正处于block状态。
- server与client建立的连接是短连接。
存在的解决方法如下
- 在client的实现上增加 server模块,监听来自于服务器的请求,并进行处理。
- 在server与client另外新建立一条长连接。通过setKeepLive来保活。
- 使用channel,保证客户端的协程不会死掉。
golang udp
udp 的通信无需创建lister,直接进行数据的传输。
udp server
package main
import (
"fmt"
"net"
)
func main() {
// 创建监听
socket, err := net.ListenUDP("udp4", &net.UDPAddr{
IP: net.IPv4(127,0,0,1),
Port: 23452,
})
if err != nil {
fmt.Println("监听失败!", err)
return
}
fmt.Println("监听成功")
defer socket.Close()
for {
// 读取数据
data := make([]byte, 4096)
read, remoteAddr, err := socket.ReadFromUDP(data)
if err != nil {
fmt.Println("读取数据失败!", err)
continue
}
fmt.Println(read, remoteAddr)
fmt.Printf("%s\n\n", data)
// 发送数据
senddata := []byte("hello client!")
_, err = socket.WriteToUDP(senddata, remoteAddr)
if err != nil {
return
fmt.Println("发送数据失败!", err)
}
}
}
udp client
package main
import (
"fmt"
"net"
)
func main() {
// 创建连接
socket, err := net.DialUDP("udp4", nil, &net.UDPAddr{
IP: net.IPv4(127,0,0,1),
Port: 23452,
})
if err != nil {
fmt.Println("连接失败!", err)
return
}
defer socket.Close()
// 发送数据
senddata := []byte("hello server!")
_, err = socket.Write(senddata)
if err != nil {
fmt.Println("发送数据失败!", err)
return
}
// 接收数据
data := make([]byte, 4096)
read, remoteAddr, err := socket.ReadFromUDP(data)
if err != nil {
fmt.Println("读取数据失败!", err)
return
}
fmt.Println(read, remoteAddr)
fmt.Printf("%s\n", data)
}
上面的server和client实现比较简单了,基本上没有什么要说的了。
golang kcp
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Usability for the Web
Tom Brinck、Darren Gergle、Scott D. Wood / Morgan Kaufmann / 2001-10-15 / USD 65.95
Every stage in the design of a new web site is an opportunity to meet or miss deadlines and budgetary goals. Every stage is an opportunity to boost or undercut the site's usability. Thi......一起来看看 《Usability for the Web》 这本书的介绍吧!
URL 编码/解码
URL 编码/解码
XML、JSON 在线转换
在线XML、JSON转换工具