9-bo‘lim
Portlar va soketlar
Port raqamlari diapazonlari, mashhur portlar, ulanishni aniqlaydigan to'rtlik, soket API ning bosqichlari va "port band" xatosi.
Ushbu bo‘lim mundarijasi
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:
| Diapazon | Nomi | Kim ishlatadi |
|---|---|---|
| 0-1023 | Mashhur (well-known) | Standart xizmatlar |
| 1024-49151 | Ro'yxatga olingan | Ma'lum dasturlar |
| 49152-65535 | Vaqtinchalik (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.
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}")
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
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.
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()
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.
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 #
Har bosqichning aniq vazifasi bor:
| Chaqiruv | Nima 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 qaytaradiBu 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:
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:
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()
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? #
| Manzil | Kim ulana oladi |
|---|---|
127.0.0.1 | Faqat shu kompyuterdagi dasturlar |
0.0.0.0 | Har qanday tarmoq interfeysidan |
| Aniq IP | Faqat o'sha interfeysdan |
0.0.0.0 ga bog'lamangKo'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:
| Qayerda | Nimaga bog'lash |
|---|---|
| O'z kompyuteringizda ishlab chiqish | 127.0.0.1 |
| Konteyner ichida | 0.0.0.0 (tashqarisi izolyatsiya qilingan) |
| Ishlab chiqarish serveri | Aniq IP yoki teskari proksi ortida |
Ishlab chiqarishda ham ko'pincha ilova 127.0.0.1 da turadi,
tashqariga esa faqat nginx chiqadi.
- Mashhur portlar misolini ishga tushiring.
- Unga
mysqlvaredisni qo'shing - tizimingizda topildimi? - To'rtlik misolini ishga tushiring va mijozlar sonini beshga oshiring.
- Nima uchun server porti o'zgarmasligini tushuntiring.
- Port band xatosi misolini ishga tushiring.
SO_REUSEADDRqo'shib ko'ring - natija o'zgardimi?- 1023 dan kichik portga bog'lanishga urinib ko'ring.
ss -tln(yokinetstat -an) bilan tinglayotgan portlarni ko'ring.accept()qaytargan soket bilan tinglovchi soket farqini yozing.- 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
servicesfaylidan 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:
socket→bind→listen→accept, mijozda esaconnect. accept()yangi soket qaytaradi; tinglovchi soket ma'lumot uzatmaydi.- Band bo'ladigan narsa - manzil va port juftligi, faqat port emas.
127.0.0.1faqat mahalliy kirishni beradi,0.0.0.0- butun tarmoqni.- Ishlab chiqishda
127.0.0.1ga bog'laning, aks holda serveringiz ochiq qoladi.
Keyingi bo'limda xususiy manzillar internetga qanday chiqishini - NAT ni ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
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.