python socket学习

1. Python Socket编程简介

Socket通常也称作”套接字”,应用程序通常通过”套接字”向网络发出请求或者应答网络请求。

三种流行的套接字类型是:stream,datagram和raw。stream和datagram套接字可以直接与TCP协议进行接口,而raw套接字则接口到IP协议。

Python Socket模块提供了对低层BSD套接字样式网络的访问,使用该模块建立具有TCP和流套接字的简单服务器。详见https://docs.python.org/2/library/socket.html
2. Python Socket Server

实现代码如下

# -*- coding:utf-8 -*-
from socket import *

def SocketServer():
    try:
        Colon = ServerUrl.find(':')
        IP = ServerUrl[0:Colon]
        Port = int(ServerUrl[Colon+1:])

        #建立socket对象
        print 'Server start:%s'%ServerUrl
        sockobj = socket(AF_INET, SOCK_STREAM)
        sockobj.setsockopt(SOL_SOCKET,SO_REUSEADDR, 1)

        #绑定IP端口号
        sockobj.bind((IP, Port))
        #监听,允许5个连结
        sockobj.listen(5)

        #直到进程结束时才结束循环
        while True:
            #等待client连结
            connection, address = sockobj.accept( )
            print 'Server connected by client:', address
            while True:
                #读取Client消息包内容
                data = connection.recv(1024)
                #如果没有data,跳出循环
                if not data: break
                #发送回复至Client
                RES='200 OK'
                connection.send(RES)
                print 'Receive MSG:%s'%data.strip()
                print 'Send RES:%s\r\n'%RES
            #关闭Socket
            connection.close( )

    except Exception,ex:
        print ex

ServerUrl = "192.168.16.15:9999"
SocketServer()

注:需要注意的是Socket对象建立后需要加上sockobj.setsockopt(SOL_SOCKET,SO_REUSEADDR, 1),否则会出现Python脚本重启后Socket Server端口不会立刻关闭,出现端口占用错误。
3. Python Socket Client

实现代码如下

# -*- coding:utf-8 -*-
from socket import *

def SocketClient():
    try:
        #建立socket对象
        s=socket(AF_INET,SOCK_STREAM,0)

        Colon = ServerUrl.find(':')
        IP = ServerUrl[0:Colon]
        Port = ServerUrl[Colon+1:]

        #建立连接
        s.connect((IP,int(Port)))
        sdata='GET /Test HTTP/1.1\r\n\
Host: %s\r\n\r\n'%ServerUrl

        print "Request:\r\n%s\r\n"%sdata
        s.send(sdata)
        sresult=s.recv(1024)

        print "Response:\r\n%s\r\n" %sresult
        #关闭Socket
        s.close()
    except Exception,ex:
        print ex

ServerUrl = "192.168.16.15:9999"
SocketClient()

3. 运行结果

Socket Server端运行截图如下:

Socket-Server1

Socket Client端运行截图如下:

Socket-Client

利用python的socket库模拟post、put、delete请求

HTTP1.1中有8种请求方式,包括GET、POST、HEAD、CONNECT、TRACE、OPTIONS、PUT、DELETE,其中前四种是最常用的。

今天用python模拟了下GET、POST、PUT、DELETE方法。

在所有的请求中,必须带有http头Host,比如请求127.0.0.1:8000,则Host头要写为Host: 127.0.0.1:8000。在每个头之间用换行来间隔,头和body之间使用两个换行来间隔。
GET方法

GET请求最简单,参数串跟在请求的url之后,用?指示参数串开始,GET请求时是不用带有body的。

一个完整的GET请求如下:

GET /?param1=a&param2=b HTTP/1.1\r\nHost: *.*.*.*\r\n\r\n

POST方法

POST请求则需要在头中声明body带的参数的类型,一般为application/x-www-form-urlencoded,并且在头中明示body数据的长度,body中的数据为urlencode过的参数串,和GET方法url问号之后的参数一样的格式

一个完整的POST请求如下:

POST / HTTP/1.1\r\nHost: *.*.*.*\r\nContent-Type: x-www-form-urlencoded\r\nContent-Length: 17\r\n\r\nparam1=a&param2=b

PUT方法

PUT请求若只是纯参数不需要带有Content-Type头,但是需要Content-Length头指明body的长度。

一个完整的PUT请求如下:

PUT / HTTP/1.1\r\nHost: *.*.*.*\r\nContent-Length: 17\r\n\r\nparam1=a&param2=b

DELETE方法

DELETE请求也同样不需要带有Content-Type头,但也需要指明body的长度。

一个完整的DELETE请求如下:

DELETE / HTTP/1.1\r\nHost: *.*.*.*\r\nContent-Length: 17\r\n\r\nparam1=a&param2=b

实例

下面是用python的socket库写的这4个方法请求实例:

import socket

param_data = 'param1=a&param2=b'
param_lenth = str(len(param_data))


request_str = '''GET /?'''+param_data+''' HTTP/1.1\r\nHost: *.*.*.*\r\n\r\n'''

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('127.0.0.1', 80))
sock.send(request_str)
data = sock.recv(4096)
sock.close()
print 'GET method fetch data:'
print data.split('\r\n\r\n')[1]


request_str = '''POST / HTTP/1.1\r\nHost: *.*.*.*\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: '''+param_lenth+'''\r\n\r\n'''+param_data

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('127.0.0.1', 80))
sock.send(request_str)
data = sock.recv(4096)
sock.close()
print 'POST method fetch data:'
print data.split('\r\n\r\n')[1]


