TCP: iki farklı soket bir bağlantı noktasını paylaşabilir mi?


125

Bu çok basit bir soru olabilir ama kafamı karıştırıyor.

İki farklı bağlı soket bir bağlantı noktasını paylaşabilir mi? 100.000'den fazla eşzamanlı bağlantıyı idare edebilecek bir uygulama sunucusu yazıyorum ve bir sistemdeki mevcut bağlantı noktalarının sayısının 60k (16bit) civarında olduğunu biliyoruz. Bağlı bir soket yeni (ayrılmış) bir bağlantı noktasına atanır, bu nedenle birden fazla soket aynı bağlantı noktasını paylaşamıyorsa, eşzamanlı bağlantıların sayısı bağlantı noktalarının sayısıyla sınırlıdır. Yani soru.

Şimdiden yardım için teşekkürler!

Yanıtlar:


176

Bir sunucu soketi tek bir bağlantı noktasında dinler. Bu sunucuda kurulan tüm istemci bağlantıları, bağlantının sunucu tarafındaki aynı dinleme bağlantı noktasıyla ilişkilendirilir. Kurulan bir bağlantı, istemci tarafı ve sunucu tarafı IP / Bağlantı Noktası çiftlerinin birleşimiyle benzersiz bir şekilde tanımlanır. Aynı sunucuda birden çok bağlantı aynı paylaşabilir sunucu tarafı farklı ilişkili sürece IP / Liman çifti istemci tarafı IP / Liman çiftleri ve sunucu mevcut sistem kaynakları buna izin gibi birçok istemcileri gibi ele almak mümkün olacaktır için.

On istemci tarafında , yeni giden bağlantılar rastgele kullanmak için genel bir uygulamadır istemci tarafı size kısa bir miktarda bağlantıları çok yaparsanız mevcut limanların dolmak üzere mümkündür ki bu durumda portu.


2
Cevap için teşekkürler, Remy! Cevabınız merak ettiğim her şey. ;)
KJ

3
@Remy Connections, yanılmıyorsam yalnızca kaynak / hedef bağlantı noktası / IP ile değil, aynı zamanda bir protokol (TCP, UDP vb.) Tarafından da ayırt edilir.
Ondrej Peterka

2
@OndraPeterka: evet, ancak tüm platformlar bunu kısıtlamaz. Örneğin, Windows mutlu bir şekilde ayrı IPv4 ve IPv6 sunucu soketlerinin aynı yerel IP'yi dinlemesine izin verir: Çemberler arasında atlamadan bağlantı noktası, ancak * Nix sistemleri (Linux ve Android dahil) bunu yapmaz.
Remy Lebeau

7
@ user2268997: Birden çok sunucuya bağlanmak için tek bir soket kullanamazsınız. Her bağlantı için ayrı bir soket oluşturmalısınız.
Remy Lebeau

3
@FernandoGonzalezSanchez: Tek bir istemci, farklı uzak IP / Port çiftlerine bağlı oldukları sürece aynı yerel IP / Port çiftine bağlı birden fazla TCP soketine sahip olabilir. Bu, Windows'a özgü değildir, bu, genel olarak TCP'nin çalışma biçiminin bir parçasıdır.
Remy Lebeau

183

Bağlantı Noktalarında TCP / HTTP Dinleme: Birçok Kullanıcı Aynı Bağlantı Noktasını Nasıl Paylaşabilir?

Peki, bir sunucu TCP bağlantı noktasında gelen bağlantıları dinlediğinde ne olur? Örneğin, 80 numaralı bağlantı noktasında bir web sunucunuz olduğunu varsayalım. Bilgisayarınızın genel IP adresi 24.14.181.229 ve size bağlanmaya çalışan kişinin 10.1.2.3 IP adresine sahip olduğunu varsayalım. Bu kişi 24.14.181.229:80'e bir TCP soketi açarak size bağlanabilir. Yeterince basit.

Sezgisel olarak (ve yanlış bir şekilde), çoğu insan bunun şöyle bir şeye benzediğini varsayar:

    Local Computer    | Remote Computer
    --------------------------------
    <local_ip>:80     | <foreign_ip>:80

    ^^ not actually what happens, but this is the conceptual model a lot of people have in mind.

Bu sezgiseldir, çünkü istemcinin bakış açısından bir IP adresine sahiptir ve IP: PORT'daki bir sunucuya bağlanır. Müşteri 80 numaralı bağlantı noktasına bağlandığına göre, bağlantı noktası da 80 olmalıdır? Bu düşünmek için mantıklı bir şey, ama aslında olan şey değil. Bu doğru olsaydı, yabancı IP adresi başına yalnızca bir kullanıcıya hizmet verebilirdik. Uzak bir bilgisayar bağlandığında, 80 numaralı bağlantı noktasını 80 numaralı bağlantı noktasına bağlar ve başka hiç kimse bağlanamaz.

Üç şey anlaşılmalıdır:

1.) Bir sunucuda, bir işlem bir bağlantı noktasında dinliyor . Bir bağlantı kurduğunda, onu başka bir konuya aktarır. İletişim asla dinleme bağlantı noktasını zorlamaz.

