python network programming tutorial

关于网络编程以及socket 等一些概念和函数介绍就不再重复了,这里示例性用python 编写客户端和服务器端。

一、最简单的客户端流程:

1. Create a socket 2. Connect to remote server 3. Send some data 4. Receive a reply

#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

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()

三、上述程序的缺点是每个连接上来就回应一次就不再搭理了,显然不可取,用多线程改进如下:

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]);

#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

#!/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

# 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》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏云计算与大数据

研发:jenkins 持续集成如何打tag

uild Timestamp Plugin will be the Best Answer to get the TIMESTAMPS in the Build...

22040
来自专栏牛客网

网易一面面经

自我介绍 TCP三次握手和四次挥手的过程 为什么是三次握手 JDK动态代理 如果没有实现接口使用什么?(CGLIB) NIO和BIO区别 HashMap Con...

43390
来自专栏码匠的流水账

使用RSQL实现端到端的动态查询

RSQL(RESTful Service Query Language)是Feed Item Query Language (FIQL) 的超集,是一种REST...

9700
来自专栏后台开发+音视频+ffmpeg

dpvs源码分析

dpvs是爱奇艺开源的,它是一款基于dpdk的高性能4层负载均衡器。源自于LVS和改版后的alibaba/LVS. dpvs即dpdk-lvs. 等多关于dpv...

1.3K20
来自专栏转载gongluck的CSDN博客

第25章 信号驱动式I/O

(1)建立SIGIO信号的信号处理函数 (2)设置套接字属主 (3)开启套接字的信号驱动式I/O标志 ? ? #include "../Gnet.h" int...

41550
来自专栏jeremy的技术点滴

SSM项目脚手架

54140
来自专栏bboysoul

渗透测试工具(KatanaFramework)

katana是一个用python写的渗透测试框架,基于一个简单而全面的架构,供任何人来使用,修改和分享。

14410
来自专栏软件工程师成长笔记

Mybatis分页插件PageHelper的使用详解

如果你也在用Mybatis,建议尝试该分页插件,个人感觉这个是最方便,超好用的分页插件。 该插件目前支持Oracle,Mysql,MariaDB,SQLite...

1.5K30
来自专栏向治洪

Android 应用安装过程分析

在之前的文章中,我们对PakageManagerService启动流程分析 做了简单的介绍,并对PMS系统的启动流程做了详细的解析。上面只是说到了Android...

78590
来自专栏一个默默无闻的工程师的日常

centos7 devstack 安装openstack liberty

19330

扫码关注云+社区

领取腾讯云代金券