diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index ff51e84230bfcd2b36149e4651bea2977d44a1b5..a5187bf766cc9ea863de986c81ee80b1cc0188b4 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -242,6 +242,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
 		utils.JSpathFlag,
 		utils.ListenPortFlag,
 		utils.MaxPeersFlag,
+		utils.MaxPendingPeersFlag,
 		utils.EtherbaseFlag,
 		utils.MinerThreadsFlag,
 		utils.MiningEnabledFlag,
diff --git a/cmd/mist/main.go b/cmd/mist/main.go
index 1030d6ada1d27d92c0f44323c6fd5ee4e3eb80ab..9d92cc1754f938907fbfce734b4c067edf2d1fd0 100644
--- a/cmd/mist/main.go
+++ b/cmd/mist/main.go
@@ -75,6 +75,7 @@ func init() {
 		utils.LogFileFlag,
 		utils.LogLevelFlag,
 		utils.MaxPeersFlag,
+		utils.MaxPendingPeersFlag,
 		utils.MinerThreadsFlag,
 		utils.NATFlag,
 		utils.NodeKeyFileFlag,
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 460068d91261675a877a6c3a500db4c31e2c0fde..a2ff05440fcdfcd8c6c0a887d0804b52f1dc11a7 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -197,6 +197,11 @@ var (
 		Usage: "Maximum number of network peers (network disabled if set to 0)",
 		Value: 16,
 	}
+	MaxPendingPeersFlag = cli.IntFlag{
+		Name:  "maxpendpeers",
+		Usage: "Maximum number of pending connection attempts (defaults used if set to 0)",
+		Value: 0,
+	}
 	ListenPortFlag = cli.IntFlag{
 		Name:  "port",
 		Usage: "Network listening port",
@@ -292,6 +297,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
 		AccountManager:     GetAccountManager(ctx),
 		VmDebug:            ctx.GlobalBool(VMDebugFlag.Name),
 		MaxPeers:           ctx.GlobalInt(MaxPeersFlag.Name),
+		MaxPendingPeers:    ctx.GlobalInt(MaxPendingPeersFlag.Name),
 		Port:               ctx.GlobalString(ListenPortFlag.Name),
 		NAT:                GetNAT(ctx),
 		NatSpec:            ctx.GlobalBool(NatspecEnabledFlag.Name),
diff --git a/eth/backend.go b/eth/backend.go
index 791336d75d50ccef78dbe8058b17b18afd92dada..0f23cde2fda42045011e4bb069cd9047d6e8f86c 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -60,8 +60,9 @@ type Config struct {
 	VmDebug  bool
 	NatSpec  bool
 
-	MaxPeers int
-	Port     string
+	MaxPeers        int
+	MaxPendingPeers int
+	Port            string
 
 	// Space-separated list of discovery node URLs
 	BootNodes string
@@ -280,16 +281,17 @@ func New(config *Config) (*Ethereum, error) {
 		protocols = append(protocols, eth.whisper.Protocol())
 	}
 	eth.net = &p2p.Server{
-		PrivateKey:     netprv,
-		Name:           config.Name,
-		MaxPeers:       config.MaxPeers,
-		Protocols:      protocols,
-		NAT:            config.NAT,
-		NoDial:         !config.Dial,
-		BootstrapNodes: config.parseBootNodes(),
-		StaticNodes:    config.parseNodes(staticNodes),
-		TrustedNodes:   config.parseNodes(trustedNodes),
-		NodeDatabase:   nodeDb,
+		PrivateKey:      netprv,
+		Name:            config.Name,
+		MaxPeers:        config.MaxPeers,
+		MaxPendingPeers: config.MaxPendingPeers,
+		Protocols:       protocols,
+		NAT:             config.NAT,
+		NoDial:          !config.Dial,
+		BootstrapNodes:  config.parseBootNodes(),
+		StaticNodes:     config.parseNodes(staticNodes),
+		TrustedNodes:    config.parseNodes(trustedNodes),
+		NodeDatabase:    nodeDb,
 	}
 	if len(config.Port) > 0 {
 		eth.net.ListenAddr = ":" + config.Port
diff --git a/p2p/server.go b/p2p/server.go
index 5e0c917fc4c9e4ca6ab24a88ea8ea596b0903e30..77f66f1673e860a8b5661fe7db05af6089da95e8 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -22,10 +22,11 @@ const (
 	refreshPeersInterval    = 30 * time.Second
 	staticPeerCheckInterval = 15 * time.Second
 
-	// This is the maximum number of inbound connection
-	// that are allowed to linger between 'accepted' and
-	// 'added as peer'.
-	maxAcceptConns = 50
+	// Maximum number of concurrently handshaking inbound connections.
+	maxAcceptConns = 10
+
+	// Maximum number of concurrently dialing outbound connections.
+	maxDialingConns = 10
 
 	// total timeout for encryption handshake and protocol
 	// handshake in both directions.
@@ -52,6 +53,11 @@ type Server struct {
 	// connected. It must be greater than zero.
 	MaxPeers int
 
+	// MaxPendingPeers is the maximum number of peers that can be pending in the
+	// handshake phase, counted separately for inbound and outbound connections.
+	// Zero defaults to preset values.
+	MaxPendingPeers int
+
 	// Name sets the node name of this server.
 	// Use common.MakeName to create a name that follows existing conventions.
 	Name string
@@ -331,8 +337,12 @@ func (srv *Server) listenLoop() {
 	// This channel acts as a semaphore limiting
 	// active inbound connections that are lingering pre-handshake.
 	// If all slots are taken, no further connections are accepted.
-	slots := make(chan struct{}, maxAcceptConns)
-	for i := 0; i < maxAcceptConns; i++ {
+	tokens := maxAcceptConns
+	if srv.MaxPendingPeers > 0 {
+		tokens = srv.MaxPendingPeers
+	}
+	slots := make(chan struct{}, tokens)
+	for i := 0; i < tokens; i++ {
 		slots <- struct{}{}
 	}
 
@@ -401,7 +411,15 @@ func (srv *Server) dialLoop() {
 	defer srv.loopWG.Done()
 	defer refresh.Stop()
 
-	// TODO: maybe limit number of active dials
+	// Limit the number of concurrent dials
+	tokens := maxAcceptConns
+	if srv.MaxPendingPeers > 0 {
+		tokens = srv.MaxPendingPeers
+	}
+	slots := make(chan struct{}, tokens)
+	for i := 0; i < tokens; i++ {
+		slots <- struct{}{}
+	}
 	dial := func(dest *discover.Node) {
 		// Don't dial nodes that would fail the checks in addPeer.
 		// This is important because the connection handshake is a lot
@@ -413,11 +431,14 @@ func (srv *Server) dialLoop() {
 		if !ok || dialing[dest.ID] {
 			return
 		}
+		// Request a dial slot to prevent CPU exhaustion
+		<-slots
 
 		dialing[dest.ID] = true
 		srv.peerWG.Add(1)
 		go func() {
 			srv.dialNode(dest)
+			slots <- struct{}{}
 			dialed <- dest
 		}()
 	}
diff --git a/p2p/server_test.go b/p2p/server_test.go
index 3f9db343ccaa0d47269c2f4336686c5e8132d484..a5e56fa186ea5319597b7e5f5801b3cee2f3aa21 100644
--- a/p2p/server_test.go
+++ b/p2p/server_test.go
@@ -369,6 +369,136 @@ func TestServerTrustedPeers(t *testing.T) {
 	}
 }
 
+// Tests that a failed dial will temporarily throttle a peer.
+func TestServerMaxPendingDials(t *testing.T) {
+	defer testlog(t).detach()
+
+	// Start a simple test server
+	server := &Server{
+		ListenAddr:      "127.0.0.1:0",
+		PrivateKey:      newkey(),
+		MaxPeers:        10,
+		MaxPendingPeers: 1,
+	}
+	if err := server.Start(); err != nil {
+		t.Fatal("failed to start test server: %v", err)
+	}
+	defer server.Stop()
+
+	// Simulate two separate remote peers
+	peers := make(chan *discover.Node, 2)
+	conns := make(chan net.Conn, 2)
+	for i := 0; i < 2; i++ {
+		listener, err := net.Listen("tcp", "127.0.0.1:0")
+		if err != nil {
+			t.Fatalf("listener %d: failed to setup: %v", i, err)
+		}
+		defer listener.Close()
+
+		addr := listener.Addr().(*net.TCPAddr)
+		peers <- &discover.Node{
+			ID:  discover.PubkeyID(&newkey().PublicKey),
+			IP:  addr.IP,
+			TCP: uint16(addr.Port),
+		}
+		go func() {
+			conn, err := listener.Accept()
+			if err == nil {
+				conns <- conn
+			}
+		}()
+	}
+	// Request a dial for both peers
+	go func() {
+		for i := 0; i < 2; i++ {
+			server.staticDial <- <-peers // hack piggybacking the static implementation
+		}
+	}()
+
+	// Make sure only one outbound connection goes through
+	var conn net.Conn
+
+	select {
+	case conn = <-conns:
+	case <-time.After(100 * time.Millisecond):
+		t.Fatalf("first dial timeout")
+	}
+	select {
+	case conn = <-conns:
+		t.Fatalf("second dial completed prematurely")
+	case <-time.After(100 * time.Millisecond):
+	}
+	// Finish the first dial, check the second
+	conn.Close()
+	select {
+	case conn = <-conns:
+		conn.Close()
+
+	case <-time.After(100 * time.Millisecond):
+		t.Fatalf("second dial timeout")
+	}
+}
+
+func TestServerMaxPendingAccepts(t *testing.T) {
+	defer testlog(t).detach()
+
+	// Start a test server and a peer sink for synchronization
+	started := make(chan *Peer)
+	server := &Server{
+		ListenAddr:      "127.0.0.1:0",
+		PrivateKey:      newkey(),
+		MaxPeers:        10,
+		MaxPendingPeers: 1,
+		NoDial:          true,
+		newPeerHook:     func(p *Peer) { started <- p },
+	}
+	if err := server.Start(); err != nil {
+		t.Fatal("failed to start test server: %v", err)
+	}
+	defer server.Stop()
+
+	// Try and connect to the server on multiple threads concurrently
+	conns := make([]net.Conn, 2)
+	for i := 0; i < 2; i++ {
+		dialer := &net.Dialer{Deadline: time.Now().Add(3 * time.Second)}
+
+		conn, err := dialer.Dial("tcp", server.ListenAddr)
+		if err != nil {
+			t.Fatalf("failed to dial server: %v", err)
+		}
+		conns[i] = conn
+	}
+	// Check that a handshake on the second doesn't pass
+	go func() {
+		key := newkey()
+		shake := &protoHandshake{Version: baseProtocolVersion, ID: discover.PubkeyID(&key.PublicKey)}
+		if _, err := setupConn(conns[1], key, shake, server.Self(), false, server.trustedNodes); err != nil {
+			t.Fatalf("failed to run handshake: %v", err)
+		}
+	}()
+	select {
+	case <-started:
+		t.Fatalf("handshake on second connection accepted")
+
+	case <-time.After(time.Second):
+	}
+	// Shake on first, check that both go through
+	go func() {
+		key := newkey()
+		shake := &protoHandshake{Version: baseProtocolVersion, ID: discover.PubkeyID(&key.PublicKey)}
+		if _, err := setupConn(conns[0], key, shake, server.Self(), false, server.trustedNodes); err != nil {
+			t.Fatalf("failed to run handshake: %v", err)
+		}
+	}()
+	for i := 0; i < 2; i++ {
+		select {
+		case <-started:
+		case <-time.After(time.Second):
+			t.Fatalf("peer %d: handshake timeout", i)
+		}
+	}
+}
+
 func newkey() *ecdsa.PrivateKey {
 	key, err := crypto.GenerateKey()
 	if err != nil {