Add bandwidth limiter

This commit is contained in:
Qian Wang 2018-10-28 22:51:00 +00:00
parent f476650953
commit 6a6b293164
4 changed files with 23 additions and 30 deletions

View File

@ -140,17 +140,11 @@ func main() {
log.Fatal("TicketTimeHint cannot be empty or 0") log.Fatal("TicketTimeHint cannot be empty or 0")
} }
initRemoteConn, err := makeRemoteConn(sta)
if err != nil {
log.Fatalf("Failed to establish connection to remote: %v\n", err)
}
obfs := util.MakeObfs(sta.SID) obfs := util.MakeObfs(sta.SID)
deobfs := util.MakeDeobfs(sta.SID) deobfs := util.MakeDeobfs(sta.SID)
// TODO: where to put obfs deobfs and rtd? sesh := mux.MakeSession(0, 1e9, 1e9, obfs, deobfs, util.ReadTillDrain)
sesh := mux.MakeSession(0, initRemoteConn, obfs, deobfs, util.ReadTillDrain)
for i := 0; i < sta.NumConn-1; i++ { for i := 0; i < sta.NumConn; i++ {
go func() { go func() {
conn, err := makeRemoteConn(sta) conn, err := makeRemoteConn(sta)
if err != nil { if err != nil {

View File

@ -102,12 +102,11 @@ func dispatchConnection(conn net.Conn, sta *server.State) {
var arrSID [32]byte var arrSID [32]byte
copy(arrSID[:], SID) copy(arrSID[:], SID)
var sesh *mux.Session var sesh *mux.Session
if sesh = sta.GetSession(arrSID); sesh != nil { if sesh = sta.GetSession(arrSID); sesh == nil {
sesh.AddConnection(conn) sesh = mux.MakeSession(0, 1e9, 1e9, util.MakeObfs(SID), util.MakeDeobfs(SID), util.ReadTillDrain)
} else {
sesh = mux.MakeSession(0, conn, util.MakeObfs(SID), util.MakeDeobfs(SID), util.ReadTillDrain)
sta.PutSession(arrSID, sesh) sta.PutSession(arrSID, sesh)
} }
sesh.AddConnection(conn)
go func() { go func() {
for { for {
newStream, err := sesh.AcceptStream() newStream, err := sesh.AcceptStream()

View File

@ -10,8 +10,6 @@ import (
const ( const (
// Copied from smux // Copied from smux
acceptBacklog = 1024 acceptBacklog = 1024
closeBacklog = 512
) )
var ErrBrokenSession = errors.New("broken session") var ErrBrokenSession = errors.New("broken session")
@ -45,7 +43,7 @@ type Session struct {
} }
// 1 conn is needed to make a session // 1 conn is needed to make a session
func MakeSession(id int, conn net.Conn, obfs func(*Frame) []byte, deobfs func([]byte) *Frame, obfsedReader func(net.Conn, []byte) (int, error)) *Session { func MakeSession(id int, uprate, downrate float64, obfs func(*Frame) []byte, deobfs func([]byte) *Frame, obfsedReader func(net.Conn, []byte) (int, error)) *Session {
sesh := &Session{ sesh := &Session{
id: id, id: id,
obfs: obfs, obfs: obfs,
@ -56,7 +54,7 @@ func MakeSession(id int, conn net.Conn, obfs func(*Frame) []byte, deobfs func([]
acceptCh: make(chan *Stream, acceptBacklog), acceptCh: make(chan *Stream, acceptBacklog),
die: make(chan struct{}), die: make(chan struct{}),
} }
sesh.sb = makeSwitchboard(conn, sesh) sesh.sb = makeSwitchboard(sesh, uprate, downrate)
return sesh return sesh
} }

View File

@ -1,22 +1,22 @@
package multiplex package multiplex
import ( import (
"errors"
"log" "log"
"net" "net"
"sync" "sync"
"sync/atomic" "sync/atomic"
)
const ( "github.com/juju/ratelimit"
sentNotifyBacklog = 1024
dispatchBacklog = 10240
newConnBacklog = 8
) )
// switchboard is responsible for keeping the reference of TLS connections between client and server // switchboard is responsible for keeping the reference of TLS connections between client and server
type switchboard struct { type switchboard struct {
session *Session session *Session
wtb *ratelimit.Bucket
rtb *ratelimit.Bucket
optimum atomic.Value optimum atomic.Value
cesM sync.RWMutex cesM sync.RWMutex
ces []*connEnclave ces []*connEnclave
@ -33,24 +33,25 @@ type connEnclave struct {
} }
// It takes at least 1 conn to start a switchboard // It takes at least 1 conn to start a switchboard
func makeSwitchboard(conn net.Conn, sesh *Session) *switchboard { // TODO: does it really?
func makeSwitchboard(sesh *Session, uprate, downrate float64) *switchboard {
sb := &switchboard{ sb := &switchboard{
session: sesh, session: sesh,
wtb: ratelimit.NewBucketWithRate(uprate, int64(uprate)),
rtb: ratelimit.NewBucketWithRate(downrate, int64(downrate)),
ces: []*connEnclave{}, ces: []*connEnclave{},
} }
ce := &connEnclave{
sb: sb,
remoteConn: conn,
sendQueue: 0,
}
sb.ces = append(sb.ces, ce)
go sb.deplex(ce)
return sb return sb
} }
var errNilOptimum error = errors.New("The optimal connection is nil")
func (sb *switchboard) send(data []byte) (int, error) { func (sb *switchboard) send(data []byte) (int, error) {
ce := sb.optimum.Load().(*connEnclave) ce := sb.optimum.Load().(*connEnclave)
if ce == nil {
return 0, errNilOptimum
}
sb.wtb.Wait(int64(len(data)))
atomic.AddUint32(&ce.sendQueue, uint32(len(data))) atomic.AddUint32(&ce.sendQueue, uint32(len(data)))
go sb.updateOptimum() go sb.updateOptimum()
n, err := ce.remoteConn.Write(data) n, err := ce.remoteConn.Write(data)
@ -118,6 +119,7 @@ func (sb *switchboard) deplex(ce *connEnclave) {
buf := make([]byte, 20480) buf := make([]byte, 20480)
for { for {
i, err := sb.session.obfsedReader(ce.remoteConn, buf) i, err := sb.session.obfsedReader(ce.remoteConn, buf)
sb.rtb.Wait(int64(i))
if err != nil { if err != nil {
log.Println(err) log.Println(err)
go ce.remoteConn.Close() go ce.remoteConn.Close()