2.) Bağlantılar, aşağıdaki 5-tuple ile işletim sistemi tarafından benzersiz şekilde tanımlanır: (yerel-IP, yerel-bağlantı noktası, uzak-IP, uzak-bağlantı noktası, protokol). Demetteki herhangi bir öğe farklıysa, bu tamamen bağımsız bir bağlantıdır.

3.) Bir istemci bir sunucuya bağlandığında, rastgele, kullanılmamış bir yüksek sıralı kaynak bağlantı noktası seçer . Bu şekilde, tek bir istemcinin aynı hedef bağlantı noktası için sunucuya en çok ~ 64k bağlantısı olabilir.

Yani, bir istemci bir sunucuya bağlandığında gerçekten yaratılan şey budur:

    Local Computer   | Remote Computer           | Role
    -----------------------------------------------------------
    0.0.0.0:80       | <none>                    | LISTENING
    127.0.0.1:80     | 10.1.2.3:<random_port>    | ESTABLISHED

Gerçekte Ne Olduğuna Bakmak

Öncelikle, bu bilgisayarda neler olduğunu görmek için netstat kullanalım. 80 yerine 500 numaralı bağlantı noktasını kullanacağız (çünkü ortak bir bağlantı noktası olduğu için 80 numaralı bağlantı noktasında bir sürü şey oluyor, ancak işlevsel olarak bir fark yaratmıyor).

    netstat -atnp | grep -i ":500 "

Beklendiği gibi çıktı boş. Şimdi bir web sunucusu başlatalım:

    sudo python3 -m http.server 500

Şimdi, işte netstat'ı tekrar çalıştırmanın çıktısı:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      - 

Yani şimdi 500 numaralı bağlantı noktasında aktif olarak dinleyen bir işlem var (Durum: LISTEN). Yerel adres, "tüm ip adreslerini dinleme" için kod olan 0.0.0.0'dır. Yapılması kolay bir hata, yalnızca mevcut bilgisayardan gelen bağlantıları kabul edecek olan 127.0.0.1 bağlantı noktasını dinlemektir. Yani bu bir bağlantı değil, bu sadece bir işlemin IP bağlantı noktasına bağlanması () istendiği ve bu işlemin bu bağlantı noktasına yapılan tüm bağlantıların işlenmesinden sorumlu olduğu anlamına gelir. Bu, bir bağlantı noktasını dinleyen bilgisayar başına yalnızca bir işlem olabileceğine dair sınırlamaya işaret eder (çoğullamayı kullanarak bunu aşmanın yolları vardır, ancak bu çok daha karmaşık bir konudur). Bir web sunucusu 80 numaralı bağlantı noktasını dinliyorsa, bu bağlantı noktasını diğer web sunucuları ile paylaşamaz.

Şimdi, makinemize bir kullanıcı bağlayalım:

    quicknet -m tcp -t localhost:500 -p Test payload.

