說到socket,學過網路基礎的人都聽過,他是TCP/IP的抽象,他是網路世界的入口,它無處不在。
學會了socket編程,就意味著能夠更深層次的控制你的流量,之後再看python的urllib*或php的curl,簡直就是小兒科!
I. 一些名詞
Address Family
地址類型,協議族,可能是以下
socket.AF_INET ---> IPv4 addresses.
socket.AF_INET6 ---> IPv6 addresses.
socket.AF_UNIX ---> Unix domain sockets (例如 /var/run/mysqld/mysqld.sock is an example).
socket.AF_IPX ---> IPX addresses.
Socktype
sock類型
socket.SOCK_STREAM ---> 流式socket , for TCP
socket.SOCK_DGRAM ---> 資料報式socket , for UDP
其他
參數 |
取值 |
值 |
說明 |
Address Family |
AF_INET |
2 |
IPv4 |
AF_INET6 |
23 |
IPv6 |
AF_UNSPEC |
0 |
協議無關 |
Protocol Numbers 協議號 |
IPPROTO_IP |
0 |
IP協議 |
IPPROTO_IPV4 |
4 |
IPv4 |
IPPROTO_IPV6 |
41 |
IPv6 |
IPPROTO_UDP |
17 |
UDP |
IPPROTO_TCP |
6 |
TCP |
Socktype sock類型 |
SOCK_STREAM |
1 |
流 |
SOCK_DGRAM |
2 |
資料報 |
ai_flags |
AI_PASSIVE |
1 |
被動的,用於bind,通常用於server socket |
AI_CANONNAME |
2 |
用於返回主機的正式名稱 |
AI_NUMERICHOST |
4 |
地址為數字串 |
II. 簡單的例子
來自官方文檔的例子,很基礎。
服務端
# Echo server program
import socket
HOST = '127.0.0.1' # Symbolic name meaning all available interfaces
PORT = 50007 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr
while 1:
data = conn.recv(1024)
if not data: break
conn.sendall(data)
conn.close()
用戶端
# Echo client program
import socket
HOST = 'localhost' # The remote host
PORT = 50007 # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall('Hello, world')
data = s.recv(1024)
s.close()
print 'Received', repr(data)
III. 更複雜的例子
服務端
與之前不同的是,支援IPV6
# Echo server program
import socket
import sys
HOST = None # Symbolic name meaning all available interfaces
PORT = 50007 # Arbitrary non-privileged port
s = None
for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC,
socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
af, socktype, proto, canonname, sa = res
try:
s = socket.socket(af, socktype, proto)
except socket.error as msg:
s = None
continue
try:
s.bind(sa)
s.listen(1)
except socket.error as msg:
s.close()
s = None
continue
break
if s is None:
print 'could not open socket'
sys.exit(1)
conn, addr = s.accept()
print 'Connected by', addr
while 1:
data = conn.recv(1024)
if not data: break
conn.send(data)
conn.close()
用戶端
# Echo client program
import socket
import sys
HOST = 'localhost' # The remote host
PORT = 50007 # The same port as used by the server
s = None
for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
try:
s = socket.socket(af, socktype, proto)
except socket.error as msg:
s = None
continue
try:
s.connect(sa)
except socket.error as msg:
s.close()
s = None
continue
break
if s is None:
print 'could not open socket'
sys.exit(1)
s.sendall('Hello, world')
data = s.recv(1024)
s.close()
print 'Received', repr(data)
上面我們提到了socket編程的基本形式,這一節我們加強服務端的效能!
#!/usr/bin/env python
import socket, threading
class ClientThread(threading.Thread):
def __init__(self, ip, port, socket):
threading.Thread.__init__(self)
self.ip = ip
self.port = port
self.socket = socket
print "[+] New thread started for "+ip+":"+str(port)
def run(self):
print "Connection from : "+ip+":"+str(port)
self.socket.send("\nWelcome to the server\n\n")
data = "dummydata"
while len(data):
data = self.socket.recv(2048)
print "Client sent : "+data
self.socket.send("You sent me : "+data)
print "Client disconnected..."
host = "0.0.0.0"
port = 9999
tcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpsock.bind((host,port))
threads = []
while True:
tcpsock.listen(4)
print "\nListening for incoming connections..."
(clientsock, (ip, port)) = tcpsock.accept()
newthread = ClientThread(ip, port, clientsock)
newthread.start()
threads.append(newthread)
for t in threads:
t.join()