关于网络编程以及socket 等一些概念和函数介绍就不再重复了,这里示例性用python 编写客户端和服务器端。
一、最简单的客户端流程:
1. Create a socket
2. Connect to remote server
3. Send some data
4. Receive a reply
Python Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#Socket client example in python
import socket #for socketsimport sys #for exitimport struct
import time
#create an INET, STREAMing sockettry:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error:
print 'Failed to create socket'
sys.exit()
print 'Socket Created'
host = 'www.google.com';
port = 80;
try:
remote_ip = socket.gethostbyname( host )
except socket.gaierror:
#could not resolve print 'Hostname could not be resolved. Exiting'
sys.exit()
#Connect to remote servers.connect((remote_ip , port))
print 'Socket Connected to ' + host + ' on ip ' + remote_ip
#Send some data to remote servermessage = "GET / HTTP/1.1\r\n\r\n"
try :
#Set the whole string s.sendall(message)
except socket.error:
#Send failed print 'Send failed'
sys.exit()
print 'Message send successfully'
def recv_timeout(the_socket,timeout=2):
#make socket non blocking the_socket.setblocking(0)
#total data partwise in an array total_data=[];
data='';
#beginning time begin=time.time()
while 1:
#if you got some data, then break after timeout if total_data and time.time()-begin > timeout:
break
#if you got no data at all, wait a little longer, twice the timeout elif time.time()-begin > timeout*2:
break
#recv something try:
data = the_socket.recv(8192)
if data:
total_data.append(data)
#change the beginning time for measurement begin=time.time()
else:
#sleep for sometime to indicate a gap time.sleep(0.1)
except:
pass
#join all parts to make final string return ''.join(total_data)
#get reply and printprint recv_timeout(s)
#Close the sockets.close()
需要注意的是也许http 响应数据比较大,要经过多次才能完整接收,设置socket 非阻塞,设定timeout,最后join 数据;因为我们并不知道具体数据到底多大,故不能这样使用 data. s.recv(4096 , socket.MSG_WAITALL); 如果最后一次来的数据不够4096,那么将一直阻塞。输出如下:
二、最简单的服务器端流程:
1. Open a socket
2. Bind to a address(and port).
3. Listen for incoming connections.
4. Accept connections
5. Read/Send
Python Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import socket
import sys
HOST = '' # Symbolic name meaning all available interfacesPORT = 8888 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
try:
s.bind((HOST, PORT))
except socket.error , msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
sys.exit()
print 'Socket bind complete'
s.listen(10)
print 'Socket now listening'
#now keep talking with the clientwhile 1:
#wait to accept a connection - blocking call conn, addr = s.accept()
print 'Connected with ' + addr[0] + ':' + str(addr[1])
data = conn.recv(1024)
reply = 'OK...' + data
if not data:
break
conn.sendall(reply)
conn.close()
s.close()
三、上述程序的缺点是每个连接上来就回应一次就不再搭理了,显然不可取,用多线程改进如下:
Python Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import socket
import sys
from thread import *
HOST = '' # Symbolic name meaning all available interfacesPORT = 8888 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
#Bind socket to local host and porttry:
s.bind((HOST, PORT))
except socket.error , msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
sys.exit()
print 'Socket bind complete'
#Start listening on sockets.listen(10)
print 'Socket now listening'
#Function for handling connections. This will be used to create threadsdef clientthread(conn):
#Sending message to connected client conn.send('Welcome to the server. Type something and hit enter\n') #send only takes string
#infinite loop so that function do not terminate and thread do not end. while True:
#Receiving from client data = conn.recv(1024)
reply = 'OK...' + data
if not data:
break
conn.sendall(reply)
#came out of loop conn.close()
#now keep talking with the clientwhile 1:
#wait to accept a connection - blocking call conn, addr = s.accept()
print 'Connected with ' + addr[0] + ':' + str(addr[1])
#start new thread takes 1st argument as a function name to be run, second is the tuple of arguments to the function. start_new_thread(clientthread ,(conn,))
s.close()
即每accept 返回一个连接,就创建一个线程对其服务。
启动server,然后开两个窗口telnet 上去,如下:
四、接下来,我们再用select 来实现,函数原型如下:
read_sockets,write_sockets,error_sockets = select(read_fds , write_fds, except_fds [, timeout]);
Python Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#Socket server in python using select function
import socket, select
#Function to broadcast chat messages to all connected clientsdef broadcast_data(sock, message):
#Do not send the message to master socket and the client who has send us the message for socket in CONNECTION_LIST:
if socket != server_socket and socket != sock:
try:
socket.send(message)
except:
#broken socket connection may be, chat client pressed ctrl+c for example socket.close()
CONNECTION_LIST.remove(socket)
if __name__ == "__main__":
CONNECTION_LIST = [] #list of socket clients RECV_BUFFER = 4096 #Advisable to keep it as an exponent of 2 PORT = 5000
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#this has no effect, why? server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(("0.0.0.0", PORT))
server_socket.listen(10)
#Add server socket to the list of readable connections CONNECTION_LIST.append(server_socket)
print "Chat server started on port " + str(PORT)
while 1:
#Get the list sockets which are ready to be read through select read_sockets, write_sockets, error_sockets = select.select(CONNECTION_LIST, [], [])
for sock in read_sockets:
#New connection if sock == server_socket:
#handle the case in which there is a new connection recieved through server_socket sockfd, addr = server_socket.accept()
CONNECTION_LIST.append(sockfd)
print "Client (%s, %s) connected" % addr
#Some incoming message from a client else:
#Data recieved from client, process it try:
#In windows, sometimes when a Tcp program closes abruptly #a "Connection reset by peer" exception will be thrown data = sock.recv(RECV_BUFFER)
#echo back the client message if data:
sock.send('Ok...' + data)
#client disconnected, so remove from socket list except:
broadcast_data(sock, "Client (%s, %s) is offline" % addr)
print "Client (%s, %s) is offline" % addr
sock.close()
CONNECTION_LIST.remove(sock)
continue
server_socket.close()
五、最后使用poll 来实现,如下:
launcelot.py
Python Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python
#coding=utf-8
#Constants and routines for supporting a certain network conversation.import sys, socket
PORT = 1060
qa = (('What is your name?', 'My name is Sir Launcelot of Camelot.'),
('What is your quest?', 'To seek the Holy Grail.'),
('What is your favorite color?', 'Blue.'))
qadict = dict(qa)
def recv_until(sock, suffix):
message = ''
while not message.endswith(suffix):
data = sock.recv(4096)
if not data:
raise EOFError('socket closed before we saw %r' % suffix)
message += data
return message
def setup():
if len(sys.argv) != 2:
print >>sys.stderr, 'usage: %s interface' % sys.argv[0]
exit(2)
interface = sys.argv[1]
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((interface, PORT))
sock.listen(128)
print 'Ready and listening at %r port %d' % (interface, PORT)
return sock
poll_server.py
Python Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# An event-driven approach to serving several clients with poll().import launcelot
import select
listen_sock = launcelot.setup()
sockets = {listen_sock.fileno():listen_sock}
requests = {}
responses = {}
poll = select.poll()
poll.register(listen_sock, select.POLLIN)
while True:
for fd, event in poll.poll():
sock = sockets[fd]
#Removed closed sockets from our list. if event & (select.POLLHUP | select.POLLERR | select.POLLNVAL):
poll.unregister(fd)
del sockets[fd]
requests.pop(sock, None)
responses.pop(sock, None)
#Accept connections from new sockets. elif sock is listen_sock:
newsock, sockname = sock.accept()
newsock.setblocking(False)
fd = newsock.fileno()
sockets[fd] = newsock
poll.register(fd, select.POLLIN)
requests[newsock] = ''
#Collect incoming data until it forms a question. elif event & select.POLLIN:
data = sock.recv(4096)
if not data: #end of file sock.close() # make POLLNVAL happen next time continue
requests[sock] += data.replace('\r\n', '')
if '?' in requests[sock]:
question = requests.pop(sock)
answer = dict(launcelot.qa)[question]
poll.modify(sock, select.POLLOUT)
responses[sock] = answer
#Send out pieces of each reply until they are all sent elif event & select.POLLOUT:
response = responses.pop(sock)
n = sock.send(response)
if n
responses[sock] = response[n:]
else:
poll.modify(sock, select.POLLIN)
requests[sock] = ''
客户端需要发送launcelot.qa 其中一个问题,然后server 索引到答案发回给客户端。
参考:
《Foundations of Python Network Programming》