mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-18 06:40:42 +08:00
添加自定义网络层功能;添加udp网络层支持
配置中,listen和dial中,可添加 network = "udp" 字段,不添加则默认tcp
This commit is contained in:
37
config.go
37
config.go
@@ -1,40 +1,3 @@
|
||||
/*
|
||||
Package main 读取配置文件,将其内容转化为 proxy.Client和 proxy.Server,然后进行代理转发.
|
||||
|
||||
命令行参数请使用 --help查看详情。
|
||||
|
||||
如果一个命令行参数无法在标准配置中进行配置,那么它就属于高级选项,或者不推荐的选项,或者正在开发中的功能.
|
||||
|
||||
Config Format 配置格式
|
||||
|
||||
一共有三种配置格式,极简模式,标准模式,兼容模式。
|
||||
|
||||
“极简模式”(即 verysimple mode),入口和出口仅有一个,而且都是使用共享链接的url格式来配置.
|
||||
|
||||
标准模式使用toml格式。
|
||||
|
||||
兼容模式可以兼容v2ray现有json格式。(暂未实现)。
|
||||
|
||||
极简模式的理念是,配置文件的字符尽量少,尽量短小精悍;
|
||||
|
||||
还有个命令行模式,就是直接把极简模式的url 放到命令行参数中,比如:
|
||||
|
||||
verysimple -L socks5://sfdfsaf -D direct://
|
||||
|
||||
|
||||
Structure 本项目结构
|
||||
|
||||
main -> proxy.Standard(配置文件) -> netLayer-> tlsLayer -> httpLayer -> ws -> proxy.
|
||||
|
||||
main中,读取配置文件,生成 Dail、Listen 和 RoutePolicy 对象后,开始监听;
|
||||
|
||||
用 netLayer操纵路由,用tlsLayer嗅探tls,用httpLayer操纵回落,可选经过ws, 然后都搞好后,传到proxy,然后就开始转发
|
||||
|
||||
Other - 其他
|
||||
|
||||
另外,本作暂时不考虑引入外界log包。依赖越少越好。
|
||||
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
|
88
main.go
88
main.go
@@ -9,9 +9,7 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/hahahrfool/v2ray_simple/httpLayer"
|
||||
"github.com/hahahrfool/v2ray_simple/netLayer"
|
||||
@@ -35,8 +33,6 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
desc = "v2ray_simple, a very simple implementation of V2Ray, 并且在某些地方试图走在v2ray前面"
|
||||
|
||||
configFileName string
|
||||
|
||||
uniqueTestDomain string //有时需要测试到单一网站的流量,此时为了避免其它干扰,需要在这里声明 一下 该域名,然后程序里会进行过滤
|
||||
@@ -55,17 +51,17 @@ var (
|
||||
serversTagMap = make(map[string]proxy.Server)
|
||||
clientsTagMap = make(map[string]proxy.Client)
|
||||
|
||||
listenURL string
|
||||
dialURL string
|
||||
|
||||
tls_lazy_encrypt bool
|
||||
tls_lazy_secure bool
|
||||
|
||||
routePolicy *netLayer.RoutePolicy
|
||||
|
||||
listenURL string
|
||||
dialURL string
|
||||
|
||||
isServerEnd bool
|
||||
|
||||
mainFallback *httpLayer.ClassicFallback
|
||||
|
||||
isServerEnd bool //这个是代码里推断的,不一定准确;不过目前仅被用于tls lazy encrypt,所以不是很重要
|
||||
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -206,7 +202,9 @@ func main() {
|
||||
if confMode == simpleMode {
|
||||
go listenSer(nil, defaultInServer)
|
||||
} else {
|
||||
go listenAllServers()
|
||||
for _, inServer := range allServers {
|
||||
go listenSer(nil, inServer)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
@@ -216,60 +214,27 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func listenAllServers() {
|
||||
|
||||
for _, inServer := range allServers {
|
||||
|
||||
listener, err := net.Listen("tcp", inServer.AddrStr())
|
||||
defer inServer.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln("can not listen inServer on", inServer.AddrStr(), err)
|
||||
|
||||
}
|
||||
|
||||
if utils.CanLogInfo() {
|
||||
log.Println(proxy.GetFullName(inServer), "is listening TCP on ", inServer.AddrStr())
|
||||
|
||||
}
|
||||
|
||||
go listenSer(listener, inServer)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func listenSer(listener net.Listener, inServer proxy.Server) {
|
||||
|
||||
var err error
|
||||
if listener == nil {
|
||||
listener, err = net.Listen("tcp", inServer.AddrStr())
|
||||
theFunc := func(conn net.Conn) {
|
||||
handleNewIncomeConnection(inServer, conn)
|
||||
}
|
||||
|
||||
network := inServer.Network()
|
||||
err := netLayer.ListenAndAccept(network, inServer.AddrStr(), theFunc)
|
||||
|
||||
if err == nil {
|
||||
if utils.CanLogInfo() {
|
||||
log.Println(proxy.GetFullName(inServer), "is listening ", network, "on", inServer.AddrStr())
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
if err != nil {
|
||||
log.Fatalln("can not listen inServer on", inServer.AddrStr(), err)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
lc, err := listener.Accept()
|
||||
if err != nil {
|
||||
errStr := err.Error()
|
||||
if strings.Contains(errStr, "closed") {
|
||||
log.Println("local connection closed", err)
|
||||
break
|
||||
}
|
||||
log.Println("failed to accepted connection: ", err)
|
||||
if strings.Contains(errStr, "too many") {
|
||||
log.Println("To many incoming conn! Sleep ", errStr)
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
go handleNewIncomeConnection(inServer, lc)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func handleNewIncomeConnection(inServer proxy.Server, thisLocalConnectionInstance net.Conn) {
|
||||
@@ -325,6 +290,8 @@ func handleNewIncomeConnection(inServer proxy.Server, thisLocalConnectionInstanc
|
||||
|
||||
}
|
||||
|
||||
//log.Println("handshake passed tls")
|
||||
|
||||
if adv := inServer.AdvancedLayer(); adv != "" {
|
||||
if adv == "ws" {
|
||||
wsConn, err := ws.Handshake(inServer.GetListenConf().Path, thisLocalConnectionInstance)
|
||||
@@ -463,7 +430,7 @@ afterLocalServerHandshake:
|
||||
}
|
||||
|
||||
//如果目标是udp则要分情况讨论
|
||||
if targetAddr.IsUDP {
|
||||
if targetAddr.Network == "udp" {
|
||||
|
||||
switch inServer.Name() {
|
||||
case "vlesss":
|
||||
@@ -535,7 +502,7 @@ afterLocalServerHandshake:
|
||||
// 因为direct使用 proxy.RelayUDP_to_Direct 函数 直接实现了fullcone
|
||||
// 那么我们只需要传入一个 UDP_Extractor 即可
|
||||
|
||||
if targetAddr.IsUDP {
|
||||
if targetAddr.Network == "udp" {
|
||||
|
||||
var unknownRemoteAddrMsgWriter netLayer.UDPResponseWriter
|
||||
|
||||
@@ -587,12 +554,13 @@ afterLocalServerHandshake:
|
||||
//log.Println("will dial", client.AddrStr())
|
||||
|
||||
realTargetAddr, _ = netLayer.NewAddr(client.AddrStr())
|
||||
realTargetAddr.Network = client.Network()
|
||||
}
|
||||
|
||||
clientConn, err := realTargetAddr.Dial()
|
||||
if err != nil {
|
||||
if utils.CanLogErr() {
|
||||
log.Println("failed in dial", targetAddr.String(), ", Reason: ", err)
|
||||
log.Println("failed in dial", realTargetAddr.String(), ", Reason: ", err)
|
||||
|
||||
}
|
||||
return
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Atyp, for vless and vmess; 注意与 trojan和socks5的区别,trojan和socks5的相同含义的值是1,3,4
|
||||
@@ -15,17 +16,20 @@ const (
|
||||
|
||||
// Addr represents a address that you want to access by proxy. Either Name or IP is used exclusively.
|
||||
type Addr struct {
|
||||
Name string // domain name
|
||||
Name string // domain name, 或者 unix domain socket 的 文件路径
|
||||
IP net.IP
|
||||
Port int
|
||||
IsUDP bool
|
||||
//IsUDP bool
|
||||
|
||||
Network string
|
||||
}
|
||||
|
||||
func NewAddrFromUDPAddr(addr *net.UDPAddr) *Addr {
|
||||
return &Addr{
|
||||
IP: addr.IP,
|
||||
Port: addr.Port,
|
||||
IsUDP: true,
|
||||
//IsUDP: true,
|
||||
Network: "udp",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,9 +78,12 @@ func NewAddrByURL(addrStr string) (*Addr, error) {
|
||||
a.Name = host
|
||||
}
|
||||
|
||||
a.Network = u.Scheme
|
||||
|
||||
/*
|
||||
if u.Scheme == "udp" {
|
||||
a.IsUDP = true
|
||||
}
|
||||
}*/
|
||||
|
||||
return a, nil
|
||||
}
|
||||
@@ -92,14 +99,12 @@ func (a *Addr) String() string {
|
||||
|
||||
func (a *Addr) UrlString() string {
|
||||
str := a.String()
|
||||
if a.IsUDP {
|
||||
return "udp://" + str
|
||||
}
|
||||
return "tcp://" + str
|
||||
|
||||
return a.Network + "://" + str
|
||||
}
|
||||
|
||||
func (a *Addr) ToUDPAddr() *net.UDPAddr {
|
||||
if !a.IsUDP {
|
||||
if a.Network != "udp" {
|
||||
return nil
|
||||
}
|
||||
ua, err := net.ResolveUDPAddr("udp", a.String())
|
||||
@@ -118,10 +123,19 @@ func (a *Addr) HostStr() string {
|
||||
}
|
||||
|
||||
func (addr *Addr) Dial() (net.Conn, error) {
|
||||
if addr.IsUDP {
|
||||
return net.Dial("udp", addr.String())
|
||||
}
|
||||
//log.Println("Dial called", addr, addr.Network)
|
||||
|
||||
switch addr.Network {
|
||||
case "":
|
||||
return net.Dial("tcp", addr.String())
|
||||
|
||||
case "udp":
|
||||
return DialUDP(addr.ToUDPAddr())
|
||||
default:
|
||||
return net.Dial(addr.Network, addr.String())
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Returned address bytes and type
|
||||
@@ -193,3 +207,16 @@ func ParseStrToAddr(s string) (atyp byte, addr []byte, port_uint16 uint16, err e
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func UDPAddr2Byte(addr *net.UDPAddr) [6]byte {
|
||||
ip := addr.IP.To4()
|
||||
|
||||
port := uint16(addr.Port)
|
||||
|
||||
var allByte [6]byte
|
||||
abs := allByte[:]
|
||||
copy(abs, ip)
|
||||
copy(abs[4:], (*(*[2]byte)(unsafe.Pointer(&port)))[:])
|
||||
|
||||
return allByte
|
||||
}
|
||||
|
@@ -1,10 +1,11 @@
|
||||
package netLayer
|
||||
|
||||
const (
|
||||
// transport Layer
|
||||
// transport Layer, 使用uint16 mask,所以最多支持16种
|
||||
|
||||
TCP uint16 = 1 << iota
|
||||
UDP
|
||||
UNIX //unix domain socket
|
||||
Raw_socket
|
||||
KCP
|
||||
Quic //quic是一个横跨多个层的协议,这里也算一个,毕竟与kcp类似
|
||||
@@ -19,6 +20,8 @@ func StrToTransportProtocol(s string) uint16 {
|
||||
return TCP
|
||||
case "udp":
|
||||
return UDP
|
||||
case "unix":
|
||||
return UNIX
|
||||
case "raw":
|
||||
return Raw_socket
|
||||
case "kcp":
|
||||
|
75
netLayer/deadline.go
Normal file
75
netLayer/deadline.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package netLayer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PipeDeadline is an abstraction for handling timeouts.
|
||||
//copied from golang standard package net
|
||||
type PipeDeadline struct {
|
||||
mu sync.Mutex // Guards timer and cancel
|
||||
timer *time.Timer
|
||||
cancel chan struct{} // Must be non-nil
|
||||
}
|
||||
|
||||
func MakePipeDeadline() PipeDeadline {
|
||||
return PipeDeadline{cancel: make(chan struct{})}
|
||||
}
|
||||
|
||||
// set sets the point in time when the deadline will time out.
|
||||
// A timeout event is signaled by closing the channel returned by waiter.
|
||||
// Once a timeout has occurred, the deadline can be refreshed by specifying a
|
||||
// t value in the future.
|
||||
//
|
||||
// A zero value for t prevents timeout.
|
||||
func (d *PipeDeadline) Set(t time.Time) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
if d.timer != nil && !d.timer.Stop() {
|
||||
<-d.cancel // Wait for the timer callback to finish and close cancel
|
||||
}
|
||||
d.timer = nil
|
||||
|
||||
// Time is zero, then there is no deadline.
|
||||
closed := isClosedChan(d.cancel)
|
||||
if t.IsZero() {
|
||||
if closed {
|
||||
d.cancel = make(chan struct{})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Time in the future, setup a timer to cancel in the future.
|
||||
if dur := time.Until(t); dur > 0 {
|
||||
if closed {
|
||||
d.cancel = make(chan struct{})
|
||||
}
|
||||
d.timer = time.AfterFunc(dur, func() {
|
||||
close(d.cancel)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Time in the past, so close immediately.
|
||||
if !closed {
|
||||
close(d.cancel)
|
||||
}
|
||||
}
|
||||
|
||||
// wait returns a channel that is closed when the deadline is exceeded.
|
||||
func (d *PipeDeadline) Wait() chan struct{} {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
return d.cancel
|
||||
}
|
||||
|
||||
func isClosedChan(c <-chan struct{}) bool {
|
||||
select {
|
||||
case <-c:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
63
netLayer/listen.go
Normal file
63
netLayer/listen.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package netLayer
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hahahrfool/v2ray_simple/utils"
|
||||
)
|
||||
|
||||
func loopAccept(listener net.Listener, acceptFunc func(net.Conn)) {
|
||||
for {
|
||||
newc, err := listener.Accept()
|
||||
if err != nil {
|
||||
errStr := err.Error()
|
||||
if strings.Contains(errStr, "closed") {
|
||||
if utils.CanLogDebug() {
|
||||
log.Println("local connection closed", err)
|
||||
|
||||
}
|
||||
break
|
||||
}
|
||||
if utils.CanLogWarn() {
|
||||
log.Println("failed to accept connection: ", err)
|
||||
}
|
||||
if strings.Contains(errStr, "too many") {
|
||||
if utils.CanLogWarn() {
|
||||
log.Println("To many incoming conn! Sleep ", errStr)
|
||||
|
||||
}
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
}
|
||||
continue
|
||||
}
|
||||
go acceptFunc(newc)
|
||||
}
|
||||
}
|
||||
|
||||
// ListenAndAccept 试图监听 所有类型的网络,包括tcp, udp 和 unix domain socket.
|
||||
// 非阻塞,在自己的goroutine中监听.
|
||||
func ListenAndAccept(network, addr string, acceptFunc func(net.Conn)) error {
|
||||
switch network {
|
||||
default:
|
||||
listener, err := net.Listen(network, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go loopAccept(listener, acceptFunc)
|
||||
case "udp":
|
||||
ua, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
listener, err := NewUDPListener(ua)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go loopAccept(listener, acceptFunc)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -73,6 +73,11 @@ func (sg *RouteSet) IsTransportProtocolAllowed(p uint16) bool {
|
||||
return sg.AllowedTransportLayerProtocols&p > 0
|
||||
}
|
||||
|
||||
func (sg *RouteSet) IsAddrNetworkAllowed(a *Addr) bool {
|
||||
p := StrToTransportProtocol(a.Network)
|
||||
return sg.IsTransportProtocolAllowed(p)
|
||||
}
|
||||
|
||||
func (sg *RouteSet) IsUDPAllowed() bool {
|
||||
return sg.IsTransportProtocolAllowed(UDP)
|
||||
}
|
||||
@@ -85,7 +90,7 @@ func (sg *RouteSet) IsAddrIn(a *Addr) bool {
|
||||
//我们先过滤传输层,再过滤网络层
|
||||
|
||||
//目前我们仅支持udp和tcp这两种传输层协议,所以可以如此。以后如果加了更多的话,还需改动
|
||||
if a.IsUDP && !sg.IsUDPAllowed() || !a.IsUDP && !sg.IsTCPAllowed() {
|
||||
if !sg.IsAddrNetworkAllowed(a) {
|
||||
return false
|
||||
|
||||
} else if sg.NetRanger == nil && sg.IPs == nil && sg.Domains == nil && sg.InTags == nil && sg.Countries == nil {
|
||||
|
157
netLayer/udpConn.go
Normal file
157
netLayer/udpConn.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package netLayer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/hahahrfool/v2ray_simple/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTimeout = errors.New("timeout")
|
||||
)
|
||||
|
||||
//UDPConn 实现了 net.Conn
|
||||
type UDPConn struct {
|
||||
peerAddr *net.UDPAddr
|
||||
realConn *net.UDPConn
|
||||
|
||||
inMsgChan chan []byte
|
||||
|
||||
readDeadline PipeDeadline
|
||||
writeDeadline PipeDeadline
|
||||
|
||||
unread []byte
|
||||
isClient bool
|
||||
}
|
||||
|
||||
func DialUDP(raddr *net.UDPAddr) (net.Conn, error) {
|
||||
conn, err := net.DialUDP("udp", nil, raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewUDPConn(raddr, conn, true), nil
|
||||
}
|
||||
|
||||
func NewUDPConn(raddr *net.UDPAddr, conn *net.UDPConn, isClient bool) *UDPConn {
|
||||
inDataChan := make(chan []byte, 50)
|
||||
theUDPConn := &UDPConn{raddr, conn, inDataChan, MakePipeDeadline(),
|
||||
MakePipeDeadline(), []byte{}, isClient}
|
||||
|
||||
//不设置缓存的话,会导致发送过快 而导致丢包
|
||||
conn.SetReadBuffer(64 * 1024)
|
||||
conn.SetWriteBuffer(64 * 1024)
|
||||
|
||||
if isClient {
|
||||
|
||||
//客户端要自己循环读取udp
|
||||
go func() {
|
||||
for {
|
||||
buf := utils.GetPacket()
|
||||
n, _, err := conn.ReadFromUDP(buf)
|
||||
|
||||
inDataChan <- buf[:n] //该数据会被ReadMsg和 Read读到
|
||||
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
}
|
||||
return theUDPConn
|
||||
}
|
||||
|
||||
func (uc *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
|
||||
}
|
||||
}
|
||||
func (uc *UDPConn) GetReadChan() chan []byte {
|
||||
return uc.inMsgChan
|
||||
}
|
||||
|
||||
func (uc *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 0, err
|
||||
}
|
||||
n = copy(buf, msg)
|
||||
|
||||
diff := len(msg) - n
|
||||
if diff > 0 { //最好不要分段读,否则我们将不会把缓存放回pool,总之建议buf直接使用 utils.GetPacket
|
||||
|
||||
uc.unread = append(uc.unread, msg[n:]...)
|
||||
} else {
|
||||
utils.PutPacket(msg)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (uc *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
|
||||
|
||||
return uc.realConn.Write(buf)
|
||||
} else {
|
||||
return uc.realConn.WriteToUDP(buf, uc.peerAddr)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (*UDPConn) Close() error { return nil }
|
||||
|
||||
func (b *UDPConn) LocalAddr() net.Addr { return b.realConn.LocalAddr() }
|
||||
func (b *UDPConn) RemoteAddr() net.Addr { return b.peerAddr }
|
||||
func (b *UDPConn) RemoteUDPAddr() *net.UDPAddr { return b.peerAddr }
|
||||
|
||||
func (b *UDPConn) SetWriteDeadline(t time.Time) error {
|
||||
b.writeDeadline.Set(t)
|
||||
return nil
|
||||
}
|
||||
func (b *UDPConn) SetReadDeadline(t time.Time) error {
|
||||
b.readDeadline.Set(t)
|
||||
return nil
|
||||
}
|
||||
func (b *UDPConn) SetDeadline(t time.Time) error {
|
||||
b.readDeadline.Set(t)
|
||||
b.writeDeadline.Set(t)
|
||||
return nil
|
||||
}
|
@@ -10,6 +10,7 @@ const (
|
||||
MaxUDP_packetLen = 64 * 1024 // 关于 udp包数据长度,可参考 https://cloud.tencent.com/developer/article/1021196
|
||||
)
|
||||
|
||||
//本文件内含 一些 转发 udp 数据的 接口与方法
|
||||
//////////////////// 接口 ////////////////////
|
||||
|
||||
type UDPRequestReader interface {
|
144
netLayer/udplistener.go
Normal file
144
netLayer/udplistener.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package netLayer
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/hahahrfool/v2ray_simple/utils"
|
||||
)
|
||||
|
||||
//UDPListener provide a udp listener which acts just like a tcp listener.
|
||||
//It will block when called Accept.The Accept() returns a new Conn which
|
||||
//just work for the communication between localhost and the remote dialer.
|
||||
//
|
||||
//UDPListener can also dial a remote host by calling NewConn.
|
||||
// UDPListener 监听 UDPAddr,并不断对新远程地址 创建 新UDPConn,
|
||||
// 并把读到的信息发送给 UDPConn;
|
||||
// UDPListener 实现了 net.Listener
|
||||
type UDPListener struct {
|
||||
conn *net.UDPConn
|
||||
|
||||
newConnChan chan *UDPConn
|
||||
connMap map[[6]byte]*UDPConn
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
func NewUDPListener(laddr *net.UDPAddr) (*UDPListener, error) {
|
||||
c, err := net.ListenUDP("udp4", laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewUDPListenerConn(c)
|
||||
}
|
||||
|
||||
func NewUDPListenerConn(conn *net.UDPConn) (*UDPListener, error) {
|
||||
ul := new(UDPListener)
|
||||
ul.conn = conn
|
||||
ul.connMap = make(map[[6]byte]*UDPConn)
|
||||
ul.newConnChan = make(chan *UDPConn, 100)
|
||||
go ul.run()
|
||||
|
||||
return ul, nil
|
||||
}
|
||||
|
||||
//It can be used to dial a remote udp
|
||||
func (ul *UDPListener) NewConn(raddr *net.UDPAddr) *UDPConn {
|
||||
addrb := UDPAddr2Byte(raddr)
|
||||
return ul.newConn(raddr, addrb)
|
||||
}
|
||||
|
||||
//newConn 创建一个新的 UDPConn,并存储在 ul.connMap 中
|
||||
func (ul *UDPListener) newConn(raddr *net.UDPAddr, addrb [6]byte) *UDPConn {
|
||||
newC := NewUDPConn(raddr, ul.conn, false)
|
||||
ul.mux.Lock()
|
||||
ul.connMap[addrb] = newC
|
||||
ul.mux.Unlock()
|
||||
return newC
|
||||
}
|
||||
|
||||
func (ul *UDPListener) DeleteConn(addrb [6]byte) {
|
||||
ul.mux.Lock()
|
||||
delete(ul.connMap, addrb)
|
||||
ul.mux.Unlock()
|
||||
}
|
||||
|
||||
func (ul *UDPListener) Accept() (net.Conn, error) {
|
||||
c, ok := <-ul.newConnChan
|
||||
if !ok {
|
||||
return nil, io.EOF
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
//Once closed, it cannot be used again.
|
||||
// it calls ul.CloseClients()
|
||||
func (ul *UDPListener) Close() error {
|
||||
|
||||
err := ul.conn.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ul.CloseClients()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//UDPListener has a very fast way to close all the clients' connection.
|
||||
// If the server side of the client connection is reading, it will get an EOF error.
|
||||
// The application can then tell the remote client that it will be closed by sending a message using its custom protocol.
|
||||
//
|
||||
//Once closed, it cannot be used again.
|
||||
func (ul *UDPListener) CloseClients() error {
|
||||
close(ul.newConnChan)
|
||||
|
||||
ul.mux.Lock()
|
||||
for _, c := range ul.connMap {
|
||||
close(c.inMsgChan)
|
||||
}
|
||||
ul.mux.Unlock()
|
||||
|
||||
//err := ul.conn.Close()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ul *UDPListener) Addr() net.Addr {
|
||||
return ul.conn.LocalAddr()
|
||||
}
|
||||
|
||||
//循环读取udp数据,对新连接会创建 UDPConn,然后把数据通过chan 传递给UDPConn
|
||||
func (ul *UDPListener) run() {
|
||||
conn := ul.conn
|
||||
for {
|
||||
buf := utils.GetPacket()
|
||||
n, raddr, err := conn.ReadFromUDP(buf)
|
||||
|
||||
go func(theraddr *net.UDPAddr, thebuf []byte) {
|
||||
addrb := UDPAddr2Byte(theraddr)
|
||||
var oldConn *UDPConn
|
||||
|
||||
ul.mux.RLock()
|
||||
oldConn = ul.connMap[addrb] //每次都从connMap查找是很慢的,先这样,以后使用更快的方法
|
||||
ul.mux.RUnlock()
|
||||
|
||||
if oldConn == nil {
|
||||
oldConn = ul.newConn(raddr, addrb)
|
||||
|
||||
ul.newConnChan <- oldConn //此时 ul 的 Accept的调用者就会收到一个新Conn
|
||||
}
|
||||
|
||||
oldConn.inMsgChan <- thebuf[:n]
|
||||
|
||||
}(raddr, buf)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -20,14 +20,14 @@ type CommonConf struct {
|
||||
Tag string `toml:"tag"` //可选
|
||||
Protocol string `toml:"protocol"` //约定,如果一个Protocol尾缀去掉了's'后仍然是一个有效协议,则该协议使用了 tls。这种方法继承自 v2simple,适合极简模式
|
||||
Uuid string `toml:"uuid"` //一个用户的唯一标识,建议使用uuid,但也不一定
|
||||
Host string `toml:"host"` //ip 或域名.
|
||||
Host string `toml:"host"` //ip 或域名. 若unix domain socket 则为文件路径
|
||||
IP string `toml:"ip"` //给出Host后,该项可以省略; 既有Host又有ip的情况比较适合cdn
|
||||
Port int `toml:"port"`
|
||||
Port int `toml:"port"` //若Network不为 unix , 则port项必填
|
||||
Version int `toml:"ver"` //可选
|
||||
TLS bool `toml:"tls"` //可选. 如果不使用 's' 后缀法,则还可以配置这一项来更清晰第标明使用tls
|
||||
Insecure bool `toml:"insecure"` //tls 是否安全
|
||||
|
||||
IsUDP bool `toml:"udp"` //默认使用tcp监听,如果udp项给出了,则用udp监听。一些即能监听udp又能监听tcp的协议就需要这一项配置.
|
||||
Network string `toml:"network"` //默认使用tcp, network可选值为 tcp,udp, uds;
|
||||
|
||||
AdvancedLayer string `toml:"advancedLayer"` //可不填,或者为ws,或者为grpc
|
||||
|
||||
@@ -37,7 +37,15 @@ type CommonConf struct {
|
||||
}
|
||||
|
||||
func (cc *CommonConf) GetAddr() string {
|
||||
switch cc.Network {
|
||||
case "unix":
|
||||
return cc.Host
|
||||
|
||||
default:
|
||||
return cc.Host + ":" + strconv.Itoa(cc.Port)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 监听所使用的设置, 使用者可以叫 listener 或者 inServer
|
||||
|
@@ -231,8 +231,9 @@ func configCommon(ser ProxyCommon, cc *CommonConf) {
|
||||
|
||||
}
|
||||
|
||||
//setIsDial(true),setDialConf(dc), call configCommon
|
||||
//setNetwork, setIsDial(true),setDialConf(dc), call configCommon
|
||||
func configCommonForClient(cli ProxyCommon, dc *DialConf) {
|
||||
cli.setNetwork(dc.Network)
|
||||
cli.setIsDial(true)
|
||||
cli.setDialConf(dc)
|
||||
cli.setTag(dc.Tag)
|
||||
@@ -244,8 +245,9 @@ func configCommonForClient(cli ProxyCommon, dc *DialConf) {
|
||||
}
|
||||
}
|
||||
|
||||
//setTag, setCantRoute,setListenConf(lc), call configCommon
|
||||
//setNetwork, setTag, setCantRoute,setListenConf(lc), call configCommon
|
||||
func configCommonForServer(ser ProxyCommon, lc *ListenConf) {
|
||||
ser.setNetwork(lc.Network)
|
||||
ser.setListenConf(lc)
|
||||
ser.setTag(lc.Tag)
|
||||
ser.setCantRoute(lc.NoRoute)
|
||||
|
@@ -53,11 +53,8 @@ type Server interface {
|
||||
// 这里认为, tcp/udp/kcp/raw_socket 是FirstName,具体的协议名称是 LastName, 中间层是 MiddleName
|
||||
// An Example of a full name: tcp+tls+ws+vless
|
||||
func GetFullName(pc ProxyCommon) string {
|
||||
if pc.IsUDP() {
|
||||
return "udp" + pc.MiddleName() + pc.Name()
|
||||
|
||||
}
|
||||
return "tcp" + pc.MiddleName() + pc.Name()
|
||||
return pc.Network() + pc.MiddleName() + pc.Name()
|
||||
|
||||
}
|
||||
|
||||
@@ -67,40 +64,49 @@ type ProxyCommon interface {
|
||||
Name() string //代理协议名称, 如vless
|
||||
MiddleName() string //其它VSI层 所使用的协议,如 +tls+ws
|
||||
|
||||
AddrStr() string //地址,在server就是监听地址,在client就是拨号地址
|
||||
/////////////////// 网络层/传输层 ///////////////////
|
||||
|
||||
// 地址,若tcp/udp的话则为 ip:port/host:port的形式, 若是uds则是文件路径 ,
|
||||
// 在server就是监听地址,在client就是拨号地址
|
||||
AddrStr() string
|
||||
SetAddrStr(string)
|
||||
Network() string
|
||||
|
||||
CantRoute() bool //for inServer
|
||||
GetTag() string
|
||||
|
||||
IsUDP() bool
|
||||
|
||||
IsDial() bool //true则为 Dial 端,false 则为 Listen 端
|
||||
GetListenConf() *ListenConf
|
||||
GetDialConf() *DialConf
|
||||
|
||||
/////////////////// TLS层 ///////////////////
|
||||
|
||||
SetUseTLS()
|
||||
IsUseTLS() bool
|
||||
|
||||
AdvancedLayer() string //如果使用了ws或者grpc,这个要返回 ws 或 grpc
|
||||
|
||||
GetTLS_Server() *tlsLayer.Server
|
||||
GetTLS_Client() *tlsLayer.Client
|
||||
|
||||
setTLS_Server(*tlsLayer.Server)
|
||||
setTLS_Client(*tlsLayer.Client)
|
||||
|
||||
setCantRoute(bool)
|
||||
setTag(string)
|
||||
setAdvancedLayer(string)
|
||||
/////////////////// 高级层 ///////////////////
|
||||
|
||||
setIsDial(bool)
|
||||
setListenConf(*ListenConf) //for inServer
|
||||
setDialConf(*DialConf) //for outClient
|
||||
AdvancedLayer() string //如果使用了ws或者grpc,这个要返回 ws 或 grpc
|
||||
|
||||
GetWS_Client() *ws.Client //for outClient
|
||||
|
||||
initWS_client() //for outClient
|
||||
|
||||
setCantRoute(bool)
|
||||
setTag(string)
|
||||
setAdvancedLayer(string)
|
||||
setNetwork(string)
|
||||
|
||||
setIsDial(bool)
|
||||
setListenConf(*ListenConf) //for inServer
|
||||
setDialConf(*DialConf) //for outClient
|
||||
|
||||
}
|
||||
|
||||
//use dc.Host, dc.Insecure, dc.Utls
|
||||
@@ -157,6 +163,7 @@ type ProxyCommonStruct struct {
|
||||
Addr string
|
||||
TLS bool
|
||||
Tag string //可用于路由, 见 netLayer.route.go
|
||||
network string
|
||||
|
||||
tls_s *tlsLayer.Server
|
||||
tls_c *tlsLayer.Client
|
||||
@@ -172,8 +179,8 @@ type ProxyCommonStruct struct {
|
||||
ws_c *ws.Client
|
||||
}
|
||||
|
||||
func (pcs *ProxyCommonStruct) IsUDP() bool {
|
||||
return false
|
||||
func (pcs *ProxyCommonStruct) Network() string {
|
||||
return pcs.network
|
||||
}
|
||||
|
||||
func (pcs *ProxyCommonStruct) MiddleName() string {
|
||||
@@ -198,6 +205,15 @@ func (pcs *ProxyCommonStruct) GetTag() string {
|
||||
func (pcs *ProxyCommonStruct) setTag(tag string) {
|
||||
pcs.Tag = tag
|
||||
}
|
||||
func (pcs *ProxyCommonStruct) setNetwork(net string) {
|
||||
if net == "" {
|
||||
pcs.network = "tcp"
|
||||
|
||||
} else {
|
||||
pcs.network = net
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (pcs *ProxyCommonStruct) setCantRoute(cr bool) {
|
||||
pcs.cantRoute = cr
|
||||
|
@@ -154,7 +154,8 @@ func (s *Server) Handshake(underlay net.Conn) (io.ReadWriter, *netLayer.Addr, er
|
||||
IP: theIP,
|
||||
Name: theName,
|
||||
Port: thePort,
|
||||
IsUDP: true,
|
||||
//IsUDP: true,
|
||||
Network: "udp",
|
||||
}
|
||||
|
||||
serverAtyp, serverAddr, _, err := netLayer.ParseStrToAddr(s.Addr)
|
||||
@@ -339,7 +340,7 @@ func (u *UDPConn) StartReadRequest(udpPutter netLayer.UDP_Putter) {
|
||||
IP: theIP,
|
||||
Name: theName,
|
||||
Port: thePort,
|
||||
IsUDP: true,
|
||||
Network: "udp",
|
||||
}
|
||||
|
||||
udpPutter.WriteUDPRequest(thisaddr.ToUDPAddr(), bs[newStart:n-newStart])
|
||||
|
@@ -41,7 +41,7 @@ type Client struct {
|
||||
type ClientCreator struct{}
|
||||
|
||||
func (_ ClientCreator) NewClientFromURL(u *url.URL) (proxy.Client, error) {
|
||||
return NewClient(u)
|
||||
return NewClientByURL(u)
|
||||
}
|
||||
|
||||
func (_ ClientCreator) NewClient(dc *proxy.DialConf) (proxy.Client, error) {
|
||||
@@ -70,7 +70,7 @@ func (_ ClientCreator) NewClient(dc *proxy.DialConf) (proxy.Client, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func NewClient(url *url.URL) (proxy.Client, error) {
|
||||
func NewClientByURL(url *url.URL) (proxy.Client, error) {
|
||||
addr := url.Host
|
||||
uuidStr := url.User.Username()
|
||||
id, err := proxy.NewV2rayUser(uuidStr)
|
||||
@@ -119,7 +119,7 @@ func (c *Client) Handshake(underlay net.Conn, target *netLayer.Addr) (io.ReadWri
|
||||
addr, atyp := target.AddressBytes()
|
||||
|
||||
cmd := CmdTCP
|
||||
if target.IsUDP {
|
||||
if target.Network == "udp" {
|
||||
if c.version == 1 && !c.is_CRUMFURS_established {
|
||||
|
||||
//log.Println("尝试拨号 Cmd_CRUMFURS 信道")
|
||||
@@ -175,7 +175,7 @@ func (c *Client) Handshake(underlay net.Conn, target *netLayer.Addr) (io.ReadWri
|
||||
Conn: underlay,
|
||||
uuid: *c.user,
|
||||
version: c.version,
|
||||
isUDP: target.IsUDP,
|
||||
isUDP: target.Network == "udp",
|
||||
}, err
|
||||
}
|
||||
|
||||
|
@@ -320,7 +320,7 @@ realPart:
|
||||
addr.Port = int(binary.BigEndian.Uint16(portbs))
|
||||
|
||||
if commandByte == CmdUDP {
|
||||
addr.IsUDP = true
|
||||
addr.Network = "udp"
|
||||
}
|
||||
|
||||
var ip_or_domain_bytesLength byte = 0
|
||||
@@ -388,7 +388,7 @@ realPart:
|
||||
optionalReader: io.MultiReader(readbuf, underlay),
|
||||
uuid: thisUUIDBytes,
|
||||
version: int(version),
|
||||
isUDP: addr.IsUDP,
|
||||
isUDP: addr.Network == "udp",
|
||||
isServerEnd: true,
|
||||
}, addr, nil
|
||||
|
||||
|
@@ -204,7 +204,7 @@ func testVLessUDP(version int, port string, t *testing.T) {
|
||||
targetStruct_forFakeUDPServer := &netLayer.Addr{
|
||||
Name: "127.0.0.1",
|
||||
Port: thePort,
|
||||
IsUDP: true,
|
||||
Network: "udp",
|
||||
}
|
||||
// 监听 Client End LocalServer
|
||||
listener, err := net.Listen("tcp", fakeServerEndLocalServer.AddrStr())
|
||||
@@ -232,7 +232,7 @@ func testVLessUDP(version int, port string, t *testing.T) {
|
||||
|
||||
remoteAddrStr := targetAddr.String()
|
||||
|
||||
if remoteAddrStr != targetStr_forFakeUDPServer || targetAddr.IsUDP == false {
|
||||
if remoteAddrStr != targetStr_forFakeUDPServer || targetAddr.Network != "udp" {
|
||||
t.Log("remoteAddrStr != targetStr_forFakeUDPServer || targetAddr.IsUDP == false ")
|
||||
t.Fail()
|
||||
return
|
||||
|
38
version.go
38
version.go
@@ -1,3 +1,39 @@
|
||||
/*Package main 读取配置文件,将其内容转化为 proxy.Client和 proxy.Server,然后进行代理转发.
|
||||
|
||||
命令行参数请使用 --help查看详情。
|
||||
|
||||
如果一个命令行参数无法在标准配置中进行配置,那么它就属于高级选项,或者不推荐的选项,或者正在开发中的功能.
|
||||
|
||||
Config Format 配置格式
|
||||
|
||||
一共有三种配置格式,极简模式,标准模式,兼容模式。
|
||||
|
||||
“极简模式”(即 verysimple mode),入口和出口仅有一个,而且都是使用共享链接的url格式来配置.
|
||||
|
||||
标准模式使用toml格式。
|
||||
|
||||
兼容模式可以兼容v2ray现有json格式。(暂未实现)。
|
||||
|
||||
极简模式的理念是,配置文件的字符尽量少,尽量短小精悍;
|
||||
|
||||
还有个命令行模式,就是直接把极简模式的url 放到命令行参数中,比如:
|
||||
|
||||
verysimple -L socks5://sfdfsaf -D direct://
|
||||
|
||||
|
||||
Structure 本项目结构
|
||||
|
||||
main -> proxy.Standard(配置文件) -> netLayer-> tlsLayer -> httpLayer -> ws -> proxy.
|
||||
|
||||
main中,读取配置文件,生成 Dail、Listen 和 RoutePolicy 对象后,开始监听;
|
||||
|
||||
用 netLayer操纵路由,用tlsLayer嗅探tls,用httpLayer操纵回落,可选经过ws, 然后都搞好后,传到proxy,然后就开始转发
|
||||
|
||||
Other - 其他
|
||||
|
||||
另外,本作暂时不考虑引入外界log包。依赖越少越好。
|
||||
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -10,6 +46,8 @@ import (
|
||||
var Version string //版本号由 Makefile 里的 BUILD_VERSION 指定
|
||||
|
||||
func printVersion() {
|
||||
const desc = "verysimple, a very simple implementation of V2Ray with some innovation"
|
||||
|
||||
fmt.Printf("===============================\nverysimple %v (%v), %v %v %v\n", Version, desc, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
fmt.Println("Support tls and websocket for all protocols.")
|
||||
if netLayer.HasEmbedGeoip() {
|
||||
|
@@ -21,6 +21,7 @@ tag = "my_vlesss1" # 可选, 但不可与其他tag重复
|
||||
protocol = "vlesss" # vless 的 尾缀s 表示 使用tls
|
||||
uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" # 这个只是一个示例uuid,请自己生成一个新的.
|
||||
host = "127.0.0.1"
|
||||
#network = "udp" #network 目前支持 udp,tcp 和unix;如果不给出则默认为 tcp
|
||||
|
||||
# ip = "127.0.0.1"
|
||||
|
||||
|
@@ -14,3 +14,4 @@ insecure = true
|
||||
utls = true
|
||||
advancedLayer = "ws"
|
||||
path = "/ohmygod_verysimple_is_very_simple" #为了防探测这里越长越随机越好
|
||||
network = "udp"
|
@@ -10,6 +10,7 @@ cert = "cert.pem"
|
||||
key = "cert.key"
|
||||
advancedLayer = "ws"
|
||||
path = "/ohmygod_verysimple_is_very_simple"
|
||||
network = "udp"
|
||||
|
||||
[[dial]]
|
||||
protocol = "direct"
|
||||
|
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/hahahrfool/v2ray_simple/utils"
|
||||
)
|
||||
|
||||
// 因为 gobwas/ws 不包装conn,在写入和读取二进制时需要使用 wsutil包的特殊函数才行,
|
||||
// 因为 gobwas/ws 不包装conn,在写入和读取二进制时需要使用 较为底层的函数才行,并未被提供标准的Read和Write
|
||||
// 因此我们包装一下,统一使用Read和Write函数 来读写 二进制数据。因为我们这里是代理,
|
||||
// 所以我们默认 抛弃 websocket的 数据帧 长度。
|
||||
// 如果以后考虑与 vless v1的 udp 相结合的话,则数据包长度 不能丢弃,需要使用另外的实现。
|
||||
@@ -44,6 +44,7 @@ func (c *Conn) Read(p []byte) (int, error) {
|
||||
return n, e
|
||||
}
|
||||
c.remainLenForLastFrame -= int64(n)
|
||||
// 这里之所以可以放心 减去 n,不怕减成负的,是因为 r的代码里 在读取一帧的数据时,用到了 io.LimitedReader, 一帧的读取长度的上限已被限定,直到 该帧完全被读完为止
|
||||
return n, nil
|
||||
}
|
||||
|
||||
|
@@ -22,10 +22,9 @@ func Handshake(path string, underlay net.Conn) (net.Conn, error) {
|
||||
// 我们的 httpLayer 的 过滤方法仍然是最安全的,可以杜绝 所有非法数据;
|
||||
// 而 ws.Upgrader.Upgrade 使用了 readLine 函数。如果客户提供一个非法的超长的一行的话,它就会陷入泥淖
|
||||
// 这个以后 可以先用 httpLayer的过滤方法,过滤掉后,再用 MultiReader组装回来,提供给 upgrader.Upgrade
|
||||
//目前设一个 ReadBufferSize即可, 看了,默认是 4096,已经够大
|
||||
// ReadBufferSize默认是 4096,已经够大
|
||||
|
||||
upgrader := &ws.Upgrader{
|
||||
//ReadBufferSize: 1,
|
||||
OnRequest: func(uri []byte) error {
|
||||
struri := string(uri)
|
||||
if struri != path {
|
||||
@@ -40,7 +39,6 @@ func Handshake(path string, underlay net.Conn) (net.Conn, error) {
|
||||
}
|
||||
|
||||
_, err := upgrader.Upgrade(underlay)
|
||||
//log.Println(hs, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -54,7 +52,6 @@ func Handshake(path string, underlay net.Conn) (net.Conn, error) {
|
||||
//不想客户端;服务端是不怕客户端在握手阶段传来任何多余数据的
|
||||
// 因为我们还没实现 0-rtt
|
||||
theConn.r.OnIntermediate = wsutil.ControlFrameHandler(underlay, ws.StateServerSide)
|
||||
//theConn.w.DisableFlush() //发现分片的话,会出问题,所以就先关了. 搞清楚分片的问题再说。
|
||||
|
||||
return theConn, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user