您的位置:首页 > 编程语言 > Python开发

python network programming tutorial

2013-10-07 21:26 561 查看
关于网络编程以及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 sockets
import sys  #for exit
import struct
import time

 
#create an INET, STREAMing socket
try:

    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 server
s.connect((remote_ip , port))

 
print 'Socket Connected to ' + host + ' on ip ' + remote_ip

 
#Send some data to remote server
message = "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 print
print recv_timeout(s)

 
#Close the socket
s.close()
需要注意的是也许http 响应数据比较大,要经过多次才能完整接收,设置socket 非阻塞,设定timeout,最后join 数据;因为我们并不知道具体数据到底多大,故不能这样使用 datasock.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 interfaces
PORT = 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 client
while 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 interfaces
PORT = 8888 # Arbitrary non-privileged port
 

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'

 
#Bind socket to local host and port
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'

 
#Start listening on socket
s.listen(10)
print 'Socket now listening'

 
#Function for handling connections. This will be used to create threads
def 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 client
while 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 clients
def 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 < len(response):

                responses[sock] = response[n:]

            else:

                poll.modify(sock, select.POLLIN)

                requests[sock] = ''

    

客户端需要发送launcelot.qa 其中一个问题,然后server 索引到答案发回给客户端。

参考:

http://www.binarytides.com/

《Foundations of Python Network Programming》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: