9-bo‘lim

Portlar va soketlar

Port raqamlari diapazonlari, mashhur portlar, ulanishni aniqlaydigan to'rtlik, soket API ning bosqichlari va "port band" xatosi.

🕑 8 daqiqa o‘qish 📄 769 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Port raqamlari diapazoni
  2. Ulanishni nima aniqlaydi?
  3. Soket API bosqichlari
  4. "Address already in use"
  5. Qaysi manzilga bog'lanish kerak?
  6. Xulosa

Port - bu transport qatlamidagi eshik raqami. IP manzil binoni ko'rsatadi, port esa o'sha binodagi xonani.

Port raqamlari diapazoni #

Port 16 bitli son, ya'ni 0 dan 65535 gacha. U uch guruhga bo'lingan:

DiapazonNomiKim ishlatadi
0-1023Mashhur (well-known)Standart xizmatlar
1024-49151Ro'yxatga olinganMa'lum dasturlar
49152-65535Vaqtinchalik (ephemeral)Mijozlarga beriladi

Birinchi guruhni egallash uchun ko'p tizimlarda administrator huquqi kerak. Aynan shuning uchun veb serverlar 80 va 443 portda ishlaydi, lekin ishlab chiqish paytida 8000 yoki 3000 ishlatiladi.

Python
import socket

xizmatlar = ["ftp", "ssh", "telnet", "smtp", "domain", "http",
             "pop3", "imap", "https"]

print(f"{'xizmat':<10} {'port':>5}  {'protokol'}")
print("-" * 30)
for x in xizmatlar:
    port = socket.getservbyname(x, "tcp")
    print(f"{x:<10} {port:>5}  tcp")

print()
# Teskari yo'nalish: port raqamidan nom
for p in [53, 80, 443, 3306, 5432]:
    try:
        nom = socket.getservbyport(p, "tcp")
    except OSError:
        nom = "(noma'lum)"
    print(f"port {p:<6} -> {nom}")
Natija
xizmat      port  protokol
------------------------------
ftp           21  tcp
ssh           22  tcp
telnet        23  tcp
smtp          25  tcp
domain        53  tcp
http          80  tcp
pop3         110  tcp
imap         143  tcp
https        443  tcp

port 53     -> domain
port 80     -> http
port 443    -> https
port 3306   -> mysql
port 5432   -> postgresql
Bu ro'yxat qayerdan olinadi?

getservbyname internetga chiqmaydi - u kompyuteringizdagi faylni o'qiydi:

  • Linux va macOS: /etc/services
  • Windows: C:\Windows\System32\drivers\etc\services

Shuning uchun mashhur portlar hamma joyda bir xil, lekin mysql yoki postgresql kabi qatorlar tizimga qarab bo'lmasligi mumkin. Kodda shu sababli try ishlatildi.

Eng muhim portlarni yodlab qo'yish foydali: 22 (SSH), 53 (DNS), 80 (HTTP), 443 (HTTPS).

Ulanishni nima aniqlaydi? #

Yuzlab odam bir vaqtda bitta saytga kiradi. Server esa faqat bitta portda tinglaydi. Javoblar qanday aralashib ketmaydi?

Javob: ulanish bitta port bilan emas, to'rtlik bilan aniqlanadi.

Har ulanish - noyob to'rtlik (mijoz IP, mijoz porti, server IP, server porti) (192.168.1.10, 51234, 93.184.216.34, 443) (192.168.1.10, 51235, 93.184.216.34, 443) (192.168.5.99, 51234, 93.184.216.34, 443) Uchalasi ham bir xil serverga, bir xil portga - lekin bu UCH XIL ulanish Faqat bitta element farq qilsa ham, to'rtlik boshqacha bo'ladi
Server bitta portda millionlab ulanishga xizmat qila oladi
Python
import socket

PORT = 9500

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("127.0.0.1", PORT))
server.listen(5)

# Uchta mijoz BIR XIL server portiga ulanadi
mijozlar = []
ulanishlar = []
for i in range(3):
    m = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    m.connect(("127.0.0.1", PORT))
    mijozlar.append(m)
    u, _ = server.accept()
    ulanishlar.append(u)

print("server porti hammada bir xil:")
portlar = set()
for i, m in enumerate(mijozlar, 1):
    mahalliy = m.getsockname()
    uzoq = m.getpeername()
    portlar.add(mahalliy[1])
    print(f"  {i}-ulanish: mijoz porti != {uzoq[1]} ->",
          mahalliy[1] != uzoq[1])

print()
print("uchta mijoz porti ham har xilmi:", len(portlar) == 3)
print("server esa bitta portda tinglaydi:", server.getsockname()[1] == PORT)

for s in mijozlar + ulanishlar:
    s.close()
server.close()
Natija
server porti hammada bir xil:
  1-ulanish: mijoz porti != 9500 -> True
  2-ulanish: mijoz porti != 9500 -> True
  3-ulanish: mijoz porti != 9500 -> True

uchta mijoz porti ham har xilmi: True
server esa bitta portda tinglaydi: True

Har mijozga operatsion tizim boshqa vaqtinchalik port berdi. Shuning uchun to'rtliklar har xil va javoblar aralashmaydi.

"Portlar tugab qoladi" degan chegara

Bitta mijoz bitta serverga 65 mingdan ortiq ulanish ocha olmaydi - vaqtinchalik portlar tugaydi.

Lekin server tomonda bunday chegara yo'q: server uchun har ulanish boshqa mijoz IP si va porti bilan farqlanadi.

Amalda serverni cheklaydigan narsa - ochiq fayl deskriptorlari soni va xotira, port emas.

Soket API bosqichlari #

Server va mijoz yo'li SERVER MIJOZ socket() bind() listen() accept() - bu yerda kutadi socket() connect() send() / recv() - ikkala tomonda ham close()
UDP da bind() dan keyin darhol sendto/recvfrom - listen va accept kerak emas

Har bosqichning aniq vazifasi bor:

ChaqiruvNima qiladi
socket()Soket obyektini yaratadi, hali hech qayerga ulanmagan
bind()Mahalliy manzil va portni egallaydi
listen()"Ulanishlarni qabul qilaman" deb belgilaydi
accept()Navbatdagi ulanishni oladi - yangi soket qaytaradi
connect()Uzoq manzilga ulanadi (TCP da qo'l berish shu yerda)
close()Yopadi
accept() yangi soket qaytaradi

Bu boshlovchilar uchun eng chalkash joy.

Tinglovchi soket ulanish uchun ishlatilmaydi - u faqat yangi ulanishlarni qabul qiladi va har biri uchun alohida soket yaratadi.

Misollarda buni ko'rgansiz:

Python
ulanish, kim = server.accept()

server tinglashda davom etadi, ulanish esa aniq bitta mijoz bilan gaplashish uchun. Ma'lumot ulanish orqali o'tadi, server orqali emas.

"Address already in use" #

Bitta manzil va port juftligini faqat bitta soket egallay oladi:

Python
import socket

PORT = 9501

birinchi = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
birinchi.bind(("127.0.0.1", PORT))
birinchi.listen(1)
print("birinchi server portni egalladi:", PORT)

# Xuddi shu portni yana egallashga urinamiz
ikkinchi = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    ikkinchi.bind(("127.0.0.1", PORT))
    print("ikkinchi ham egalladi - bu kutilmagan holat")
except OSError as x:
    print("ikkinchi egallay olmadi:", x.strerror)

# Lekin BOSHQA manzilda o'sha port bo'sh
uchinchi = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
uchinchi.bind(("::1", PORT))
print("IPv6 loopback'da o'sha port band emas: ha")

ikkinchi.close()
uchinchi.close()
birinchi.close()
Natija
birinchi server portni egalladi: 9501
ikkinchi egallay olmadi: Address already in use
IPv6 loopback'da o'sha port band emas: ha

Oxirgi qator muhim: band bo'ladigan narsa port emas, manzil va port juftligi. 127.0.0.1:9501 band bo'lsa ham, [::1]:9501 bo'sh qolaveradi.

Qaysi manzilga bog'lanish kerak? #

ManzilKim ulana oladi
127.0.0.1Faqat shu kompyuterdagi dasturlar
0.0.0.0Har qanday tarmoq interfeysidan
Aniq IPFaqat o'sha interfeysdan
Ishlab chiqishda 0.0.0.0 ga bog'lamang

Ko'p qo'llanmalarda 0.0.0.0 yoziladi, chunki u "hamma joyda ishlaydi".

Lekin bu sizning ishlab chiqish serveringizni butun tarmoqqa ochib qo'yadi: kafedagi Wi-Fi da o'tirgan har kim uning ochiq bazasiga va sozlamalariga kirishi mumkin.

Qoida:

QayerdaNimaga bog'lash
O'z kompyuteringizda ishlab chiqish127.0.0.1
Konteyner ichida0.0.0.0 (tashqarisi izolyatsiya qilingan)
Ishlab chiqarish serveriAniq IP yoki teskari proksi ortida

Ishlab chiqarishda ham ko'pincha ilova 127.0.0.1 da turadi, tashqariga esa faqat nginx chiqadi.

Amaliy topshiriq
  1. Mashhur portlar misolini ishga tushiring.
  2. Unga mysql va redis ni qo'shing - tizimingizda topildimi?
  3. To'rtlik misolini ishga tushiring va mijozlar sonini beshga oshiring.
  4. Nima uchun server porti o'zgarmasligini tushuntiring.
  5. Port band xatosi misolini ishga tushiring.
  6. SO_REUSEADDR qo'shib ko'ring - natija o'zgardimi?
  7. 1023 dan kichik portga bog'lanishga urinib ko'ring.
  8. ss -tln (yoki netstat -an) bilan tinglayotgan portlarni ko'ring.
  9. accept() qaytargan soket bilan tinglovchi soket farqini yozing.
  10. O'z loyihangizda server qaysi manzilga bog'langanini tekshiring.

Xulosa #

  • Port - 16 bitli son: 0-1023 mashhur, 1024-49151 ro'yxatdagi, qolgani vaqtinchalik.
  • 1024 dan kichik portlar uchun odatda administrator huquqi kerak.
  • Port nomlari tarmoqdan emas, tizimdagi services faylidan olinadi.
  • Ulanishni to'rtlik aniqlaydi: mijoz IP va porti, server IP va porti.
  • Shuning uchun bitta server porti millionlab ulanishga xizmat qila oladi.
  • Soket yo'li: socketbindlistenaccept, mijozda esa connect.
  • accept() yangi soket qaytaradi; tinglovchi soket ma'lumot uzatmaydi.
  • Band bo'ladigan narsa - manzil va port juftligi, faqat port emas.
  • 127.0.0.1 faqat mahalliy kirishni beradi, 0.0.0.0 - butun tarmoqni.
  • Ishlab chiqishda 127.0.0.1 ga bog'laning, aks holda serveringiz ochiq qoladi.

Keyingi bo'limda xususiy manzillar internetga qanday chiqishini - NAT ni ko'ramiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.