Bu basit bir betiktir ( https://github.com/grokit/quickweb ), bir TCP soketi açar, yükü gönderir (bu durumda "Test yükü."), Birkaç saniye bekler ve bağlantıyı keser. Bu olurken netstat'ı tekrar yapmak aşağıdakileri görüntüler:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:54240      ESTABLISHED -

Başka bir istemciye bağlanır ve tekrar netstat yaparsanız, aşağıdakileri göreceksiniz:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:26813      ESTABLISHED -

... yani, istemci bağlantı için başka bir rastgele bağlantı noktası kullandı. Bu yüzden IP adresleri arasında asla bir karışıklık olmaz.


12
Bu, SO'da gördüğüm en iyi cevap.
Jobs

1
@ N0thing "Bu şekilde, tek bir istemcinin aynı hedef bağlantı noktası için sunucuya en çok ~ 64k bağlantısı olabilir." Dolayısıyla pratikte, bir istemci aynı sunucuya ve bağlantı noktasına eşzamanlı olarak iki veya birden çok kez bağlanmazsa, bir istemcinin ~ 64K'dan daha fazla bağlantısı olabilir. Bu doğru mu. Evet ise, bu, istemci tarafındaki tek bir bağlantı noktasından birçok farklı sunucu işlemine bağlanabileceği anlamına gelir (yuva bağlantısı farklıdır). Yani birden fazla istemci soketi istemci makinede aynı bağlantı noktasında bulunabilir mi? Lütfen "Remey Lebeau" cevabına ilişkin yorumumu okuyun. Teşekkürler: D
Prem KTiw

6
@premktiw: Evet, farklı sunucu IP / Bağlantı noktası çiftlerine bağlanmışlarsa, birden çok istemci soketi aynı anda aynı yerel IP / bağlantı noktası çiftine bağlanabilir, böylece yerel + uzak çiftlerin demetleri benzersiz olur. Ve evet, bir istemcinin toplamda 64K'dan fazla eşzamanlı bağlantıya sahip olması mümkündür. Tek bir bağlantı noktasından, sunucu IP / bağlantı noktası çiftleri benzersiz olduğu sürece potansiyel olarak sonsuz sayıda sunucuya (mevcut işletim sistemi kaynakları, mevcut yönlendirici bağlantı noktaları vb. İle sınırlandırılır) bağlanabilir.
Remy Lebeau

1
@RemyLebeau Memnun. Çok çok teşekkür ederim: D
Prem KTiw

1
@bibstha Gelen tüm bağlantılar reddedildiğinde, güvenlik duvarı rastgele bağlantı noktalarıyla nasıl başa çıkıyor?
PatrykG

35

Bağlı bir soket yeni (özel) bir bağlantı noktasına atanır

Bu ortak bir sezgi, ama yanlış. Bağlı bir soket, yeni / özel bir bağlantı noktasına atanmamış. TCP yığınının karşılaması gereken tek gerçek kısıtlama (local_address, local_port, remote_address, remote_port) demetinin her soket bağlantısı için benzersiz olması gerektiğidir. Bu nedenle, bağlantı noktasındaki soketlerin her biri farklı bir uzak konuma bağlı olduğu sürece, sunucu aynı yerel bağlantı noktasını kullanan birçok TCP soketine sahip olabilir.

Şu adresteki "Soket Çifti" paragrafına bakın: http://books.google.com/books?id=ptSC4LpwGA0C&lpg=PA52&dq=socket%20pair%20tuple&pg=PA52#v=onepage&q=socket%20pair%20tuple&f=false


1
Mükemmel cevap için teşekkürler Jeremy!
KJ

6
Söyledikleriniz tamamen sunucu tarafı için doğrudur. Bununla birlikte, BSD Yuva API'sinin yapısı, giden istemci tarafı bağlantı noktalarının uygulamada benzersiz olması gerektiği anlamına gelir, çünkü işlem, dolaylı olarak bile bind()işlemden önce gelir connect().
Marquis of Lorne

1
@EJP Merhaba, daha bind()önce sadece sunucu tarafında kullanıldığını düşünmüştüm. accept()?Yani istemci tarafı da belirli bir portu bağlayacak mı?
GMsoF

5
@GMsoF: bind()daha önce istemci tarafında kullanılabilir connect().
Remy Lebeau

10

Teorik olarak evet. Pratik yapmayın. Çoğu çekirdek (linux dahil), bind()önceden tahsis edilmiş bir bağlantı noktasına bir saniye izin vermez . Buna izin vermek gerçekten büyük bir yama değildi.

Kavramsal olarak, soket ve bağlantı noktası arasında ayrım yapmalıyız . Soketler çift yönlü iletişim uç noktalarıdır, yani bayt gönderip alabileceğimiz "şeyler". Kavramsal bir şeydir, "soket" adlı bir paket başlığında böyle bir alan yoktur.

Bağlantı noktası, bir soketi tanımlayabilen bir tanımlayıcıdır. TCP durumunda, bir bağlantı noktası 16 bitlik bir tamsayıdır, ancak başka protokoller de vardır (örneğin, unix soketlerinde, bir "bağlantı noktası" aslında bir dizedir).

Ana sorun şudur: Gelen bir paket gelirse, çekirdek soketini hedef port numarasıyla belirleyebilir. En yaygın yol budur, ancak tek olasılık bu değildir:

  • Soketler, gelen paketlerin hedef IP'si ile tanımlanabilir. Örneğin, aynı anda iki IP kullanan bir sunucumuz varsa durum budur. Daha sonra, örneğin, aynı bağlantı noktalarında ancak farklı IP'lerde farklı web sunucuları çalıştırabiliriz.
  • Soketler, kaynak portu ve ip ile de tanımlanabilir. Birçok yük dengeleme yapılandırmasında durum budur.

Bir uygulama sunucusu üzerinde çalıştığınız için, bunu yapabilecektir.


2
Bir saniye yapmayı sormadı bind().
Lorne Markisi

1
@ user207421 Hiç dinleme soketlerinin kurulmadığı bir işletim sistemi gördünüz mü bind()? Hayal edebiliyorum, evet bu oldukça mümkün, ama gerçek şu ki, hem WinSock hem de Posix API bunun için bind()çağrıyı kullanıyor , hatta parametrelendirmeleri neredeyse aynı. Bir API bu çağrıya sahip olmasa bile, bir şekilde onu söylemeniz gerekir, gelen baytları nereden okumak istiyorsunuz .
peterh

1
@ user207421 Elbette 100k veya daha fazla TCP bağlantısı aynı portlarla işlenebilir, listen()/ accept()API çağrıları soketleri, gelen portlarına göre çekirdeğin farklılaştıracağı şekilde oluşturabilir. OP sorusu, onun esasen sorduğu şekilde yorumlanabilir. Bence oldukça gerçekçi, ama sorusunun tam anlamıyla anlamı bu değil.
peterh

1

Hayır. Aynı bağlantı noktasını belirli bir anda paylaşmak mümkün değildir. Ancak uygulamanızı, port erişimini farklı anlarda yapacak şekilde yapabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.