เรื่องราวของการสื่อสาร
เคยสงสัยไหมว่าอินเทอร์เน็ต "พูด" กันอย่างไร? คอมพิวเตอร์เครื่องหนึ่ง "คุย" กับอีกเครื่องผ่านอินเทอร์เน็ตอย่างไร?
มนุษย์สื่อสารด้วยคำต่อเป็นประโยคที่มีความหมาย ประโยคมีความหมายเพราะเราตกลงกันในความหมาย เรากำหนด Protocol ของการสื่อสาร
คอมพิวเตอร์คุยกันบนอินเทอร์เน็ตแบบคล้ายกัน แต่เราวิ่งหน้าไป มนุษย์ใช้ปาก คอมพิวเตอร์ใช้อะไร?
เข้าสู่ Socket
Socket เป็น Concept พื้นฐานที่สุดใน Computer Science สร้าง Network อุปกรณ์เชื่อมต่อกันได้ทั้งระบบ
เหมือนทุกอย่างใน CS Socket เป็น Abstract Concept มาก กำหนด Socket คืออะไร ยากกว่ากำหนด Socket ทำอะไร
Socket ทำอะไร? ช่วย 2 Computers สื่อสารกัน ทำอย่างไร? มี 2 Methods: send() และ recv() สำหรับส่งและรับ
ดี แต send() และ recv() ส่ง/รับอะไร? มนุษย์เคลื่อนปากแลกคำ Socket ใช้ Methods แลก Bits และ Bytes
ilustrasi ด้วยตัวอย่าง มี 2 Computers A และ B Computer A พยายามพูดกับ B B พยายามฟัง A ดูหน้าตา:

อ่าน Buffer
ดูแปลกหน่อย ไหน ทั้งคู่ชี้ไป Bar กลางชื่อ 'buffer'
Buffer คืออะไร? Buffer คือ Memory Stack ที่เก็บ Data ของแต่ละ Computer Allocate โดย Kernel
ทำไมชี้ Buffer เดียวกัน? ไม่แม่นยำจริงๆ จริงๆ แต่ละ Computer มี Buffer ของตัวเอง Allocate โดย Kernel ของตัวเอง Network ขนส่ง Data ระหว่าง Buffer แยกกัน แต่ไม่ไหวพูด Network Details สมมติทั้งคู่เข้าถึง Buffer เดียว "ที่ไหนสักแห่งระหว่างว่าง"
รู้ Visual แล้ว Abstract เป็น Code
#Computer A ส่งข้อมูล computerA.send(data) #Computer B รับข้อมูล computerB.recv(1024)Code Snippet นี้ทำอย่างเดียวกับรูปข้างต้น ยกเว้น Curiosity หนึ่ง ไม่เรียก computerB.recv(data) ใส่ตัวเลขแปลกรandom แทน Data
เหตุผลง่าย Data ผ่าน Network ส่งเป็น Bits ตอน Recv ใน Computer B ระบุจำนวน Bits ที่ยอมรับในเวลาใดเวลาหนึ่ง
ทำไมเลือก 1024 Bytes? ไม่มีเหตุผลเฉพาะ มักดีที่ระบุ Bytes เป็น Power of 2 เลือก 1024 = 2¹⁰
Buffer ทำงานอย่างไร? Computer A เขียน/ส่ง Data ที่เก็บอยู่เข้า Buffer Computer B อ่าน/รับ 1024 Bytes แรกใน Buffer
สุดยอด! แต 2 Computers รู้จักคุยกันอย่างไร? Computer A เขียน Buffer รู้ไหมว่า B จะมารับ? กล่าวอีกอย่าง Connection ระหว่าง 2 Computers มี Unique Buffer อย่างไร?
Porting Into IPs

