diff --git a/server/udpserver.go b/server/udpserver.go index 70de25b3c243b326dc50e237f1b5bb9927835c82..af7d3686cf53b925eb5d7d7e7bfee6d611fecf4e 100644 --- a/server/udpserver.go +++ b/server/udpserver.go @@ -55,14 +55,28 @@ func (s *UDPServer) Start() error { if s.cfg.HoppingEnabled { listeners = make([]net.Listener, s.cfg.PortCount) portHopRange := int(s.cfg.MaxHopPort - s.cfg.MinHopPort) + listenerCount := 0 for i := 0; i < int(s.cfg.PortCount); i++ { portOffset := r.Intn(portHopRange) addr := net.JoinHostPort(s.cfg.Obfs4ListenIP, fmt.Sprint(portOffset+int(s.cfg.MinHopPort))) - listeners[i], err = listenConfig.Listen(s.ctx, addr) + listener, err := listenConfig.Listen(s.ctx, addr) if err != nil { s.logger.Printf("Error binding to %s: %v", addr, err) + continue } + listeners[listenerCount] = listener + listenerCount++ + } + + if listenerCount < int(float64(s.cfg.PortCount)*0.9) { + // if too many of the required ports are already occupied, clients will suffer from + // bad throughput due to many dialing errors + return fmt.Errorf("too many port binding errors: %d/%d", len(listeners), int(s.cfg.PortCount)) + } else if listenerCount < int(s.cfg.PortCount) { + // adjust the listener slice size so that we avoid nil entries + listeners = listeners[:listenerCount] + s.debug.Printf("WARNING: listening on less ports then clients expect: %d / %d", len(listeners), int(s.cfg.PortCount)) } } else { listenAddr := net.JoinHostPort(s.cfg.Obfs4ListenIP, strconv.Itoa(s.cfg.Obfs4ListenPort))