Python socket 开发(一)

socket介绍

今天利用周末闲来无事,对以前学习的socket模块作一个总结。话说今天成都天气不错,呵呵。。扯远了

那今天就来说说socket ,那什么是socket呢?百度百科里说的很好,socket 又叫”套接字“以下来源于百度百科

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一 般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原 意那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务
我们常用的一些网络通信模块如ftplib,ulrlib 等都是在原始套接字上做了一个上层的封装,底层还是使用socket方式来进行通信的。标准协议通信端口(0-1023),如80,21,20,22等都是系统保留端口,所以我们在编写socket 服务器时不要使用这些标准协议,也免报错。
以下是socket (C/S)通信过程
d000baa1cd11728b45647b06cafcc3cec3fd2c4c

好了,基本上我们了解了下socket,现在我们就来用python来编写socket,下面是我们就来编写一个基本的socket实例,以下模块说明来源参考《python编程》及http://blog.csdn.net/rebelqsp/article/details/22109925

socket 调用:

socket类型 描述
socket.AF_UNIX 只能够用于单一的Unix系统进程间通信
socket.AF_INET 服务器之间网络通信
socket.AF_INET6 IPv6
socket.SOCK_STREAM 流式socket , for TCP
socket.SOCK_DGRAM 数据报式socket , for UDP
socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_SEQPACKET 可靠的连续数据包服务
创建TCP Socket: s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
创建UDP Socket: s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

 

使用方法:

socket函数 描述
服务端socket函数
s.bind(address) 将套接字绑定到地址, 在AF_INET下,以元组(host,port)的形式表示地址.
s.listen(backlog) 开始监听TCP传入连接。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
s.accept() 接受TCP连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
客户端socket函数
s.connect(address) 连接到address处的套接字。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
s.connect_ex(adddress) 功能与connect(address)相同,但是成功返回0,失败返回errno的值。
公共socket函数
s.recv(bufsize[,flag]) 接受TCP套接字的数据。数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
s.send(string[,flag]) 发送TCP数据。将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。
s.sendall(string[,flag]) 完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
s.recvfrom(bufsize[.flag]) 接受UDP套接字的数据。与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
s.sendto(string[,flag],address) 发送UDP数据。将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
s.close() 关闭套接字。
s.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
s.getsockname() 返回套接字自己的地址。通常是一个元组(ipaddr,port)
s.setsockopt(level,optname,value) 设置给定套接字选项的值。
s.getsockopt(level,optname[.buflen]) 返回套接字选项的值。
s.settimeout(timeout) 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
s.gettimeout() 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
s.fileno() 返回套接字的文件描述符。
s.setblocking(flag) 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
s.makefile() 创建一个与该套接字相关连的文件

 

 

实例一(基本socket通信)

server端

#!/usr/bin/env python
#coding:utf8
#description: this is a test socket server 
__author__ = "luodi"


from socket import *

myHost=''
myPort=50007

socketobj = socket(AF_INET, SOCK_STREAM)
socketobj.bind((myHost, myPort))
socketobj.listen(5)


while True:
    connec, address = socketobj.accept()
    print('Server connected by:', address)
    while True:
        data = connec.recv(1024)
        if not data: break
        connec.sendall(data)
    connec.close()

客户端:

#!/usr/bin/env python
#coding:utf8
#description:this is a test socket clinet
__author__ = 'luodi'

import sys
import time
from socket import *
serverHost = '127.0.0.1'
serverPort = 50007

socketobj= socket(AF_INET, SOCK_STREAM)
socketobj.connect((serverHost, serverPort))

while True:
    message = 'Hello roddy welcome to www.roddypy.com'
    socketobj.send(message)
    data = socketobj.recv(1024)
    time.sleep(2)
    print('client received:',data)

socketobj.close()

 

执行结果,客户端每隔2秒发送"Hello roddy welcome to www.roddypy.com"到服务端,服务端将信息回复给客户端

 

