2015-11-13 02:48:48

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

服务与客户端简单实例:

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

服务端实现:

[code]

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

客户端实现:

[code]

#!/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实现服务端:

[code]#!/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

服务端:

[code]#!/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()

客户端:

[code]#!/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

发表回复