importsocketdeftcp_server():withsocket.socket(socket.AF_INET,socket.SOCK_STREAM)asserver:# 주소 재사용 허용 (빠른 재시작)server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)server.bind(('localhost',8080))server.listen(5)# 대기 큐 크기print('Server listening on port 8080...')whileTrue:# 클라이언트 연결 대기client,addr=server.accept()withclient:print(f'Connected by {addr}')# 데이터 수신data=client.recv(1024)ifdata:print(f'Received: {data.decode()}')# 응답 전송client.sendall(b'Hello, Client!')# tcp_server()
# 다중 클라이언트 처리 (스레드)importsocketimportthreadingdefhandle_client(client:socket.socket,addr:tuple):withclient:print(f'Connected: {addr}')whileTrue:data=client.recv(1024)ifnotdata:breakclient.sendall(data)# 에코 서버print(f'Disconnected: {addr}')defthreaded_server():withsocket.socket(socket.AF_INET,socket.SOCK_STREAM)asserver:server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)server.bind(('localhost',8080))server.listen(5)whileTrue:client,addr=server.accept()thread=threading.Thread(target=handle_client,args=(client,addr))thread.start()
importsocket# UDP 클라이언트defudp_client():withsocket.socket(socket.AF_INET,socket.SOCK_DGRAM)assock:sock.settimeout(5.0)# connect 없이 바로 전송 (비연결)sock.sendto(b'Hello, UDP Server!',('localhost',9999))# 응답 수신data,addr=sock.recvfrom(1024)print(f'Received from {addr}: {data.decode()}')# UDP 서버defudp_server():withsocket.socket(socket.AF_INET,socket.SOCK_DGRAM)assock:sock.bind(('localhost',9999))print('UDP Server listening on port 9999...')whileTrue:data,addr=sock.recvfrom(1024)print(f'Received from {addr}: {data.decode()}')sock.sendto(b'ACK',addr)
타임아웃 처리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
importsocketwithsocket.socket(socket.AF_INET,socket.SOCK_STREAM)assock:# 전체 타임아웃 설정sock.settimeout(5.0)try:sock.connect(('localhost',8080))sock.sendall(b'test')data=sock.recv(1024)exceptsocket.timeout:print('Connection timed out')exceptsocket.errorase:print(f'Socket error: {e}')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 논블로킹 모드importsocketimportselectsock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)sock.setblocking(False)try:sock.connect(('localhost',8080))exceptBlockingIOError:pass# 논블로킹에서는 바로 반환# select로 대기ready=select.select([sock],[sock],[],5.0)ifready[1]:# 쓰기 가능 = 연결됨sock.sendall(b'test')
유용한 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
importsocket# 호스트 이름 → IPip=socket.gethostbyname('example.com')print(ip)# 93.184.216.34# 서비스 이름 → 포트port=socket.getservbyname('http')print(port)# 80# 현재 호스트 이름hostname=socket.gethostname()print(hostname)# 주소 정보 조회info=socket.getaddrinfo('example.com',80)forfamily,socktype,proto,canonname,sockaddrininfo:print(sockaddr)# ('93.184.216.34', 80)
소켓 옵션
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
importsocketsock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 주소 재사용 (서버 재시작 시 유용)sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)# TCP Keepalivesock.setsockopt(socket.SOL_SOCKET,socket.SO_KEEPALIVE,1)# 버퍼 크기sock.setsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF,8192)sock.setsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF,8192)# TCP_NODELAY (Nagle 알고리즘 비활성화)sock.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,1)
sendall() vs send(): send()는 일부만 보낼 수 있음, sendall() 권장
리소스 정리: with 문 또는 try/finally로 소켓 닫기
인코딩: 문자열은 encode()/decode() 필요
포트 번호: 1024 미만은 root 권한 필요
IPv6: AF_INET6 사용, 듀얼 스택은 별도 처리 필요
고수준 대안
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 일반 HTTP 요청 → requests 라이브러리importrequestsresponse=requests.get('https://example.com')# 비동기 서버 → asyncioimportasyncioasyncdefhandle_client(reader,writer):data=awaitreader.read(100)writer.write(data)awaitwriter.drain()writer.close()asyncdefmain():server=awaitasyncio.start_server(handle_client,'localhost',8080)asyncwithserver:awaitserver.serve_forever()# 웹 서버 → Flask, FastAPI 등