[root@localhost python-socket]# python socket-client.py 
('client received:', 'Hello roddy welcome to www.roddypy.com')
('client received:', 'Hello roddy welcome to www.roddypy.com')
('client received:', 'Hello roddy welcome to www.roddypy.com')
('client received:', 'Hello roddy welcome to www.roddypy.com')
('client received:', 'Hello roddy welcome to www.roddypy.com')
('client received:', 'Hello roddy welcome to www.roddypy.com')
('client received:', 'Hello roddy welcome to www.roddypy.com')
('client received:', 'Hello roddy welcome to www.roddypy.com')
('client received:', 'Hello roddy welcome to www.roddypy.com')
('client received:', 'Hello roddy welcome to www.roddypy.com')
('client received:', 'Hello roddy welcome to www.roddypy.com')

OK,那我们现在再来启动一个客户端试试

 

[root@localhost ~]# cd /root/python-roddy/python-socket/
[root@localhost python-socket]# ls
socket-client.py  socket_server.py
[root@localhost python-socket]# python socket-client.py 



发现一个问题没有任何返回,那是因为目前有一个客户端正在和socket服务端之间进行信通,第二个客户端目前就处于挂起(阻塞)状态,此时如果中断第一个客户端,第二个客户端就可以通信了

那如何解决这个问题呢?可以用“派生并行”的方式?其实就是socket server利用 多线程方式处理客户端的请求,从而进行通信。python 多线程模块可以使用Threading ,multiprocessing等来进行实现。好了,我们现在就用multiprocessing来解决这个问题

实例二(多线程socket服务器)

 

#!/usr/bin/env python
#coding:utf8
#description: this is a test socket server 
__author__ = "luodi"


from socket import *

import multiprocessing

myHost=''
myPort=50007
socketobj = socket(AF_INET, SOCK_STREAM)
socketobj.bind((myHost, myPort))
socketobj.listen(5)

def socketserver(socketobj):
    while True:
        connec, address = socketobj.accept()
        print('Server connected by:', address)
        while True:
            data = connec.recv(1024)
            if not data: break
            connec.sendall(data)
        connec.close()

if __name__ == '__main__':
    jobs = []
    numclients = 8
    for client in range(numclients):
        p = multiprocessing.Process(target=socketserver, args=(socketobj,))
        jobs.append(p)
        p.start()

 

服务端生成了8个线程等待客户端进行连接,这个目前不适合在大的应用中,如果客户端超过8个,将会阻塞。

 

[root@localhost python-socket]# ps -elf | grep client
0 S root     45389 44840  0  80   0 - 41418 poll_s 19:02 pts/6    00:00:00 python socket-client.py
0 S root     45408 44322  0  80   0 - 41418 poll_s 19:03 pts/3    00:00:00 python socket-client1.py
0 S root     45456 45417  0  80   0 - 41418 poll_s 19:04 pts/7    00:00:00 python socket-client.py
0 S root     45458 45417  0  80   0 - 41418 poll_s 19:04 pts/7    00:00:00 python socket-client.py
0 S root     45460 45417  0  80   0 - 41418 poll_s 19:04 pts/7    00:00:00 python socket-client.py
0 S root     45462 45417  0  80   0 - 41418 poll_s 19:04 pts/7    00:00:00 python socket-client.py
0 S root     45463 45417  0  80   0 - 41418 poll_s 19:04 pts/7    00:00:00 python socket-client.py
0 S root     45466 45417  0  80   0 - 41418 poll_s 19:04 pts/7    00:00:00 python socket-client.py
0 S root     45525 45472  0  80   0 - 28164 pipe_w 19:05 pts/9    00:00:00 grep --color=auto client
[root@localhost python-socket]#

 

[root@localhost python-socket]# python socket-client.py <==客户端阻塞了 




其实python有一个模块叫socketserver,这个模块可以实现各种分支和多线程服务,不需要开发者手动去维护线程池,好了,跑步去了,长肉了。下篇文章我们来研究下socketserver