Files
v2ray_simple/netLayer/udpConn.go
2022-04-08 10:42:01 +08:00

226 lines
5.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package netLayer
import (
"errors"
"io"
"net"
"time"
"github.com/hahahrfool/v2ray_simple/utils"
)
var (
ErrTimeout = errors.New("timeout")
)
//Uni_UDPConn将一个udp连接包装成一个 只能向单一目标发送数据的 连接。
//Uni_UDPConn 实现了 net.Conn 和 net.PacketConn
type Uni_UDPConn struct {
peerAddr *net.UDPAddr
realConn *net.UDPConn
inMsgChan chan []byte
inMsgChanClosed bool
readDeadline PipeDeadline
writeDeadline PipeDeadline
clientFirstWriteChan chan int
clientFirstWriteChanClosed bool
unread []byte
isClient bool
}
//我们这里为了保证udp连接不会一直滞留导致 too many open files的情况,
// 主动设置了 内层udp连接的 read的 timeout为 UDP_timeout。
// 你依然可以设置 DialUDP 所返回的 net.Conn 的 Deadline, 这属于外层的Deadline,
// 不会影响底层 udp所强制设置的 deadline.
func DialUDP(raddr *net.UDPAddr) (*Uni_UDPConn, error) {
conn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
return nil, err
}
return NewUDPConn(raddr, conn, true), nil
}
//如果isClient为true则本函数返回后必须要调用一次 Write才能在Read读到数据. 这是udp的原理所决定的。
// 在客户端没有Write之前该udp连接实际上根本没有被建立, Read也就不可能/不应该 读到任何东西.
func NewUDPConn(raddr *net.UDPAddr, conn *net.UDPConn, isClient bool) *Uni_UDPConn {
inDataChan := make(chan []byte, 20)
theUDPConn := &Uni_UDPConn{raddr, conn, inDataChan, false, MakePipeDeadline(),
MakePipeDeadline(), make(chan int), false, []byte{}, isClient}
//不设置缓存的话,会导致发送过快 而导致丢包
conn.SetReadBuffer(MaxUDP_packetLen)
conn.SetWriteBuffer(MaxUDP_packetLen)
if isClient {
//客户端要自己循环读取udp,(但是要等待客户端自己先Write之后)
//我们这里为了保证udp连接不会一直滞留导致 too many open files的情况,
// 主动设置了 timeout为 UDP_timeout
go func() {
<-theUDPConn.clientFirstWriteChan
for {
buf := utils.GetPacket()
conn.SetReadDeadline(time.Now().Add(UDP_timeout))
n, _, err := conn.ReadFromUDP(buf)
//这里默认认为每个客户端都是在NAT后的,不怕遇到其它raddr,
// 即默认认为只可能读到 我们服务器发来的数据.
if n > 0 {
inDataChan <- buf[:n] //该数据会被ReadMsg和 Read读到
}
if err != nil {
theUDPConn.Close()
break
}
}
}()
}
return theUDPConn
}
func (uc *Uni_UDPConn) ReadMsg() (b []byte, err error) {
select {
case msg, ok := <-uc.inMsgChan:
if !ok {
return nil, io.EOF
}
return msg, nil
case <-uc.readDeadline.Wait():
return nil, ErrTimeout
}
}
//实现 net.PacketConn 可以与 miekg/dns 配合。返回的 addr 只可能为 之前预先配置的远程目标地址
func (uc *Uni_UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
select {
case msg, ok := <-uc.inMsgChan:
if !ok {
return 0, uc.peerAddr, io.EOF
}
n = copy(msg, p)
return n, uc.peerAddr, io.EOF
case <-uc.readDeadline.Wait():
return 0, uc.peerAddr, ErrTimeout
}
}
//实现 net.PacketConn 可以与 miekg/dns 配合。会无视传入的地址, 而使用 之前预先配置的远程目标地址
func (uc *Uni_UDPConn) WriteTo(p []byte, _ net.Addr) (n int, err error) {
return uc.Write(p)
}
func (uc *Uni_UDPConn) GetReadChan() chan []byte {
return uc.inMsgChan
}
func (uc *Uni_UDPConn) Read(buf []byte) (n int, err error) {
if len(uc.unread) > 0 {
n = copy(buf, uc.unread)
uc.unread = uc.unread[n:]
return
}
var msg []byte
msg, err = uc.ReadMsg()
if err != nil {
return
}
n = copy(buf, msg)
diff := len(msg) - n
if diff > 0 { //最好不要分段读否则我们将不会把缓存放回pool总之建议buf直接使用 utils.GetPacket
uc.unread = append(uc.unread, msg[n:]...)
} else {
//我们Read时统一用的 GetPacket, 所以整个拷贝完后可以放回
utils.PutPacket(msg)
}
return
}
func (uc *Uni_UDPConn) Write(buf []byte) (n int, err error) {
select {
case <-uc.writeDeadline.Wait():
return 0, ErrTimeout
default:
if uc.isClient {
time.Sleep(time.Millisecond) //不能发送太快,否则会出现丢包,实测简单1毫秒即可避免
/*
一些常见的丢包后出现的错误:
tls
bad mac
ws
non-zero rsv bits with no extension negotiated, Data: 0
裸奔curl客户端:
curl: (56) LibreSSL SSL_read: error:1404C3FC:SSL routines:ST_OK:sslv3 alert bad record mac, errno 0
*/
//if use writeToUDP at client end, we will get err Write write udp 127.0.0.1:50361->:60006: use of WriteTo with pre-connected connection
if !uc.clientFirstWriteChanClosed {
defer func() {
close(uc.clientFirstWriteChan)
uc.clientFirstWriteChanClosed = true
}()
}
return uc.realConn.Write(buf)
} else {
return uc.realConn.WriteToUDP(buf, uc.peerAddr)
}
}
}
func (uc *Uni_UDPConn) CloseMsgChan() {
if uc.isClient {
if !uc.clientFirstWriteChanClosed {
uc.clientFirstWriteChanClosed = true
close(uc.inMsgChan)
}
}
}
func (uc *Uni_UDPConn) Close() error {
if uc.isClient {
uc.CloseMsgChan()
return uc.realConn.Close()
}
return nil
}
func (b *Uni_UDPConn) LocalAddr() net.Addr { return b.realConn.LocalAddr() }
func (b *Uni_UDPConn) RemoteAddr() net.Addr { return b.peerAddr }
func (b *Uni_UDPConn) RemoteUDPAddr() *net.UDPAddr { return b.peerAddr }
func (b *Uni_UDPConn) SetWriteDeadline(t time.Time) error {
b.writeDeadline.Set(t)
return nil
}
func (b *Uni_UDPConn) SetReadDeadline(t time.Time) error {
b.readDeadline.Set(t)
return nil
}
func (b *Uni_UDPConn) SetDeadline(t time.Time) error {
b.readDeadline.Set(t)
b.writeDeadline.Set(t)
return nil
}