request_str = '''PUT / HTTP/1.1\r\nHost: *.*.*.*\r\nContent-Length: '''+param_lenth+'''\r\n\r\n'''+param_data

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('127.0.0.1', 80))
sock.send(request_str)
data = sock.recv(4096)
sock.close()
print 'PUT method fetch data:'
print data.split('\r\n\r\n')[1]


request_str = '''DELETE / HTTP/1.1\r\nHost: *.*.*.*\r\nContent-Length: '''+param_lenth+'''\r\n\r\n'''+param_data

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('127.0.0.1', 80))
sock.send(request_str)
data = sock.recv(4096)
sock.close()
print 'DELETE method fetch data:'
print data.split('\r\n\r\n')[1]

server是用php写的,用于接收这四种方法发来的参数并打印,其中php://input中保存了只读的客户端请求body中的raw data,如果请求头中不包含Content-Length,则php://input的值将为空。parse_str函数的作用是将query字符串拆解开成为一个一维数组并将这个数组赋到$param这个变量中。

<?php
if ($_SERVER['REQUEST_METHOD'] === 'GET'){
    print_r($_GET);
} else {
    parse_str(file_get_contents('php://input'), $param);
    print_r($param);
}

服务与客户端简单实例:

实现客户端向服务端发送的信息,服务器收到信息后加上当前时间再返回给客户端
服务端实现:

#!/usr/bin/python
import socket, traceback, time
host = ''
port = 8000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
s.listen(1)
while 1:
    try:
        clientsock, clientaddr = s.accept()
    except KeyboardInterrupt:
        raise
    except:
        traceback.print_exc()
        continue
    try:
        print "Got connection from", clientsock.getpeername()
        while 1:
            data = clientsock.recv(4096)
            if not len(data):
                break
            clientsock.sendall("[%s] %s" % (time.ctime(),data))
    except (KeyboardInterrupt, SystemExit):
        raise
    except:
        traceback.print_exc()
try:
    clientsock.close()
except KeyboardInterrupt:
    raise
except:
    traceback.print_exc()

客户端实现:

#!/usr/bin/pythonh
import socket, ssl
host = '192.168.209.128'
port = 8000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
while True:
        data = raw_input('please input data:')
        if not data:
                break
        s.sendall(data)
        data = s.recv(4096)
        print data
ssl_sock.close()

运行测试:

[root@localhost example]# python echoclient1.py

please input data:test python

[Sun Sep 15 14:38:47 2013] test python

please input data:test 1111111111111111111111

[Sun Sep 15 14:38:52 2013] test 1111111111111111111111

please input data:

socket 多任务处理

以上代码要实现的功能是实现了,但是当你用多个客户端同时执行向服务器端发送信息的时候,就发现同一时间服务端只能处理一个连接,其他的无法正常返回。这是因为服务端的单线程实现的,当收到一个客户端请求处理的时候,就会进入阻塞状态,无法同时处理多个任务。

为了能够同时为多个客户端服务,需要能够同时处理多个网络连接的方法。可通过三种方法来实现:

forking、threading和异步I/O。

通过threading实现服务端:

#!/usr/bin/python
import socket, traceback, os, sys, time
from threading import *
host = ''
port = 8000
def handlechild(clientsock):
        print "New child", currentThread().getName()
        print "Got connection from", clientsock.getpeername()
        while 1:
                data = clientsock.recv(4096)
                if not len(data):
                        break
                clientsock.sendall("[%s] %s" % (time.ctime(),data))
        clientsock.close()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
s.bind((host, port))
s.listen(1)
while 1:
        try:
                clientsock, clientaddr = s.accept()
        except KeyboardInterrupt:
                raise
        except:
                traceback.print_exc()
                continue
        t = Thread(target = handlechild, args = [clientsock])
        t.setDaemon(1)
        t.start()

这样就可以让服务器同时处理多个任务了。

在服务端和客户端使用ssl
首先创建一个自签名证书:

openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.key

服务端:

#!/usr/bin/python
import socket, ssl, traceback, threading, sys, time
host = ''
port = 443
def Myhandlechild(clientsock):
        print "New child", threading.currentThread().getName()
        print "Got connection from", clientsock.getpeername()
        while 1:
                data = clientsock.recv(4096)
                if not len(data):
                        break
                clientsock.sendall("[%s] %s" % (time.ctime(),data))
        clientsock.close()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
s.listen(5)
while 1:
        try:
                clientsock, clientaddr = s.accept()
                connsock = ssl.wrap_socket(clientsock, server_side=True,
                                           certfile="cert.pem", keyfile="cert.key")
        except KeyboardInterrupt:
                raise
        except:
                traceback.print_exc()
                continue
        t = threading.Thread(target = Myhandlechild, args = [connsock])
        t.setDaemon(1)
        t.start()

客户端:

#!/usr/bin/pythonh
import socket, ssl
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ssl_sock = ssl.wrap_socket(s,ca_certs="cert.pem", cert_reqs = ssl.CERT_REQUIRED)
ssl_sock.connect(('127.0.0.1', 443))
while 1:
        data = raw_input('input data:')
        if not data:
                break
        ssl_sock.sendall(data)
        data = ssl_sock.recv(4096)
        print data
ssl_sock.close()

本文出自 “网络收藏夹” 博客,请务必保留此出处http://liyaoyi.blog.51cto.com/442933/1297348

发表评论

电子邮件地址不会被公开。 必填项已用*标注