Exercises — Socket programming — TCP server - client, UDP server - client in Python
4.3.28 · D4· Coding › Computer Networks › Socket programming — TCP server - client, UDP server - clien
Level 1 — Recognition
Tum parts ko naam de sako aur ek socket call padh sako.
Exercise 1.1
Har line ke liye batao TCP ya UDP, aur kyun:
- (a)
socket.socket(socket.AF_INET, socket.SOCK_STREAM) - (b)
socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - (c)
s.recvfrom(1024) - (d)
s.listen(5)
Recall Solution 1.1
- (a) TCP.
SOCK_STREAM= ek reliable, ordered byte stream. Socho bytes ka ek continuous pipe. - (b) UDP.
SOCK_DGRAM= independent datagrams (postcards). Harsendtoek self-contained packet hota hai. - (c) UDP.
recvfrom(data, addr)return karta hai — tum jaante ho kisne bheja kyunki koi fixed connection nahi hai. TCP plainrecvuse karta hai (peer already known hota hai). - (d) TCP.
listenek socket ko passive banata hai (incoming calls queue karne ke liye willing). UDP ke paas queue karne ke liye koi calls nahi hoti, isliye yeh kabhilistenuse nahi karta.
Exercise 1.2
s.bind(('127.0.0.1', 9000)) mein tuple ('127.0.0.1', 9000) ka kya matlab hai? Har part ka naam batao.
Recall Solution 1.2
'127.0.0.1'= IP address (yahan loopback address = "yahi machine").9000= port = us machine par kaun sa darwaza is program ko answer karta hai. Dono milke us socket ka local address banate hain jo woh claim karta hai. Dekho Ports and IP addressing. Client ko exactly is(IP, port)ko target karna hoga server tak pahunchne ke liye.
Exercise 1.3
accept() do cheezein return karta hai: conn, addr = s.accept(). Har ek kya hai, aur kyun conn ek naya socket hai?
Recall Solution 1.3
conn= ek brand-new socket sirf is ek client ke liye dedicated.addr= client ka(IP, port). Original listening socketsko doosre clients accept karte rehne ke liye free rehna chahiye. Isliye OS tumhe actual conversation ke liye ek alag socket deta hai. Yahi split reason hai ek server bahut saare clients serve kar sakta hai: ek listener, bahut saareconns.
Level 2 — Application
Correct call likho, correct order mein, correct types ke saath.
Exercise 2.1
Blanks fill karo ek minimal TCP server banane ke liye jo ek client accept kare aur 512 bytes tak padhe:
import socket
s = socket.socket(socket.AF_INET, ____A____)
s.bind(('127.0.0.1', 5000))
s.____B____(1)
conn, addr = s.____C____()
data = conn.____D____(512)Recall Solution 2.1
- A =
socket.SOCK_STREAM(TCP). - B =
listen— passive ho jao.1backlog hai (waiting-bench size jo page ke upar define ki gayi hai): yahan zyada se zyada 1 completed-but-unaccepted connection wait kar sakti hai. - C =
accept— block karta hai jab tak client connect nahi karta, per-client socket return karta hai. - D =
recv— stream se 512 bytes tak padho. Order matter karta hai:listense pehleacceptnahi kar sakte, aurbindse pehlelisten/acceptnahi kar sakte (pehle address chahiye).
Exercise 2.2
Ek student ne c.send("hello") likha aur TypeError: a bytes-like object is required, not 'str' mila. Ise fix karo, aur dikhao ki server received bytes ko text mein kaise convert kare.
Recall Solution 2.2
Sockets bytes carry karte hain, str nahi. Client fix karo:
c.send("hello".encode()) # or c.send(b"hello")Server par, text mein decode karo:
text = conn.recv(1024).decode() # bytes -> str"hello".encode() 5 bytes produce karta hai (UTF-8); .decode() ise reverse karta hai. Yeh sabse common beginner error hai.
Exercise 2.3
Ek UDP client likho jo 127.0.0.1:6000 par b"ping" bheje aur reply ko text ki tarah print kare.
Recall Solution 2.3
import socket
c = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
c.sendto(b"ping", ('127.0.0.1', 6000)) # address travels WITH the packet
reply, addr = c.recvfrom(1024)
print(reply.decode())
c.close()Client par koi connect, koi bind nahi — OS automatically ek ephemeral source port choose kar leta hai, aur destination sendto call mein hi hota hai. Compare karo UDP vs TCP tradeoffs se.
Level 3 — Analysis
Runtime behaviour predict karo — "TCP is a stream" family.
Exercise 3.1 (the classic)
Ek TCP client row mein teen sends karta hai:
c.sendall(b"A"); c.sendall(b"B"); c.sendall(b"C")Server ek conn.recv(1024) karta hai. Woh kya receive kar sakta hai? Possibilities list karo.
Recall Solution 3.1
TCP ek byte stream hai — yeh message boundaries preserve nahi karta. Ek single recv(1024) return kar sakta hai:
b"ABC"(teeno coalesce ho gaye — localhost par sabse common), yab"A", phir baad meinb"BC", yab"AB", phirb"C", yab"A",b"B",b"C"teenrecvcalls mein. Jo tum kabhi nahi dekhoge woh hai bytes out of order ya missing (TCP order + delivery guarantee karta hai). Bytes hameshaA B Csequence mein aate hain — sirf chunking vary karta hai. Isliye tumhe Message framing / length-prefix protocol chahiye.
Exercise 3.2
Wahi teen sends, lekin UDP ke saath teen sendto(b"A"), sendto(b"B"), sendto(b"C"). Server recvfrom(1024) loop karta hai. Woh kya padh sakta hai, aur kis order mein?
Recall Solution 3.2
UDP message boundaries preserve karta hai lekin delivery ya order ke baare mein kuch guarantee nahi karta:
- Har
recvfromexactly ek poora datagram return karta hai: tumb"A",b"B",b"C"individually paoge — ek call se kabhib"ABC"nahi milega. - Lekin inme se koi bhi lost, duplicate, ya reorder ho sakta hai (jaise tum
C,A,Bpadh sakte ho, ya sirfA,C). Boundary-preserving lekin unreliable — TCP ke exact opposite trade. Dekho UDP vs TCP tradeoffs.
Exercise 3.3
Ek TCP loop mein, conn.recv(1024) achanak b'' (empty bytes) return karta hai. Tumhara loop CPU pegging karte hue forever chalta rehta hai. Diagnose karo aur fix karo.
Recall Solution 3.3
TCP mein, recv ka b'' return karna "empty data" nahi hai — yeh EOF hai: peer ne connection close kar diya hai (orderly shutdown). Bina check kiye loop karna zero-length reads par forever spin karta hai.
Fix: b'' ko end-of-stream treat karo aur break karo:
while True:
data = conn.recv(1024)
if not data: # b'' -> peer closed
break
handle(data)
conn.close()Level 4 — Synthesis
Parts se ek chhota protocol banao.
Exercise 4.1 — Length-prefix framing
Kyunki TCP mein koi message boundaries nahi hain (Exercise 3.1), ek framing scheme design karo aur ek recv_msg(conn) likho jo exactly ek poora message padhe. Fixed 4-byte big-endian length prefix use karo.
Neeche figure idea dikhati hai: wire par har message ek orange header hota hai uske teal payload ke baad. Figure ko left-to-right padho jaise receiver bytes arrive hote dekhta hai.

