خوب سوکت چیه؟
سوکت یک کانال ارتباطی بین دو برنامه هست که در پروتکل TCP یا UDP انجام میشه
برنامه معروفی که برای ارتباط سوکت بین دو کامپیوتر استفاده میشه telnet هست.
مثال دریافت صفحه یک سایت با سوکت به وسیله telnet
1 2 3 4 5 6 |
$ telnet bestical.ir 80 # press enter GET / HTTP/1.1 # press enter Host: bestical.ir # press enter twice |
بعد از مشخص کردن هاست باید دو بار کلید اینتر رو بزنید
مثال دریافت whois با سوکت به وسیله telnet
1 2 3 4 |
$ telnet whois.nic.ir 43 # press enter bestical.ir # press enter |
سرورهای هوییز در پورت ۴۳ سرویس میدن
حالا همین کار رو میخوایم با پایتون انجام بدیم
برنامه نویسی سوکت با کتابخونه socket
خوب شروع میکنیم
1 2 3 4 |
import socket from socket import AF_INET, SOCK_STREAM s = socket.socket(socket_family, socket_protocol) |
socket_family میتونه AF_INET (برای IPv4) و AF_INET6 (برای IPv6) باشه و یا AF_UNIX
socket_type هم میتونه SOCK_STREAM (برای استفاده از پروتکل TCP) و SOCK_DGRAM (برای استفاده از پروتکل UDP) باشه. دو پروتکل TCP و UDP موارد استفاده متفاوتی داره
در ۹۹ درصد مواقع خانواده AF_INET درکنار پروتکل TCP کارتون رو راه میندازه
پس اگه بخوایم یک سوکت در IPv4 با پروتکل TCP داشته باشیم
1 |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
برای اتصال به یک سرور از تابع connect باید استفاده کنیم اگه از AF_INET استفاده کنیم تابع connect دو مقدار addr و port رو میگیره
1 |
s.connect((addr,port)) |
addr میتونه نام هاست و یا آدرس آی پی باشه و port هم پورت موردنظر برای اتصال هست.
برای به دست آوردن آی پی یک هاست میتونید از تابع gethostbyname استفاده کنید
1 |
socket.gethostbyname('bestical.ir') |
خوب الان میخوایم صفحه اول یک سایت رو بگیریم
برای ارسال اطلاعات از تابع send استفاده میکنیم تابع send بایت قبول میکنه برای همین باید اطلاعات رو انکد کنید و چون utf-8 استاندارد بیشتر سرورها هست از utf-8 استفاده میکنیم. QUERY دستوری هست که میخواید بفرستید.
1 |
s.send('QUERY'.encode('utf-8')) |
برای مثال اگه بخوایم صفحه اول یک سایت رو دانلود کنیم:
1 2 3 4 5 6 7 8 9 10 |
s.connect((socket.gethostbyname("bestical.ir"), 80)) s.send("GET / HTTP/1.1\r\nHost: bestical.ir\r\n\r\n".encode("utf-8")) result = b"" while True: chunk = s.recv(4096) if not chunk: break result += chunk s.close() print(result.decode("utf-8")) |
برای دریافت صفحه اول سایت باید هدرهای بالا رو بفرستیم اگه مرورگرتون رو باز کنید و به بخش developertools برید و یک سایت رو باز کنید متوجه هدرهای بالا میشید.
ما همه دستورها رو در قالب یک رشته بزرگ فرستادیم میتونیم اونها رو جدا کنیم و تکی بفرستیم مثل نمونه زیر البته فکر میکنم فرستادن یک رشته بزرگ قابل اعتماد تر از این روش باشه.
1 2 |
s.send("GET / HTTP/1.1\r\n".encode("utf-8")) s.send("Host: bestical.ir\r\n\r\n".encode("utf-8")) |
هدر Host ضروری هست شما با ارسالی این هدر به سرور دستور میدید که یک سایت مشخص رو بده همونطور که میدونید در هر سرور تعداد زیادی سایت میتونه قرار داشته باشه.
هدرها با r\n\ از هم جدا شدن، تمامی وب سرورها وقتی این کاراکتر رو میبینن متوجه میشن که مقدار هدر تموم شده
تقریبا همه سایتها روی پورت ۸۰ اجرا هستند برای همین به پورت ۸۰ سرور سوکت میزنیم.
در مثال بالا با تابع recv به اندازه ۴۰۹۶ بایت از اطلاعات دریافت شده از سرور رو میخونیم این عدد دلبخواه هست البته خود پایتون توصیه کرده که این عدد توانی از ۲ باشه تا بهترین هماهنگی بین سخت افزار و نرم افزار باشه
یادتون نره که وقتی کارتون تموم شد سوکت رو ببندید
یک مثال دیگه
میخوایم whois یک دومین رو بگیریم برای این کار باید به سرور whois.nic.ir با سوکت وصل شیم و اطلاعات بفرستیم و نتیجه رو بگیریم
1 2 3 4 5 6 7 8 9 10 |
s.connect(("whois.nic.ir", 53)) s.send("bestical.ir\r\n".encode("utf-8")) result = b"" while True: chunk = s.recv(4096) result += chunk if not chunk: break s.close() print(result.decode("utf-8")) |
انتهای اطلاعات ارسالی باید r\n\ یا n\ بزارید وقتی این کار رو میکنید سرور هوییز متوجه میشه که اطلاعات رو باید پردازش کنه در واقع این کاراکتر مثل زدن کلید اینتر میمونه
اتصال سوکت با پروکسی
برای اینکه با پروکسی سوکت بزنید میتونید از کتابخونه pysocks استفاده کنید این کتابخونه توانایی برقرار ارتباط با پروکسی های socks و http رو داره API اون هم بسیار شبیه کتابخونه سوکت خود پایتون هست.
مثال بالا در pysocks
با دستور pip install pysocks کتابخونه pysocks رو نصب کنید
به جای proxy_server و proxy_port مقادیر خودتون رو قرار بدید
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import socks s = socks.socksocket() s.set_proxy(socks.HTTP, proxy_server, proxy_port) s.connect(("whois.nic.ir", 43)) s.send("bestical.ir\r\n".encode("utf-8")) result = b"" while True: chunk = s.recv(4096) result += chunk if not chunk: break s.close() print(result.decode("utf-8")) |
این پایان قسمت اول آموزش بود در قسمت دوم درباره ایجاد سرور سوکت توضیح خواهم داد.