توی این درس یاد میگیری آدرس IP میگوید «کدام ماشین» و پورت میگوید «کدام برنامهی روی آن ماشین». دو پروتکل اصلی TCP (اتصالمحور و قابلاطمینان) و UDP (سریع و بدون اتصال) را میشناسی، پورتهای معروف (۲۲ برای SSH، ۸۰ برای HTTP، ۴۴۳ برای HTTPS…) را میدانی، با ss -tulpn میبینی هر برنامه روی چه پورتی گوش میدهد (listen)، و با curl -I و nc -zv host 22 اتصال به یک سرویس را آزمایش میکنی. تمرین: پیدا میکنی کدام پروسه پورت ۲۲ را گرفته است.
مسئله: برنامه بالا آمده، ولی کار نمیکند
Section titled “مسئله: برنامه بالا آمده، ولی کار نمیکند”یک وبسرور را راه انداختهای ولی از بیرون باز نمیشود. یا برنامهات میگوید Address already in use. یا ssh وصل نمیشود. همهی اینها یک ریشه دارند: پورتها. باید بتوانی بپرسی: چه کسی روی این پورت گوش میدهد؟ روی کدام آدرس؟ و آیا من از بیرون میتوانم به آن برسم؟
تشبیه: ساختمان و شمارهی واحد
Section titled “تشبیه: ساختمان و شمارهی واحد”IP آدرس یک ساختمان اداری است؛ پورت شمارهی واحد (اتاق) در آن. وبسرور در واحد ۸۰ مینشیند، ssh در واحد ۲۲ و دیتابیس در واحد ۵۴۳۲. برنامهای که «گوش میدهد» مثل کارمندی است که پشت در یک واحد منتظر مراجعهکننده نشسته. هر واحد فقط یک کارمند دارد (دو برنامه نمیتوانند یک پورت را بگیرند). و TCP مثل تماس تلفنی است (اول وصل میشوی، تضمین میکند حرفت کامل برسد) در حالی که UDP مثل کارتپستال است (میفرستی و میروی، بدون تضمین).
یک اتصال در شبکه را چهار چیز مشخص میکنند: IP و پورت کلاینت، IP و پورت سرور. مثلاً مرورگر تو از 203.0.113.5:51234 (پورت موقت و تصادفی) به 192.0.2.10:443 (پورت ثابت وبسرور) وصل میشود. سرور روی یک پورت ثابت و معروف منتظر میماند و کلاینتها از پورتهای موقت (ephemeral) وصل میشوند. هر پورت یک عدد ۱۶ بیتی است: 0 تا 65535.
مثالهای عملی
Section titled “مثالهای عملی”این درس را روی ماشین systemdدار آزمایشی (Ubuntu) اجرا کردهام که در آن sshd و nginx واقعی نصباند و من root هستم.
مثال ۱: پورتهای معروف
Section titled “مثال ۱: پورتهای معروف”عدد پورت قرارداد است و فهرست رسمیاش در /etc/services هست:
grep -E '^(ssh|telnet|smtp|domain|http|https|ntp|imaps|mysql|postgresql|redis)\s' /etc/servicesecho "--- پرسیدن مستقیم (getent services):"getent services 22/tcpgetent services 443/tcpgetent services 53/udpssh 22/tcp # SSH Remote Login Protocoltelnet 23/tcpsmtp 25/tcp maildomain 53/tcp # Domain Name Serverdomain 53/udphttp 80/tcp www # WorldWideWeb HTTPntp 123/udp # Network Time Protocolhttps 443/tcp # http protocol over TLS/SSLhttps 443/udp # HTTP/3imaps 993/tcp # IMAP over SSLmysql 3306/tcppostgresql 5432/tcp postgres # PostgreSQL Databaseredis 6379/tcp--- پرسیدن مستقیم (getent services):ssh 22/tcphttps 443/tcpdomain 53/udp| پورت | پروتکل | سرویس |
|---|---|---|
| 22 | TCP | SSH (ورود امن به سرور) |
| 53 | TCP و UDP | DNS |
| 80 | TCP | HTTP (وب بدون رمزنگاری) |
| 443 | TCP | HTTPS (وب امن) |
| 25 / 465 / 587 | TCP | ایمیل (SMTP) |
| 3306 | TCP | MySQL / MariaDB |
| 5432 | TCP | PostgreSQL |
| 6379 | TCP | Redis |
| 123 | UDP | NTP (همگامسازی ساعت) |
پورتهای ۰ تا ۱۰۲۳ «پورتهای ممتاز» هستند: در لینوکس فقط root (یا برنامهای با اجازهی ویژه) میتواند روی آنها گوش بدهد؛ پورتهای ۱۰۲۴ تا ۴۹۱۵۱ ثبتشدهاند و بالاتر پورتهای موقتِ کلاینتها.
مثال ۲: ss -tulpn، چه کسی گوش میدهد؟
Section titled “مثال ۲: ss -tulpn، چه کسی گوش میدهد؟”ss (socket statistics؛ جایگزین قدیمی netstat) سوکتها را نشان میدهد. اولین حالت پرکاربردش را با ۵ فلگ میزنند: -t TCP، -u UDP، -l فقط سوکتهای در حال گوشدادن، -p نام پروسهی صاحب سوکت (برای دیدن پروسههای دیگران root لازم است)، -n عدد پورت بهجای اسم (سریعتر و بدون DNS). اول وضع فعلی این سرور، بعد چند سرویس راه میاندازم تا چیز بیشتری برای دیدن باشد:
echo "--- قبل از راهاندازی سرویسها:"ss -tulpn--- قبل از راهاندازی سرویسها:Netid State Recv-Q Send-Q Local Address:Port Peer Address:PortProcesstcp LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("systemd",pid=1,fd=46))tcp LISTEN 0 4096 [::]:22 [::]:* users:(("systemd",pid=1,fd=49))فقط پورت ۲۲ باز است؛ ولی عجیب: صاحبش systemd است، نه sshd! در Ubuntu جدید، ssh با socket activation کار میکند: خود systemd (پروسهی ۱) روی پورت ۲۲ گوش میدهد و فقط وقتی اولین اتصال برسد، sshd را بالا میآورد (درس بعد). حالا یک وبسرور واقعی (nginx)، دو وبسرور سادهی پایتون (یکی فقط روی 127.0.0.1 و یکی روی همهی آدرسها) و یک سوکت UDP:
systemctl start nginxpython3 -m http.server 8098 --bind 127.0.0.1 > /dev/null 2>&1 &python3 -m http.server 8099 --bind 0.0.0.0 > /dev/null 2>&1 &python3 -c 'import socket, time; s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.bind(("0.0.0.0", 9999)); time.sleep(600)' &sleep 1ss -tulpnNetid State Recv-Q Send-Q Local Address:Port Peer Address:PortProcessudp UNCONN 0 0 0.0.0.0:9999 0.0.0.0:* users:(("python3",pid=935,fd=3))tcp LISTEN 0 5 127.0.0.1:8098 0.0.0.0:* users:(("python3",pid=933,fd=3))tcp LISTEN 0 5 0.0.0.0:8099 0.0.0.0:* users:(("python3",pid=934,fd=3))tcp LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("systemd",pid=1,fd=46))tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=931,fd=5),("nginx",pid=930,fd=5),("nginx",pid=929,fd=5))tcp LISTEN 0 4096 [::]:22 [::]:* users:(("systemd",pid=1,fd=49))tcp LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=931,fd=6),("nginx",pid=930,fd=6),("nginx",pid=929,fd=6))ستونها:
| ستون | معنی |
|---|---|
Netid |
پروتکل: tcp یا udp |
State |
LISTEN (TCP در حال گوشدادن) یا UNCONN (سوکت UDP که منتظر بسته است) |
Recv-Q / Send-Q |
صفهای دریافت و ارسال (برای LISTEN: اندازهی صف انتظار اتصالها) |
Local Address:Port |
روی کدام آدرس و کدام پورت گوش میدهد |
Peer Address:Port |
طرف مقابل (برای LISTEN همیشه *) |
Process |
users:(("نام",pid=...,fd=...)): چه پروسهای |
Local Address بسیار مهم است (مثال ۳): 0.0.0.0:80 یعنی روی همهی آدرسهای IPv4 این ماشین، [::]:80 یعنی همهی آدرسهای IPv6، و 127.0.0.1:8098 یعنی فقط از خود همین ماشین. برای nginx چند ردیف users هست چون پروسهی اصلی و کارگرها (worker) همه همان سوکت را دارند. برای فیلتر یک پورت از زبان فیلتر ss استفاده کن:
echo "--- فقط پورت 80:"ss -tlnp 'sport = :80'echo "--- خلاصهی کل سوکتها (ss -s، چهار خط اول):"ss -s | head -4--- فقط پورت 80:State Recv-Q Send-Q Local Address:Port Peer Address:PortProcessLISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=931,fd=5),("nginx",pid=930,fd=5),("nginx",pid=929,fd=5))LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=931,fd=6),("nginx",pid=930,fd=6),("nginx",pid=929,fd=6))--- خلاصهی کل سوکتها (ss -s، چهار خط اول):Total: 55TCP: 68 (estab 0, closed 62, orphaned 0, timewait 0)
Transport Total IP IPv6مثال ۳: گوشدادن روی 127.0.0.1 در برابر 0.0.0.0
Section titled “مثال ۳: گوشدادن روی 127.0.0.1 در برابر 0.0.0.0”آدرسی که یک سرویس روی آن گوش میدهد، تعیین میکند چه کسی میتواند به آن برسد: 127.0.0.1 فقط خود ماشین؛ 0.0.0.0 همهی آدرسهای ماشین، یعنی از بیرون هم. دو وبسرور نمونهی بالا دقیقاً همین را نشان میدهند. از آدرس شبکهی همین ماشین (نه 127.0.0.1) به هر دو وصل میشوم:
ip=$(hostname -I | awk '{print $1}')echo "آدرس شبکهی این ماشین: $ip"echo "--- از طریق 127.0.0.1 به هر دو:"curl -s -o /dev/null -w '8098 (فقط localhost): HTTP %{http_code}\n' http://127.0.0.1:8098curl -s -o /dev/null -w '8099 (همهی آدرسها): HTTP %{http_code}\n' http://127.0.0.1:8099echo "--- از طریق آدرس شبکه:"curl -s -m 3 -o /dev/null http://$ip:8098 && echo "8098: پاسخ آمد" || echo "8098: اتصال رد شد"curl -s -m 3 -o /dev/null http://$ip:8099 && echo "8099: پاسخ آمد" || echo "8099: اتصال رد شد"آدرس شبکهی این ماشین: 172.17.0.4--- از طریق 127.0.0.1 به هر دو:8098 (فقط localhost): HTTP 2008099 (همهی آدرسها): HTTP 200--- از طریق آدرس شبکه:8098: اتصال رد شد8099: پاسخ آمدسرویسی که فقط به 127.0.0.1 bind شده، از بیرون غیرقابلدسترس است، حتی اگر فایروال باز باشد. این دقیقاً همان چیزی است که برای دیتابیسها میخواهی (فقط برنامهی روی همان ماشین به آن برسد) و برای وبسرور عمومی نمیخواهی. خطای رایج «سایت از بیرون باز نمیشود» اغلب همین 127.0.0.1 در تنظیمات است.
مثال ۴: curl -I، آزمایش یک سرویس HTTP
Section titled “مثال ۴: curl -I، آزمایش یک سرویس HTTP”curl -I فقط سرآیندهای (headers) پاسخ HTTP را میگیرد (درخواست HEAD) و برای بررسی سلامت سریع یک وبسرور عالی است. (-s نوار پیشرفت را خاموش میکند.)
curl -sI http://localhostecho "--- پورت بسته (نه nginx، نه هیچ برنامهای روی 81):"curl -sI http://localhost:81echo "کد خروج curl: $?"HTTP/1.1 200 OKServer: nginx/1.24.0 (Ubuntu)Date: Sat, 03 Oct 2026 21:57:18 GMTContent-Type: text/htmlContent-Length: 615Last-Modified: Sat, 03 Oct 2026 12:33:24 GMTConnection: keep-aliveETag: "6ac0f614-267"Accept-Ranges: bytes
--- پورت بسته (نه nginx، نه هیچ برنامهای روی 81):کد خروج curl: 7خط اول HTTP/1.1 200 OK یعنی درخواست موفق بود (کدهای ۲xx موفق، ۳xx ریدایرکت، ۴xx خطای کلاینت مثل 404، ۵xx خطای سرور). Server: نرمافزار و Content-Type نوع محتوا است. برای پورتی که کسی گوش نمیدهد، curl چیزی چاپ نکرد و با کد خروج ۷ (Couldn’t connect) تمام شد. فقط کد وضعیت را هم میشود گرفت:
echo "کد HTTP: $(curl -s -o /dev/null -w '%{http_code}' http://localhost)"echo "--- با -v مراحل اتصال TCP را هم ببین (چند خط اول):"curl -sv -o /dev/null http://localhost 2>&1 | grep -E '^(\* Trying|\* Connected|> GET|< HTTP)'کد HTTP: 200--- با -v مراحل اتصال TCP را هم ببین (چند خط اول):* Trying [::1]:80...* Connected to localhost (::1) port 80> GET / HTTP/1.1< HTTP/1.1 200 OKمثال ۵: nc -zv، آیا این پورت باز است؟
Section titled “مثال ۵: nc -zv، آیا این پورت باز است؟”nc (netcat) برای آزمایش اتصال TCP سادهترین ابزار است. -z یعنی فقط وصل شو و بدون فرستادن داده ببند (اسکن)، -v یعنی گزارش بده، و -w 2 حداکثر دو ثانیه صبر کن. من 127.0.0.1 را صریح مینویسم تا IPv6 قاطی نشود (-4 هم همین است):
echo "--- پورت 22 (ssh، باز):"nc -zv 127.0.0.1 22echo "--- پورت 80 (nginx، باز):"nc -zv 127.0.0.1 80echo "--- پورت 81 (کسی گوش نمیدهد):"nc -zv 127.0.0.1 81echo "--- یک بازه (-z روی چند پورت، با -w 1):"nc -zv -w 1 127.0.0.1 78-82 2>&1--- پورت 22 (ssh، باز):Connection to 127.0.0.1 22 port [tcp/ssh] succeeded!--- پورت 80 (nginx، باز):Connection to 127.0.0.1 80 port [tcp/http] succeeded!--- پورت 81 (کسی گوش نمیدهد):nc: connect to 127.0.0.1 port 81 (tcp) failed: Connection refused--- یک بازه (-z روی چند پورت، با -w 1):nc: connect to 127.0.0.1 port 78 (tcp) failed: Connection refusednc: connect to 127.0.0.1 port 79 (tcp) failed: Connection refusedConnection to 127.0.0.1 80 port [tcp/http] succeeded!nc: connect to 127.0.0.1 port 81 (tcp) failed: Connection refusednc: connect to 127.0.0.1 port 82 (tcp) failed: Connection refusedدو خروجی ممکن: succeeded! یعنی اتصال TCP برقرار شد (یک برنامه آنجا گوش میدهد) و Connection refused یعنی ماشین جواب داد ولی هیچ برنامهای روی آن پورت گوش نمیدهد. و nc -zv روی ماشین دیگر هم همینطور کار میکند (nc -zv example.com 443)؛ تنها ابزار تو برای جواب «از این ماشین به آن پورت راه دارم؟».
nc -zv host 80 نوشت «Connection refused». یعنی…
refused یعنی یک RST فوری برگشته: ماشین زنده است و پورت بسته. اگر فایروال بسته بود (DROP) بهجای آن timeout میدیدی (مثال ۶).
مثال ۶: «refused» یا «timed out»؟
Section titled “مثال ۶: «refused» یا «timed out»؟”وقتی اتصال نمیگیرد، متن خطا علت را نشان میدهد: Connection refused یعنی ماشین رسید و گفت «اینجا کسی نیست» (پورت بسته)؛ ولی timed out یعنی هیچ جوابی نیامد: بستهها در راه یا در خود ماشین دور ریخته میشوند (فایروال، مسیر نادرست). با یک قانون فایروال (iptables، درس فایروال) روی پورت ۸۰۹۹ میبینیم:
echo "--- پورت 8099 بدون فایروال:"nc -zv -w 2 127.0.0.1 8099iptables -A INPUT -p tcp --dport 8099 -j DROPecho "--- بعد از قانون DROP (بستهها بیصدا دور ریخته میشوند):"nc -zv -w 2 127.0.0.1 8099iptables -D INPUT -p tcp --dport 8099 -j DROPecho "--- بعد از برداشتن قانون:"nc -zv -w 2 127.0.0.1 8099echo "--- و پورتی که اصلاً برنامهای ندارد (8097):"nc -zv -w 2 127.0.0.1 8097--- پورت 8099 بدون فایروال:Connection to 127.0.0.1 8099 port [tcp/*] succeeded!--- بعد از قانون DROP (بستهها بیصدا دور ریخته میشوند):nc: connect to 127.0.0.1 port 8099 (tcp) timed out: Operation now in progress--- بعد از برداشتن قانون:Connection to 127.0.0.1 8099 port [tcp/*] succeeded!--- و پورتی که اصلاً برنامهای ندارد (8097):nc: connect to 127.0.0.1 port 8097 (tcp) failed: Connection refused| نتیجه | معنی | معمولاً کجا را بگرد |
|---|---|---|
succeeded |
سرویس گوش میدهد و مسیر باز است | |
Connection refused |
ماشین رسید، پورت بسته (برنامهای گوش نمیدهد) | سرویس بالا نیست؟ پورت دیگری است؟ |
timed out |
هیچ جوابی نیامد | فایروال (روی سرور یا بین راه)، مسیر، ماشین خاموش |
No route to host / Network is unreachable |
مسیر پیدا نشد | شبکه، gateway |
مثال ۷: چه کسی پورت را گرفته؟ (تمرین اصلی)
Section titled “مثال ۷: چه کسی پورت را گرفته؟ (تمرین اصلی)”وقتی Address already in use میبینی، باید صاحب پورت را پیدا کنی. سه ابزار: ss -tlnp، lsof -i و fuser. همین الآن پورت ۲۲ را بپرس:
echo "--- ss:"ss -tlnp 'sport = :22'echo "--- lsof -i :22:"lsof -i :22echo "--- fuser (فقط PID ها):"fuser 22/tcp--- ss:State Recv-Q Send-Q Local Address:Port Peer Address:PortProcessLISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=965,fd=3),("systemd",pid=1,fd=46))LISTEN 0 4096 [::]:22 [::]:* users:(("sshd",pid=965,fd=4),("systemd",pid=1,fd=49))--- lsof -i :22:COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEsystemd 1 root 46u IPv4 3265877 0t0 TCP *:ssh (LISTEN)systemd 1 root 49u IPv6 3264996 0t0 TCP *:ssh (LISTEN)sshd 965 root 3u IPv4 3265877 0t0 TCP *:ssh (LISTEN)sshd 965 root 4u IPv6 3264996 0t0 TCP *:ssh (LISTEN)--- fuser (فقط PID ها):22/tcp: 1 965میبینی چه کسی پورت ۲۲ را گرفته: systemd (PID 1) بهخاطر socket activation. بیا یک اتصال بزنیم تا sshd هم بیاید و دوباره بپرسیم:
nc -z 127.0.0.1 22sleep 1ss -tlnp 'sport = :22'echo "--- sshd حالا هم کار میکند:"systemctl is-active ssh.serviceps -eo pid,comm | grep -w sshd | head -2Connection to 127.0.0.1 22 port [tcp/ssh] succeeded!State Recv-Q Send-Q Local Address:Port Peer Address:PortProcessLISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=965,fd=3),("systemd",pid=1,fd=46))LISTEN 0 4096 [::]:22 [::]:* users:(("sshd",pid=965,fd=4),("systemd",pid=1,fd=49))--- sshd حالا هم کار میکند:active 965 sshdبعد از اولین اتصال، sshd راه افتاد و همان سوکت را از systemd گرفت؛ حالا هر دو در ستون Process هستند. حالا خطای معروف را بسازیم: دوباره روی پورتی که پر است گوش بده:
python3 -m http.server 8099 2>&1 | tail -2echo "--- پیدا کردن صاحب پورت:"ss -tlnp 'sport = :8099' self.socket.bind(self.server_address)OSError: [Errno 98] Address already in use--- پیدا کردن صاحب پورت:State Recv-Q Send-Q Local Address:Port Peer Address:PortProcessLISTEN 0 5 0.0.0.0:8099 0.0.0.0:* users:(("python3",pid=934,fd=3))OSError: [Errno 98] Address already in use: همین خطا را nginx هم میدهد (bind() ... failed (98: Address already in use)). با ss -tlnp صاحب پورت (اینجا وبسرور پایتون دیگر ما با PID مشخص) را پیدا میکنی؛ بعد یا او را متوقف میکنی (kill PID)، یا سرویس خودت را روی پورت دیگری میگذاری.
مثال ۸: اتصالهای برقرارشده
Section titled “مثال ۸: اتصالهای برقرارشده”ss -tln فقط گوشدهندهها را نشان میداد. اتصالهای در جریان (ESTAB) را بدون -l میبینی. با nc یک اتصال میسازم و چند ثانیه باز نگه میدارم:
nc -l 9100 > /dev/null &server=$!sleep 1( sleep 4 | nc -N 127.0.0.1 9100 ) &client=$!sleep 1echo "--- اتصالهای ESTABLISHED روی پورت 9100 (هر اتصال دو سر دارد):"ss -tn state established '( sport = :9100 or dport = :9100 )'wait $server $client--- اتصالهای ESTABLISHED روی پورت 9100 (هر اتصال دو سر دارد):Recv-Q Send-Q Local Address:Port Peer Address:Port Process0 0 127.0.0.1:43044 127.0.0.1:91000 0 127.0.0.1:9100 127.0.0.1:43044هر اتصال دو ردیف دارد (یکی از دید سرور، یکی از دید کلاینت): 127.0.0.1:9100 ↔ 127.0.0.1:پورت-موقت. پورت کلاینت (عددی مثل ۴۰۰۰۰) را سیستم تصادفی از بازهی پورتهای موقت میدهد. وضعیتهای مهم TCP:
| وضعیت | معنی |
|---|---|
LISTEN |
سرور منتظر اتصال است |
ESTAB |
اتصال برقرار و فعال |
SYN-SENT |
کلاینت درخواست اتصال فرستاده و منتظر جواب است (اگر ماندگار شود: سرور/فایروال جواب نمیدهد) |
TIME-WAIT |
اتصال بسته شده و سیستم چند ثانیه آن را نگه میدارد (عادی) |
CLOSE-WAIT |
طرف مقابل بسته ولی برنامهی ما هنوز سوکت را نبسته (اگر زیاد شد: باگ برنامه) |
پشت پرده: سوکت چیست؟
Section titled “پشت پرده: سوکت چیست؟”برنامهها از طریق سوکت (socket) با شبکه حرف میزنند: یک «پریز» داخل هسته که آدرس و پورتی دارد. سرور یک سوکت میسازد، با bind() آن را به یک آدرس و پورت میبندد، با listen() شروع به گوشدادن میکند و با accept() اتصالها را میپذیرد. کلاینت با connect() وصل میشود. پیامهای خطای این درس همانها هستند: Address already in use از bind()، و Connection refused از connect(). ss اطلاعاتش را از هسته میخواند (فایلهای /proc/net/tcp و مشابه):
echo "--- بازهی پورتهای موقت کلاینت:"cat /proc/sys/net/ipv4/ip_local_port_rangeecho "--- اولین پورتی که کاربر عادی میتواند bind کند:"sysctl net.ipv4.ip_unprivileged_port_startecho "--- سه خط اول /proc/net/tcp (سوکتهای TCP خام؛ پورتها هگزادسیمالاند):"head -3 /proc/net/tcp | cut -c1-90--- بازهی پورتهای موقت کلاینت:32768 60999--- اولین پورتی که کاربر عادی میتواند bind کند:net.ipv4.ip_unprivileged_port_start = 1024--- سه خط اول /proc/net/tcp (سوکتهای TCP خام؛ پورتها هگزادسیمالاند): sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout 0: 0100007F:1FA2 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 1: 00000000:1FA3 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0در فایل خام، 0100007F:1FA2 یعنی 127.0.0.1:8098 (آدرس بهصورت هگزا و برعکس، 1FA2 هگزای ۸۱۹۴ است؛ پورت واقعی همان است که ss برایت ترجمه میکند). و چون «پورتهای زیر ۱۰۲۴ ممتازند»، کاربر عادی روی آنها گوش نمیدهد:
su - ali -c 'python3 -m http.server 81 2>&1 | tail -1'echo "--- کاربر عادی با ss -p، نام پروسهی سوکتهای root را نمیبیند:"su - ali -c 'ss -tlnp | head -4'PermissionError: [Errno 13] Permission denied--- کاربر عادی با ss -p، نام پروسهی سوکتهای root را نمیبیند:State Recv-Q Send-Q Local Address:Port Peer Address:PortProcessLISTEN 0 5 127.0.0.1:8098 0.0.0.0:*LISTEN 0 5 0.0.0.0:8099 0.0.0.0:*LISTEN 0 4096 0.0.0.0:22 0.0.0.0:*PermissionError: Permission denied: برای همین سرویسهایی مثل nginx ابتدا (بهعنوان root) به پورت ۸۰ گوش میدهند و بعد دسترسی کارگرها را پایین میآورند. و ستون Process برای ali خالی است: ss -p برای دیدن پروسههای دیگران sudo لازم دارد.
جدولهای مرجع
Section titled “جدولهای مرجع”| دستور | کار |
|---|---|
ss -tulpn |
همهی گوشدهندهها (TCP و UDP) با پروسه، عددی |
ss -tlnp 'sport = :22' |
فقط یک پورت |
ss -tn / ss -tn state established |
اتصالهای برقرار |
ss -s |
خلاصهی آمار |
lsof -i :80 |
چه کسی پورت ۸۰ را دارد |
fuser 80/tcp |
PID های پورت |
curl -I آدرس / curl -sI آدرس |
فقط سرآیندهای HTTP |
curl -s -o /dev/null -w '%{http_code}' آدرس |
فقط کد وضعیت |
nc -zv host 22 |
آزمایش پورت TCP |
nc -zv -w 2 host 20-25 |
آزمایش بازهی پورتها |
getent services 22/tcp |
اسم سرویس یک پورت |
فلگ ss |
معنی |
|---|---|
-t / -u |
TCP / UDP |
-l |
فقط در حال گوشدادن |
-p |
نام پروسه (دیدن پروسههای دیگران: sudo) |
-n |
عددی (بدون DNS و بدون اسم سرویس) |
-a |
همه (شامل گوشدهندهها و برقرارشدهها) |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) سرویس فقط روی 127.0.0.1 گوش میدهد
Section titled “۱) سرویس فقط روی 127.0.0.1 گوش میدهد”مثال ۳. علامتش: از خود سرور کار میکند، از بیرون نه، و ss -tln آدرس 127.0.0.1:پورت را نشان میدهد. راهحل: در تنظیمات سرویس آدرس 0.0.0.0 (یا IP مشخص) را بگذار؛ برای دیتابیسها برعکس: عمداً روی 127.0.0.1 بماند.
۲) Address already in use
Section titled “۲) Address already in use”مثال ۷: دو برنامه یک پورت را نمیگیرند. راهحل: ss -tlnp 'sport = :پورت' برای پیدا کردن صاحب، بعد متوقف کردنش یا عوض کردن پورت.
۳) فایروال را با «پورت بسته» اشتباه گرفتن
Section titled “۳) فایروال را با «پورت بسته» اشتباه گرفتن”مثال ۶: refused (برنامه نیست) در برابر timed out (بستهها دور ریخته میشوند). راهحل: اول از روی خود سرور با nc -zv 127.0.0.1 پورت بزن؛ اگر آنجا succeeded شد ولی از بیرون timed out، فایروال یا مسیر است؛ اگر آنجا هم refused بود، سرویس را بررسی کن.
۴) ss بدون sudo و ستون خالی
Section titled “۴) ss بدون sudo و ستون خالی”su - ali -c 'ss -tlnp | cat' | head -3State Recv-Q Send-Q Local Address:Port Peer Address:PortProcessLISTEN 0 5 127.0.0.1:8098 0.0.0.0:*LISTEN 0 5 0.0.0.0:8099 0.0.0.0:*برای سوکتهای دیگران ستون Process خالی میماند. راهحل: sudo ss -tulpn.
۵) گوشدادن روی پورت زیر ۱۰۲۴ با کاربر عادی
Section titled “۵) گوشدادن روی پورت زیر ۱۰۲۴ با کاربر عادی”مثال «پشت پرده»: Permission denied. راهحل: پورت بالاتر (مثل ۸۰۸۰) با یک reverse proxy روی ۸۰ و ۴۴۳، یا اجرا بهعنوان root برای bind اولیه (روش nginx).
۶) UDP را با nc -z آزمایشکردن
Section titled “۶) UDP را با nc -z آزمایشکردن”UDP اتصال ندارد، پس «succeeded» برای UDP فقط یعنی «خطایی برنگشت»، نه «برنامهای آنجاست». برای UDP بهتر است ابزار خود آن سرویس (مثل dig @سرور برای DNS) را آزمایش کنی.
بگو کدام پروسه روی پورت ۸۰ گوش میدهد (با ss). (nginx در مثال ۲ راه افتاد.)
دیدن جواب
ss -tlnp 'sport = :80' | awk 'NR>1 {print $4, $6}' | cut -c1-700.0.0.0:80 users:(("nginx",pid=931,fd=5),("nginx",pid=930,fd=5),("ngin[::]:80 users:(("nginx",pid=931,fd=6),("nginx",pid=930,fd=6),("nginx",تمرین اصلی: پیدا کن کدام پروسه پورت ۲۲ را گرفته است. با دو ابزار (ss -tlnp و lsof -i) صاحب پورت ۲۲ را پیدا کن و بگو چرا اسم آن sshd نیست (راهنمایی: مثال ۲ و ۷).
دیدن جواب
ss -tlnp 'sport = :22' | awk 'NR>1 {print $4, $6}'lsof -i :22 | awk 'NR>1 {print $1, $2, $9}'0.0.0.0:22 users:(("sshd",pid=965,fd=3),("systemd",pid=1,fd=46))[::]:22 users:(("sshd",pid=965,fd=4),("systemd",pid=1,fd=49))systemd 1 *:sshsystemd 1 *:sshsshd 965 *:sshsshd 965 *:sshپورت ۲۲ را systemd (PID 1) نگه میدارد و (چون در مثال ۷ یک اتصال زدیم) sshd هم همان سوکت را دارد. سرویس ssh با socket activation کار میکند: systemd (از طریق ssh.socket) پورت را از ابتدا نگه میدارد و فقط با اولین اتصال sshd را بالا میآورد و همان سوکت را به او هم میدهد؛ برای همین قبل از اولین اتصال فقط systemd را میبینی.
یک «پورتچکر» کوچک بنویس: برای فهرستی از پورتها (22 80 443 8099 9999) روی 127.0.0.1 با nc -z -w 1 بررسی کن باز است یا بسته، و اسم سرویس آن را با getent services چاپ کن (برای پورتهایی که اسم ندارند «نامشخص»).
دیدن جواب
for p in 22 80 443 8099 9999; do name=$(getent services $p/tcp | awk '{print $1}') if nc -z -w 1 127.0.0.1 $p 2>/dev/null; then st="باز"; else st="بسته"; fi echo "پورت $p (${name:-نامشخص}): $st"doneپورت 22 (ssh): بازپورت 80 (http): بازپورت 443 (https): بستهپورت 8099 (نامشخص): بازپورت 9999 (نامشخص): بسته(پورت ۹۹۹۹ در این ماشین فقط UDP است؛ برای TCP بسته نشان داده میشود.)
آزمونک
Section titled “آزمونک”تفاوت اصلی TCP و UDP؟
وب، ssh و دیتابیسها از TCP؛ DNS، NTP و ویدیوی زنده از UDP.
ss -tulpn چه نشان میدهد؟
t=TCP، u=UDP، l=listening، p=process، n=numeric.
سرویسی روی 127.0.0.1:5432 گوش میدهد. از ماشین دیگر میشود به آن وصل شد؟
127.0.0.1 فقط loopback است. برای دسترسی بیرونی باید روی 0.0.0.0 یا IP شبکه گوش بدهد.
nc -zv host 80 نتیجه میدهد «Connection refused». یعنی…
refused یعنی جواب فوری رد؛ timeout یعنی هیچ جوابی نیامد (فایروال یا مسیر).
برنامهات خطای «Address already in use» میدهد. اول چه میزنی؟
دو برنامه یک پورت را نمیگیرند؛ صاحب را پیدا کن و متوقفش کن یا پورت را عوض کن.
curl -I http://localhost چه میکند؟
درخواست HEAD؛ برای بررسی سریع سلامت یک وبسرور.
جمعبندی
Section titled “جمعبندی”- پورت عدد ۱۶ بیتی که مشخص میکند کدام برنامه؛ معروفها: 22 ssh، 80 http، 443 https، 53 dns، 5432 postgres. هر اتصال = IP و پورت دو طرف. TCP قابلاطمینان، UDP سریع.
ss -tulpn: چه کسی روی چه پورتی و چه آدرسی گوش میدهد؛ss -tlnp 'sport = :22'برای یک پورت؛lsof -i :پورتوfuser. دیدن پروسهها به sudo نیاز دارد.- آدرس گوشدادن:
127.0.0.1فقط خود ماشین،0.0.0.0(و[::]) همهی آدرسها. - آزمایش اتصال:
curl -Iبرای HTTP،nc -zv host پورتبرای هر پورت TCP. refused= ماشین زنده، پورت بسته؛timed out= جوابی نیامد (فایروال/مسیر).Address already in use: صاحب پورت را باssپیدا کن. پورتهای زیر ۱۰۲۴ برای کاربر عادیPermission deniedمیدهند. ssh در Ubuntu جدید با socket activation است.
| دستور | کاری که میکند |
|---|---|
ss -tulpn | همهی گوشدهندهها با پروسه (sudo برای همه) |
ss -tlnp 'sport = :22' | چه کسی پورت ۲۲ را دارد |
ss -tn state established | اتصالهای برقرار |
lsof -i :80 / fuser 80/tcp | صاحب پورت |
curl -sI http://host | سرآیندهای HTTP |
curl -s -o /dev/null -w '%{http_code}' URL | فقط کد وضعیت |
nc -zv -w 2 host 22 | آیا پورت TCP باز است؟ |
nc -zv -w 1 host 20-25 | یک بازه |
getent services 443/tcp | اسم سرویس یک پورت |
cat /etc/services | فهرست پورتهای معروف |