Blocks ke neeche do arrows trace karo: receiver pehle 4 orange header bytes padhta hai length N jaanne ke liye (yahan N = 300), phir exactly N teal payload bytes padhta hai. Kyunki use pehle se N pata hai, yeh hamesha correct byte par rukta hai chahe TCP ne underlying recvs split ya merge kiye hon.
Recall Solution 4.1
Scheme: har message = [4-byte length N][N bytes of payload]. Receiver pehle 4 bytes padhta hai, N jaanta hai, phir exactly N aur bytes padhta hai (loop karte hue, kyunki har recv kam return kar sakta hai). Yeh upar figure mein two-step trace se match karta hai.
Sender:
import struct
def send_msg(conn, payload: bytes):
header = struct.pack('>I', len(payload)) # '>I' = big-endian unsigned 4-byte int
conn.sendall(header + payload)Receiver — pehle ek robust "read exactly n bytes" helper:
def recv_exact(conn, n):
buf = b''
while len(buf) < n:
chunk = conn.recv(n - len(buf))
if not chunk: # b'' -> peer closed early
raise ConnectionError("peer closed mid-message")
buf += chunk
return buf
def recv_msg(conn):
header = recv_exact(conn, 4)
(length,) = struct.unpack('>I', header) # 4 bytes -> int
return recv_exact(conn, length)Kyun kaam karta hai: length humein exactly batati hai ki is message mein kitne payload bytes belong karte hain, isliye coalescing/splitting ab humein confuse nahi karti — hum hamesha sahi byte par rukते हैं. Dekho Message framing / length-prefix protocol.
Numeric check (decimal aur hex mein — hex page ke upar introduce ki gayi thi): 300-byte payload ka header struct.pack('>I', 300) hai, jo figure mein dikhaye gaye 4 bytes hain. Plain decimal mein, : isliye teesra byte 1 hai aur chautha byte 44 hai. Un chaar byte-values ko hexadecimal mein likhne par (do hex digits per byte) 00 00 01 2C milta hai, kyunki 44 base 16 mein 0x2C hai (). Neeche verified hai.
Exercise 4.2 — Crash ke baad port reuse karo
Tumhara server crash karta hai aur tum immediately restart karte ho; bind OSError: [Errno 98] Address already in use throw karta hai. Explain karo kyun, aur ek-line fix do.
Recall Solution 4.2
Ek TCP connection close hone ke baad, OS local (IP, port) ko TIME_WAIT state mein ~1–2 minutes ke liye rakhta hai taaki purani connection ke stray late packets catch ho sakein. Us exact port par fresh bind is window ke dauran refuse kar diya jaata hai.
Fix — bind se pehle SO_REUSEADDR set karo:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', 9000))Yeh OS ko batata hai "ek address jo TIME_WAIT mein stuck hai use reuse karna theek hai." Details: TIME_WAIT and SO_REUSEADDR.
Exercise 4.3 — Raw IP ki jagah DNS
c.connect(('127.0.0.1', 9000)) ko rewrite karo taaki client host name "example.com" se port 80 par connect kare. Kaun si layer naam ko IP mein convert karti hai?
Recall Solution 4.3
c.connect(('example.com', 80))connect internally naam ko DNS ke through resolve karta hai (ya tum explicitly socket.gethostbyname('example.com') se kar sakte ho, jo ek IPv4 string return karta hai). DNS woh phone book hai jo human names → IP addresses map karta hai; dekho DNS. Port 80 HTTP ka well-known port hai.
Level 5 — Mastery
Edge cases, concurrency, aur poore system ke baare mein reasoning.
Exercise 5.1 — Ek single-accept server sirf ek hi client kyun serve karta hai?
Worked Example 1 server accept() ek baar call karta hai, client handle karta hai, phir close() karta hai aur exit karta hai. Tum bahut saare clients ko ek ke baad ek kaise serve karoge? Aur threads ke bina unhe ek saath kaise serve karoge?
Recall Solution 5.1
Bahut saare sequentially serve karo: accept + handle ko loop mein wrap karo, aur listening socket ko close mat karo:
while True:
conn, addr = s.accept() # blocks for each new client
data = conn.recv(1024)
conn.sendall(b"echo: " + data)
conn.close() # close the per-client socket, keep s openThreads ke bina bahut saare concurrently serve karo: ek event loop use karo jo bahut saare sockets watch kare aur sirf ready wale par wake kare — select ya asyncio. Ek process, bahut saare live conns, koi blocking accept show monopolize nahi karta. Dekho select / asyncio for concurrent servers.
Key insight: listening socket s aur har connection socket conn alag objects hain — yahi separation ek server ko clients multiplex karne deta hai.
Exercise 5.2 — Zero-length datagram
Ek UDP client c.sendto(b"", ('127.0.0.1', 6000)) run karta hai. Kya server ka recvfrom fire hoga? Woh kya return karta hai?
Recall Solution 5.2
Haan — ek zero-length UDP datagram ek real, valid packet hai. Server ka recvfrom(1024) unblock hota hai aur (b'', addr) return karta hai: empty payload, lekin ek real sender address.
TCP se compare karo, jahan recv ka b'' return karna matlab hai peer ne connection close kar diya. UDP mein b'' ka matlab sirf "ek empty postcard aaya" — close karne ke liye koi connection nahi hai. Same-looking value, opposite meaning: yeh ek favourite exam trap hai.
Exercise 5.3 — Three-way handshake ka cost
TCP connect free nahi hai: tumhara pehla byte move hone se pehle, packets fly karte hain. Us exchange ka naam batao, messages gino, aur explain karo UDP immediately kyun send kar sakta hai.
Recall Solution 5.3
TCP three-way handshake se connection setup karta hai: SYN → SYN-ACK → ACK = koi bhi data se pehle 3 messages. Isliye TCP ek "phone call" hai (dial, ring, pick up). Dekho TCP three-way handshake.
UDP connectionless hai: koi handshake nahi, isliye client ka pehla sendto already data carry karta hai — 0 setup round-trips. Wahi saved latency exactly reason hai DNS, gaming, aur voice UDP use karte hain (UDP vs TCP tradeoffs).
Exercise 5.4 — Backlog reasoning
s.listen(5) mein 5 kya bound karta hai, aur 6th simultaneous pending connection ka kya hoga agar server ne abhi accept call nahi kiya hai?
Recall Solution 5.4
Page ke upar backlog definition yaad karo: yeh us chhoti "waiting bench" ki size hai connections ki jo connecting finish kar chuki hain lekin abhi accept nahi hui hain. Isliye 5 us queue ko bound karta hai — zyada se zyada 5 completed-but-unaccepted connections wait kar sakti hain.
6th ka kya hota hai? Agar 6th client apna connection complete karta hai jab queue already full hai aur server ne use accept se drain nahi kiya hai, toh OS us connection ko refuse ya drop kar sakta hai. Client phir ek connection error dekhta hai (ya uska TCP stack silently retry karta hai).
Important: backlog total ya concurrent clients limit nahi karta — yeh sirf bursts ke liye ek buffer hai. Jab tak tumhara server accept call karta raha, bench empty hoti rahegi aur naye clients succeed karte rahenge. Actually bahut saare clients serve karne ke liye tum apna accept-loop / concurrency model fix karte ho (Exercise 5.1), yeh number nahi.
Aage kahan jaana hai
Ab tum poori ladder climb kar chuke ho: calls ka naam lena (L1) → unhe bytes not str ke saath likhna (L2) → TCP-stream vs UDP-datagram behaviour predict karna (L3) → framing + port reuse + DNS banana (L4) → concurrency, EOF, handshakes, aur backlog ke baare mein reason karna (L5).
Yahan se natural follow-ups:
- Exercise 4.1 ki framing ko ek full request/response protocol mein turn karo → Message framing / length-prefix protocol.
- Blocking ke bina bahut saare clients ek saath serve karo → select / asyncio for concurrent servers.
connect/closeke peeche handshake aur shutdown states samjho → TCP three-way handshake aur TIME_WAIT and SO_REUSEADDR.- Zoom out karo ki har protocol kab choose karein → UDP vs TCP tradeoffs.
Recall Poori ladder ka one-line summary
Calls ka naam lo (L1) → unhe order mein bytes not str ke saath likho (L2) → predict karo ki TCP streams / UDP datagrams alag behave karte hain (L3) → framing + port reuse + DNS banao (L4) → concurrency, EOF, handshakes, backlog ke baare mein reason karo (L5).