รูปข้างต้น Computer เดียวกัน เพิ่ม Detail หนึ่ง มีตัวเลขชุดใหญ่หน้าของแต่ละ Computer ตาม Bar ยาว
Bar ยาวหน้า Computer คือ Router เชื่อม Computer สู่ Internet ตัวเลขบน Bar เรียกว่า Ports Computer คุณมี Ports พันๆ ตอนนี้ แต่ละ Port อนุญาต Socket Connection รูปแสดง 6 Ports แต่เข้าใจได้
Ports ต่ำกว่า 255 Reserved สำหรับ System Calls และ Low-level Connections แนะนำเปิด Connection Port เลข 4 หลักสูง เช่น 8000 รูปไม่ได้วาด Buffer แต่แต่ละ Port มี Buffer ของตัวเอง
Bar เองก็มีหมายเลข เรียกว่า IP Address IP Address มี Ports มากมาย ผูกมือคิดแบบนี้:
127.0.0.1 / | \ / | \ / | \ 8000 8001 8002สร้าง Connection บน Port ระหว่าง Computer A และ B
# computerA.py import socket computerA = socket.socket() # Connect ไป localhost:8000 computerA.connect(('127.0.0.1', 8000)) string = 'abcd' encoded_string = string.encode('utf-8') computerA.send(encoded_string)Code สำหรับ computerB.py
# computerB.py import socket computerB = socket.socket() # Listen บน localhost:8000 computerB.bind(('127.0.0.1', 8000)) computerB.listen(1) client_socket, address = computerB.accept() data = client_socket.recv(2048) print(data.decode('utf-8'))ดูเหมือนกระโดดหน้าใน Code แต่อธิบายทีละบรรทัด มี 2 Computers A และ B ต้องการหนึ่งส่ง หนึ่งรับ
เลือก A ส่ง B รับ ผมสมมติ ในบรรทัด computerA.connect((‘127.0.0.1’, 8000) ทำ A Connect ไป Port 8000 IP 127.0.0.1
Note: 127.0.0.1 มักหมายถึง Localhost อ้างอิงเครื่องตัวเอง
Computer B Bind ไป Port 8000 IP 127.0.0.1 คิดไหมว่าทำไม IP เดียวกัน 2 Computers?
เพราะผมลักลอบ ใช้ 1 Computer Demo Sockets (Connect จากและไปเครื่องเดียวเพื่อความง่าย) ปกติ 2 Computers ต่างกัน IP ต่างกัน
รู้ว่าแค่ Bits ส่งใน Data Packet จึง Encode String ก่อนส่ง Decode String บน Computer B รัน 2 ไฟล์ Local ได้ ต้องรัน computerB.py ก่อน รัน computerA.py ก่อนจะได้ Connection Refused Error
Serving The Clients (บริการ Clients)

หลายคนคงรู้ว่าสิ่งที่อธิบายคือ Client-Server Model เรียบง่าย จากรูปข้างต้น แค่เปลี่ยน Computer A เป็น Client B เป็น Server
มี Communication Stream ตลอดระหว่าง Clients และ Servers ใน Code ก่อนหน้า Describe One-shot Data Transfer แทนที่จะเป็น Constant Stream Data จาก Client ไป Server แต่ยังอยากรู้ว่า Transfer เสร็จเมื่อไหร่ จึงรู้ว่าหยุด Listen ได้
ลอง Analogy ดูต่อไป Conversation 2 คน:

2 คนแนะนำตัว แต่ไม่พูดพร้อมกัน สมมติ Raj เริ่มก่อน John รอจน Raj จบก่อนเริ่มแนะนำตัว ตรงนี้ Based on Learned Heuristics แต่อธิบายได้ว่าเป็น Protocol
Clients และ Servers ต้องการ Protocol คล้ายกัน ไม่งั้นรู้เมื่อไหร่ถึงคาส่ง Data Packets
ทำ Simple Protocol สมมติส่ง Data เป็น Array of Strings Array คือ:
arr = ['random', 'strings', 'that', 'need', 'to', 'be', 'transferred', 'across', 'the', 'network', 'using', 'sockets']
Data นี้ Client ส่งไป Server Constraint อีกอย่าง: Server ต้องรับ Data เท่ากับ String ที่ส่งใน Instant นั้น
เช่น Client ส่ง 'random' สมมติ Char 1 ตัว = 1 Byte String ใช้ 6 Bytes = 6*8 = 48 Bits ส่ง 'random' ผ่าน Sockets Server ต้องรู้ว่า Access 48 Bits สำหรับ Packet นั้น
โอกาส Break Down Problem มี 2-3 สิ่งต้องแก้ก่อน
หาจำนวน Bytes ที่ String ใช้ใน Python ยังไง?
เริ่มหาความยาว String ก่อน ง่าย เรียก len() แต่ต้องการ Bytes ไม่ใช่แค่ความยาว
Convert String เป็น Binary ก่อน หาความยาว Binary Representation นั้นควรให้ Bytes ที่ใช้
len(‘random’.encode(‘utf-8’)) ให้สิ่งที่ต้องการ
ส่งจำนวน Bytes ของแต่ละ String ไป Server ยังไง?
ง่าย Convert จำนวน Bytes (Integer) เป็น Binary Representation ส่งไป Server Server คาดหวังรับความยาว String ก่อนรับ String จริง
Server รู้เมื่อไหร่ Client ส่ง Strings ครบทุกตัว?
จาก Analogy Conversation ต้องมีทางรู้ Transfer เสร็จ Computers ไม่มี Heuristics เอง กำหนด Rule Random: ส่ง String 'end' = Server รับครบ ปิด Connection ได้ หมายถึง 'end' ห้ามใช้ส่วนอื่นใน Array นอกจากท้ายสุด
Protocol ที่ออกแบบมา:

ความยาว String 2 Bytes ตามด้วย String จริง Variable Length ขึ้นอยู่กับ String Length ส่งก่อนหน้า สลับส่ง String Lengths และ String เอง EOT = End Of Transmission ส่ง 'end' = ไม่มี Data อีก
Note: ก่อนต่อ ต้องบอกว่า Protocol นี้ Simple และ Stupid อยากดู Well-designed Protocol ดู HTTP Protocol
Code ออกมา Comments ใน Code ด้านล่างอธิบายเอง
มี Client รันแล้ว ต้องการ Server
อธิบายบรรทัดเฉพาะใน Gists ข้างต้น บรรทัดแรกจาก clientSocket.py
len_in_bytes = (len_of_string).to_bytes(2, byteorder='little')
Convert Number เป็น Bytes Parameter แรกของ to_bytes คือ Bytes Allocate สำหรับ Result Convert len_of_string เป็น Binary
Parameter ที่ 2 เลือก Little Endian หรือ Big Endian อ่านเพิ่มได้ที่นี่ ตอนนี้ใช้ Little เสมอ
บรรทัดต่อไป:
client_socket.send(string.encode(‘utf-8’))
Convert String เป็น Binary ด้วย Encoding ‘utf-8’
ใน serverSocket.py:
data = client_socket.recv(2) str_length = int.from_bytes(data, byteorder='little')บรรทัดแรกรับ 2 Bytes Data จาก Client จำได้ไหมตอน Convert String Length เป็น Binary ใน clientSocket.py ใส่ Result ใน 2 Bytes เพราะฉะนั้นอ่าน 2 Bytes สำหรับ Data เดียวกัน
บรรทัดต่อไป Convert Binary เป็น Integer byteorder "little" Match กับ Client
รัน 2 Sockets ควรเห็น Server Print Strings ที่ Client ส่ง สื่อสารสำเร็จ!
สรุป
ครอบคลุมพอสมควร Socket คืออะไร ใช้ยังไง ออกแบบ Protocol เรียบง่าย Stupid ถ้าอยากเรียนรู้ Socket เพิ่ม อ่าน Beej's Guide To Network Programming E-book นั้นมีเนื้อหาดีมาก
นำไปประยุกต์ปัญหาซับซ้อนได้ เช่น Stream รูปจาก RaspberryPi Camera ไป Computer สนุกกับมัน!
อยาก Follow Twitter หรือ GitHub ก็ได้ Check Blog ที่นี่ ผมพร้อมตอบทุกเวลา!
Originally published at https://redixhumayun.github.io/networking/2019/02/14/how-the-internet-speaks.html on February 14, 2019.