socket编程了解
Socket 编程
Socket通讯原理描述:
套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象。它们允许程序接受并进行连接,如发送和接受数据。为了建立通信通道,网络通信的每个端点拥有一个套接字对象极为重要。和大多数语言一样,Python 支持面向连接和无连接,实现接口功能与步骤也大致相同。
面向连接即需要先连接然后通讯, 面向连接主要协议就是传输控制协议(tcp),要创建tcp套接字时需要指定套接字类型为 SOCK_STRAM,表达了他作为流套接字的特点。
无连接,顾名思义无需建立连接就可以进行通讯,这时数据到达顺序、可靠性就无法保证了。实现这种连接的协议就是用户数据包协议(udp)。创建UDP时需要指定套接字类型为 SOCK_DGRAM。
服务器端通常是用来处理数据的,客户端是用来请求数据的。服务端可以同时处理多条数据。
同一个80端口
TCP服务器端:
1. 第一步是创建socket对象。调用socket构造函数。如:
socket = socket.socket( family, type )
family参数代表地址家族,可为AF_INET或AF_UNIX。AF_INET家族包括Internet地址,AF_UNIX家族用于同一台机器上的进程间通信。
type参数代表套接字类型,可为SOCK_STREAM(流套接字)和SOCK_DGRAM(数据报套接字)。
2. 第二步是将socket绑定到指定地址。这是通过socket对象的bind方法来实现的:
socket.bind( address )
由AF_INET所创建的套接字,address地址必须是一个双元素元组,格式是(host,port)。host代表主机,port代表端口号。如果端口号正在使用、主机名不正确或端口已被保留,bind方法将引发socket.error异常。
3. 第三步是使用socket套接字的listen方法接收连接请求。
socket.listen( backlog )
backlog指定最多允许多少个客户连接到服务器。它的值至少为1。收到连接请求后,这些请求需要排队,如果队列满,就拒绝请求。
4. 第四步是服务器套接字通过socket的accept方法等待客户请求一个连接。
connection, address = socket.accept()
调 用accept方法时,socket会时入“waiting”状态。客户请求连接时,方法建立连接并返回服务器。accept方法返回一个含有两个元素的 元组(connection,address)。第一个元素connection是新的socket对象,服务器必须通过它与客户通信;第二个元素 address是客户的Internet地址。
5. 第五步是处理阶段,服务器和客户端通过send和recv方法通信(传输 数据)。服务器调用send,并采用字符串形式向客户发送信息。send方法返回已发送的字符个数。服务器使用recv方法从客户接收信息。调用recv 时,服务器必须指定一个整数,它对应于可通过本次方法调用来接收的最大数据量。recv方法在接收数据时会进入“blocked”状态,最后返回一个字符 串,用它表示收到的数据。如果发送的数据量超过了recv所允许的,数据会被截短。多余的数据将缓冲于接收端。以后调用recv时,多余的数据会从缓冲区 删除(以及自上次调用recv以来,客户可能发送的其它任何数据)。
6. 传输结束,服务器调用socket的close方法关闭连接
伪代码大致如下:
1 创建套接字,绑定套接字到当地地址,然后开始监听连接。就是socket,bind,listen。
2 进入循环,不断接受客户端的连接请求,然后接收传来的数据,当然也可以发送给对方数据。就是accept一个连接,然后recv数据。
3 接收完毕可以关闭套接字,close。
ss.socket(Socket.AF_INET,Socket.SOCK_STRAM) #创建服务器套接字
ss.bind() #把本地地址绑到套接字上
ss.listen() #监听连接
inf_loop: #服务器无限循环
cs=ss.accept() #接受客户端的连接
comm._loop: #通信循环
cs.recv()/cs.send() #对话
cs.close() #关闭客户套接字
ss.close() #关闭服务器套接字
TCP客户端:
1. 第一步是创建一个socket以连接服务器:socket = socket.socket( family, type )
2. 第二步是使用socket的connect方法连接服务器。对于AF_INET家族,连接格式如下:
socket.connect( (host,port) )
host代表服务器主机名或IP,port代表服务器进程所绑定的端口号。如连接成功,客户就可通过套接字与服务器通信,如果连接失败,会引发socket.error异常。
3. 第三步是处理阶段,客户和服务器将通过send方法和recv方法通信。
4. 传输结束,客户通过调用socket的close方法关闭连接。
伪代码如下:
1 创建套接字,然后连接远端地址,socket ,connect。
2 建立连接之后开始发送数据。Send(data),当然可以从缓冲区读取服务器发来的数据。Recv(BUFF)
3 完毕后,关闭套接字。Close
cs=socket(Socket.AF_INET,Socket.SOCK_DGRAM)
#创建客户套接字
cs.connect() #尝试连接服务器
comm._loop: #通信循环
cs.send()/cs.recv() #对话
cs.close() #关闭套接字
简单的server 端和客户端(tcp协议)
服务端
import socket #socket模块
import commands #执行系统命令模块
HOST='127.0.0.1'
PORT=8085
s= socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定义socket类型,网络通信,TCP协议,前面是ipv4协议,后面的tcp协议。
s.bind((HOST,PORT)) #套接字绑定的IP与端口,host用0.0.0.1就会包含本机的所有ip
s.listen(1) #开始TCP监听,listen中的参数表示可以多少客户端来进行连接
while 1:
conn,addr=s.accept() #接受TCP连接,并返回新的套接字与IP地址
print'Connected by',addr #输出客户端的IP地址
while 1:
data=conn.recv(1024) #把接收的数据实例化
print data
conn.sendall('got message from server:'+data.upper())
conn.close() #关闭连接
客户端:
import socket
HOST='127.0.0.1'
PORT=8085
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定义socket类型,网络通信,TCP
s.connect((HOST,PORT)) #要连接的IP与端口
while 1:
cmd=raw_input("Please input cmd:") #与人交互,输入命令
s.sendall(cmd) #把命令发送给对端
data=s.recv(1024) #把接收的数据定义为变量
print data #输出变量
s.close() #关闭连接
变形:把上面的代码改成只能发3次
netstat -ano|findstr "1104" 查看windows上某个端口是否被占用
netstat -anp|grep 8080 linux使用的
服务端:
import socket #socket模块
import commands #执行系统命令模块
HOST='127.0.0.1'
PORT=8085
s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#定义socket类型,网络通信,TCP
s.bind((HOST,PORT)) #套接字绑定的IP与端口
s.listen(5)
#开始TCP监听,listen中的参数表示可以多少客户端来进行连接
while 1:
conn,addr=s.accept() #接受TCP连接,并返回新的套接字与IP地址
print'Connected by',addr #输出客户端的IP地址
while 1:
try:
data=conn.recv(1024) #把接收的数据实例化
print data
conn.sendall('got message from server:'+data.upper())
except Exception:
conn.close() #关闭连接
break
客户端:
import socket
HOST='127.0.0.1'
PORT=8085
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#定义socket类型,网络通信,TCP
s.connect((HOST,PORT)) #要连接的IP与端口
times=3
while times>0:
cmd=raw_input("Please input cmd:") #与人交互,输入命令
s.sendall(cmd) #把命令发送给对端
data=s.recv(1024) #把接收的数据定义为变量
print data #输出变量
times-=1
s.close() #关闭连接
稍微复杂一点的客户端例子
服务端:
import time
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8009))
sock.listen(5)
while True:
time.sleep(0.1)
connection,address = sock.accept()
while 1:
try:
connection.settimeout(5)
buf = connection.recv(1024)
print "got message from client:",buf
if buf == '1':
print "1"
connection.send('welcome to server!')
elif buf == '2':
connection.send('please go out!')
elif buf == "close":
connection.send('bye!')
connection.close()
break
except socket.timeout:
print 'time out'
connection.close()
break
except Exception,e:
print e
connection.close()
break
客户端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8009))
import time
time.sleep(2)
sock.send('2')
print sock.recv(1023)
sock.send('1')
print sock.recv(1024)
sock.send('close')
print sock.recv(1024)
print "Done!"
sock.close()
变形练习:服务端把客户端传来的消息全部存入文件中,然后给客户端回传一句话。
自己:
服务端:
import time
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 10015))
sock.listen(5)
while True:
time.sleep(0.1)
connection,address = sock.accept()
while 1:
try:
connection.settimeout(5)
buf = connection.recv(1024)
print "got message from client:",buf
fp = open("e:\\test1.txt", "a")
if buf == "close":
connection.send('bye!')
connection.close()
break
else:
fp.write(buf+"\n")
#fp.write("\n")
fp.close()
except socket.timeout:
print 'time out'
connection.close()
break
except Exception,e:
print e
connection.close()
break
客户端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 10015))
import time
time.sleep(2)
sock.send('2')
sock.send('1')
sock.send('close')
print "Done!"
sock.close()
老师:
>>> import traceback
>>> try:
... 1/0
... except:
... print "error"
...
error
>>> try:
... 1/0
... except Exception,e:
... print e
...
integer division or modulo by zero
>>> try:
... 1/0
... except:
... traceback.print_exc()
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
服务端:
# encoding=utf-8
import time
import random
import traceback
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8009))
sock.listen(5)
AI_robot =[u'吃了么?',u"最近涨工资了么?",u"记得找对象",u"记得减肥"]
while True:
time.sleep(0.1)
connection,address = sock.accept()
while 1:
try:
connection.settimeout(5)
buf = connection.recv(1024)
fp=open("e:\\chat_record.txt","a")
print "got message from client:",buf.decode("utf-8")
if buf == "close":
connection.send('bye!')
connection.close()
break
elif buf=='q':
connection.close()
break
else:
fp.write(buf+"\n")
connection.send(str(time.ctime())+AI_robot[random.randint(0,3)].encode("utf-8"))
fp.close()
except socket.timeout:
print 'time out'
traceback.print_exc()
connection.close()
break
except Exception,e:
traceback.print_exc()
print e
connection.close()
break
客户端:
# encoding=utf-8
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8009))
import time
while 1:
try:
data=raw_input("say to server:")
sock.send(data.decode("gbk").encode("utf-8"))
print sock.recv(1023).decode("utf-8")
if data == "q":
sock.close()
break
except:
print "exception"
print "Done!"
实现一个类似ssh的功能(仅支持Linux操作系统)
ip:39.106.41.11
username:student
password:gloryroad987!
服务器端:
import commands
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8008))
sock.listen(5)
while True:
connection,address = sock.accept()
try:
connection.settimeout(5)
command = connection.recv(1024)
print command
code,result=commands.getstatusoutput(command)
print code
print result
except socket.timeout:
print 'time out'
connection.close()
客户端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8008))
import time
time.sleep(2)
sock.send('ls -al /home/wxh')
print sock.recv(1024)
sock.close()
服务器端:
import commands
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8001))
sock.listen(5)
while True:
connection,address = sock.accept()
try:
connection.settimeout(5)
command = connection.recv(1024)
print command
code,result=commands.getstatusoutput(command)
print code
print result
connection.send(str(code))
connection.send(result)
except socket.timeout:
print 'time out'
connection.close()
客户端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8001))
import time
time.sleep(2)
sock.send('ls -al /home/wxh')
print "command status:",sock.recv(1)
print "command result:",sock.recv(1024)
sock.close()
使用Socket 传送一个文件
服务器端
#-*- coding: UTF-8 -*-
import socket,time,SocketServer,struct,os,thread
host='127.0.0.1'
port=12307
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定义socket类型
s.bind((host,port)) #绑定需要监听的Ip和端口号,tuple格式
s.listen(1)
def conn_thread(connection,address):
while True:
try:
connection.settimeout(600) #设置连接时间
fileinfo_size=struct.calcsize('128sl') #文件信息
buf = connection.recv(fileinfo_size) #接收此文件的信息在buf里面
if buf: #如果不加这个if,第一个文件传输完成后会自动走到下一句,需要拿到文件大小信息才可以继续执行
filename,filesize =struct.unpack('128sl',buf) #解包,解开后是两个值
filename_f = filename.strip('\00') #解出文件名,去掉C语言中的字符串\00结束
filenewname = os.path.join('e:\\',('new_'+ filename_f))
print 'file new name is %s, filesize is %s' %(filenewname,filesize)
recvd_size = 0 #定义接收了的文件大小
file = open(filenewname,'wb')
print 'stat receiving...'
while not recvd_size == filesize: #判断收到的和原有文件是不是一样大,不一样继续接收
if filesize - recvd_size > 1024:
rdata = connection.recv(1024)
recvd_size += len(rdata)
else:
rdata = connection.recv(filesize - recvd_size) #接收到的文件和告知的一样大了,停止接收,判定为接收完毕
recvd_size = filesize
file.write(rdata)
file.close()
print 'receive done'
#connection.close()
except socket.timeout:
connection.close()
while True:
connection,address=s.accept()
print('Connected by ',address)
#thread = threading.Thread(target=conn_thread,args=(connection,address)) #使用threading也可以
#thread.start()
thread.start_new_thread(conn_thread,(connection,address))
s.close()
客户端
#-*- coding: UTF-8 -*-
import socket,os,struct
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('127.0.0.1',12307))
while True:
filepath = raw_input('Please Enter chars:\r\n')
if os.path.isfile(filepath):
fileinfo_size=struct.calcsize('128sl') #定义打包规则
#定义文件头信息,包含文件名和文件大小
fhead = struct.pack('128sl',os.path.basename(filepath),os.stat(filepath).st_size)
s.send(fhead)
print 'client filepath: ',filepath
# with open(filepath,'rb') as fo: 这样发送文件有问题,发送完成后还会发一些东西过去
fo = open(filepath,'rb')
while True:
filedata = fo.read(1024)
if not filedata:
break
s.send(filedata)
fo.close()
print 'send over...'
#s.close()
SocketServer模块的Fork方式(进程模式)linux 下执行
Server 端代码
#!/usr/bin/python
#encoding=utf-8
from SocketServer import TCPServer, ForkingMixIn, StreamRequestHandler
import time
class Server(ForkingMixIn, TCPServer): #自定义Server类 ,以进程模式启动
pass
class MyHandler(StreamRequestHandler):
def handle(self): #重载handle函数
addr = self.request.getpeername()
print 'Get connection from', addr #打印客户端地址
data= self.rfile.readline().strip() #客户端发送的信息必须带有回车,否则会一直等待客户端继续发送数据
print data
time.sleep(1) #休眠5秒钟
if data:
self.wfile.write('This is a ForkingMixIn tcp socket server') #给客户端发送信息
host = '127.0.0.1'
port = 8009
server = Server((host, port), MyHandler)
server.serve_forever() #开始侦听并处理连接
客户端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8009))
import time
time.sleep(2)
sock.send('ls -al /home/wxh'+"\n")
print sock.recv(1024)
sock.close()
注释:
多个连接同时到达服务器端的时候,每个连接主进程都生成一个子进程专门用来处理此连接,而主进程则依旧保持在侦听状态。因主进程和子进程是同时进行的,所以不会阻塞新的连接。但由于生成进程消耗的资源比较大,这种处理方式在有很多连接的时候会带来性能问题。Server类须继承ForkingMixIn和TCPServer两个类。
SocketServer模块的Fork方式(线程模式)linux 下执行
服务器端代码:
#!/usr/bin/python
#encoding=utf-8
from SocketServer import TCPServer, ThreadingMixIn, StreamRequestHandler
import time
class Server(ThreadingMixIn, TCPServer): #自定义Server类 ,以线程方式启动
pass
class MyHandler(StreamRequestHandler):
def handle(self): #重载handle函数
addr = self.request.getpeername()
print 'Get connection from', addr #打印客户端地址
data= self.rfile.readline().strip() #客户端发送的信息必须带有回车,否则会一直等待客户端继续发送数据
print data
time.sleep(1) #休眠5秒钟
if data:
self.wfile.write('This is a ForkingMixIn tcp socket server') #给客户端发送信息
host = '127.0.0.1'
port = 8001
server = Server((host, port), MyHandler)
server.serve_forever() #开始侦听并处理连接
线程是一种轻量级的进程,比Fork消耗的资源更少,而且主线程和子线程之间具有相同的地址空间,处理效率高。但大量的使用线程会带来线程之间的数据同步问题,处理不好可能使服务程序失去响应。上述与Fork方式中代码基本相同,仅仅是采用的ThreadingMixIn类不同。
SocketServer模块的Threading线程池方式
import SocketServer
import threading
class MyTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
while True:
self.data = self.request.recv(1024).strip()
cur_thread = threading.current_thread()
print cur_thread
if not self.data:
print "client:%s leave!" % self.client_address[0]
break
print "%s wrote:%s" % (self.client_address[0], self.data)
self.request.sendall(self.data.upper())
if __name__ == "__main__":
HOST, PORT = "127.0.0.1",8044
server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
server.serve_forever()
if __name__ == "__main__":
HOST, PORT = "127.0.0.1",8001
server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
server.serve_forever()
#服务器可以自动判断客户端是否还会发送数据
客户端程序:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8044))
import time
time.sleep(2)
sock.send('ls -al /home/wxh'+"\n")
print sock.recv(1024)
sock.close()
使用socketserver 传送一个文件
服务端:
#-*- coding: UTF-8 -*-
import socket,time,SocketServer,struct,os
host='127.0.0.1'
port=12307
ADDR=(host,port)
class MyRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
print('connected from:', self.client_address)
while True:
fileinfo_size=struct.calcsize('128sl') #定义文件信息。128s表示文件名为128bytes长,l表示一个int或log文件类型,在此为文件大小
self.buf = self.request.recv(fileinfo_size)
if self.buf: #如果不加这个if,第一个文件传输完成后会自动走到下一句
self.filename,self.filesize =struct.unpack('128sl',self.buf) #根据128sl解包文件信息,与client端的打包规则相同
print 'filesize is: ',self.filesize,'filename size is: ',len(self.filename) #文件名长度为128,大于文件名实际长度
self.filenewname = os.path.join('e:\\',('new_'+ self.filename).strip('\00')) #使用strip()删除打包时附加的多余空字符
print self.filenewname,type(self.filenewname)
recvd_size = 0 #定义接收了的文件大小
file = open(self.filenewname,'wb')
print 'stat receiving...'
while not recvd_size == self.filesize:
if self.filesize - recvd_size > 1024:
rdata = self.request.recv(1024)
recvd_size += len(rdata)
else:
rdata = self.request.recv(self.filesize - recvd_size)
recvd_size = self.filesize
file.write(rdata)
file.close()
print 'receive done'
#self.request.close()
tcpServ = SocketServer.ThreadingTCPServer(ADDR, MyRequestHandler)
print('waiting for connection...' )
tcpServ.serve_forever()
客户端:
#-*- coding: UTF-8 -*-
import socket,os,struct
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('127.0.0.1',12307))
while True:
filepath = raw_input('Please Enter chars:\r\n')
if os.path.isfile(filepath):
fileinfo_size=struct.calcsize('128sl') #定义打包规则
#定义文件头信息,包含文件名和文件大小
fhead = struct.pack('128sl',os.path.basename(filepath),os.stat(filepath).st_size)
s.send(fhead)
print 'client filepath: ',filepath
# with open(filepath,'rb') as fo: 这样发送文件有问题,发送完成后还会发一些东西过去
fo = open(filepath,'rb')
while True:
filedata = fo.read(1024)
if not filedata:
break
s.send(filedata)
fo.close()
print 'send over...'
#s.close()
Select :
I/O多路复用是在单线程模式下实现多线程的效果,实现一个多I/O并发的效果。
进程指定内核监听哪些文件描述符(最多监听1024个fd)的哪些事件,当没有文件描述符事件发生时,进程被阻塞;当一个或者多个文件描述符事件发生时,进程被唤醒。
当我们调用select()时:
1、上下文切换转换为内核态
2、将fd从用户空间复制到内核空间
3、内核遍历所有fd,查看其对应事件是否发生
4、如果没发生,将进程阻塞,当设备驱动产生中断或者timeout时间后,将进程唤醒,再次进行遍历
5、返回遍历后的fd
6、将fd从内核空间复制到用户空间
一个任务:
1 www.sohu.com
2 www.sogou.com
3 www.gloryroad.cn
4 www.baidu.com
单进程或者单线程:
1->2->3-4
多进程或者多线程:
一个线程、一个进程:去执行一个访问
select:
1 个线程:
1 www.sohu.com
5秒
2 www.sogou.com
4秒
3 www.gloryroad.cn
3秒
4 www.baidu.com
2秒 select不会等,一直发请求,哪个先返回用哪个。
fd:file descriptor 文件描述符
fd_r_list, fd_w_list, fd_e_list = select.select(rlist, wlist, xlist, [timeout])
参数: 可接受四个参数(前三个必须)
rlist: wait until ready for reading
wlist: wait until ready for writing(一般不使用)
xlist: wait for an “exceptional condition”
timeout: 超时时间
Server端:
import socket
import select
s = socket.socket()
s.bind(('127.0.0.1',8888))
s.listen(5)
r_list = [s,]
num = 0
while True:
rl, wl, error = select.select(r_list,[],[],10) #r_list读的句柄,[][]后面两个是异常的句柄,10是超时
执行过程:
#第一次执行循环体:客户端建立的连接的时候,rl和r_list分别是[s,]和[s,]
# 执行连接之后,r_list变为了[s,conn]
#第二次执行循环体:rl和r_list分别是[conn,]和[s,conn]
num+=1
print('counts is %s'%num)
print("rl's length is %s"%len(rl))
for fd in rl:
if fd == s:
conn, addr = fd.accept()
r_list.append(conn)
msg = conn.recv(200)
conn.sendall(('first----%s'%conn.fileno()).encode())
else:
try:
msg = fd.recv(200)
fd.sendall('second'.encode())
except ConnectionAbortedError:
r_list.remove(fd)
s.close()
客户端:
import socket
flag = 1
s = socket.socket()
s.connect(('127.0.0.1',8888))
while flag:
input_msg = input('input>>>')
if input_msg == '0':
break
s.sendall(input_msg.encode())
msg = s.recv(1024)
print(msg.decode())
s.close()
#注意客户端输入的数据需要外带引号,例如:’hi’
Twisted 框架
服务器端:
# coding=utf-8
from twisted.internet.protocol import Protocol
from twisted.internet.protocol import Factory
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.internet import reactor
clients = []
class Spreader(Protocol):
def __init__(self, factory):
self.factory = factory
def connectionMade(self):
self.factory.numProtocols = self.factory.numProtocols + 1
self.transport.write(
"欢迎来到Spread Site, 你是第%s个客户端用户!\n" % (self.factory.numProtocols)
)
print "new connect: %d" % (self.factory.numProtocols)
clients.append(self)
def connectionLost(self, reason):
self.factory.numProtocols = self.factory.numProtocols - 1
clients.remove(self)
print "lost connect: %d" % (self.factory.numProtocols)
def dataReceived(self, data):
print "received data:",data
self.transport.write(data)
class SpreadFactory(Factory):
def __init__(self):
self.numProtocols = 0
def buildProtocol(self, addr):
return Spreader(self)
endpoint = TCP4ServerEndpoint(reactor, 8007)
endpoint.listen(SpreadFactory())
reactor.run()
客户端:
# coding=utf-8
from twisted.internet.protocol import Protocol, ClientFactory
from twisted.internet import reactor
import threading
import time
import sys
import datetime
class Echo(Protocol):
def __init__(self):
self.connected = False
def connectionMade(self):
self.connected = True
def connectionLost(self, reason):
self.connected = False
def dataReceived(self, data):
print "data from server:",data.decode("utf-8")
class EchoClientFactory(ClientFactory):
def __init__(self):
self.protocol = None
def startedConnecting(self, connector):
print "Start to Connect..."
def buildProtocol(self, addr):
print "Connected..."
self.protocol = Echo()
return self.protocol
def clientConnectionLost(self, connector, reason):
print "Lost connection. Reason: ", reason
def clientConnectionFailed(self, connector, reason):
print "Connection is failed, Reason: ", reason
bStop = False
def routine(factory):
while not bStop:
if factory.protocol and factory.protocol.connected:
factory.protocol.transport.write("hello, I'm %s %s" % (
sys.argv[0], datetime.datetime.now()
))
print sys.argv[0], datetime.datetime.now()
time.sleep(5)
host = '127.0.0.1'
port = 8007
factory = EchoClientFactory()
reactor.connectTCP(host, port, factory)
threading.Thread(target=routine, args=(factory,)).start()
reactor.run()
bStop = True
——————————————————————————————————————
#!/usr/bin/python
#encoding=utf-8
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, Factory
class EchoServer(Protocol):
def connectionMade(self): #连接建立的时候
print 'Get connection from', self.transport.client
self.factory.numProtocols = self.factory.numProtocols+1
if self.factory.numProtocols > 2: #当连接超过2个的时候,断开连接
self.transport.write("Too many connections, try later\n")
self.transport.loseConnection()
return
print 'Get connection from', self.transport.client
def connectionLost(self, reason): #断开连接
self.factory.numProtocols = self.factory.numProtocols-1
def dataReceived (self, data): #将收到的数据返回给客户端
self.transport.write(data)
print data
self.transport.loseConnection() #收到数据后就断开
factory = Factory()
factory.numProtocols = 0
factory.protocol = EchoServer
port = 1200
reactor.listenTCP(port, factory)
reactor.run() #进入循环
客户端程序:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8001))
import time
time.sleep(2)
sock.send('ls -al /home/wxh'+"\n")
print sock.recv(1024)
sock.close()
twisted 官网的例子,保存为echoServ.py:
#!/usr/bin/env python
# coding: utf-8
from twisted.internet.protocol import Protocol
from twisted.internet.protocol import Factory
from twisted.internet import reactor
class Echo(Protocol):
'''协议类实现用户的服务协议,例如 http,ftp,ssh 等'''
def __init__(self, factory):
self.factory = factory
def connectionMade(self):
'''连接建立时被回调的方法'''
self.factory.numProtocols = self.factory.numProtocols + 1
self.transport.write("Welcome! There are currently %d open connections.\n" %(self.factory.numProtocols,))
def connectionLost(self, reason):
'''连接关闭时被回调的方法'''
self.factory.numProtocols = self.factory.numProtocols - 1
def dataReceived(self, data):
'''接收数据的函数,当有数据到达时被回调'''
print data
self.transport.write(data)
class EchoFactory(Factory):
'''协议工厂类,当客户端建立连接的时候,创建协议对象,协议对象与客户端连接一一对应'''
numProtocols = 0
#protocol = Echo
def buildProtocol(self, addr):
return Echo(self)
if __name__ == '__main__':
# 创建监听端口
FACTORY = EchoFactory()
reactor.listenTCP(1200, FACTORY)
# 开始监听事件
reactor.run()
协议工厂继承自twisted.internet.protocol.Factory,需实现buildProtocol方法,协议工厂负责实例化协议类,不应该保存于连接相关的状态信息,因为协议工厂类仅创建一个。协议类继承自twisted.internet.protocol.Protocol,需实现dataReceived等方法,在协议类中实现应用协议,每一个客户端连接都会创建一个新的协议类对象。transport就是连接对象,通过它进行网络写数据。
客户端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 1200))
import time
time.sleep(2)
sock.send('ls -al /home/wxh'+"\n")
print sock.recv(1024)
sock.send('ls -al /home/wxh'+"\n")
print sock.recv(1024)
sock.close()
使用守护进程来运行服务器端程序
使用守护进程的方式运行服务,需要提供一个tac配置文件(这就是一个python文件,只是扩展名不同),并且在这个文件中需要创建一个应用程序对象,对象名必须是application。
首先创建一个echo.tac文件:
#!/usr/bin/env python
# coding: utf-8
from twisted.application import service, internet
import sys
sys.path.append("e:\\") #echoServ.py保存在哪个目录,就把这个路径设定到哪里
from echoServ import EchoFactory
# 创建应用程序对象
application = service.Application('Echo 服务程序')
# 创建 service 对象
myServices = internet.TCPServer(1200, EchoFactory())
# 设置 application 为 service 的父元素
myServices.setServiceParent(application)
然后用守护进程方式运行服务,运行命令:#twistd -y echo.tac。
若想要停止服务:#kill -15 (pid)。
客户端程序:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 1200))
import time
time.sleep(2)
sock.send('ls -al /home/wxh'+"\n")
print sock.recv(1024)
sock.close()
聊天的服务器版本:
服务器端:
from twisted.internet import protocol, reactor
class Echo(protocol.Protocol):
def dataReceived(self, data):
# As soon as any data is received, write it back
self.transport.write(data)
print data
class EchoFactory(protocol.Factory):
def buildProtocol(self, addr):
return Echo()
reactor.listenTCP(8000, EchoFactory())
reactor.run()
客户端:
from twisted.internet import reactor, protocol
class EchoClient(protocol.Protocol):
def connectionMade(self):
self.transport.write("hello, world!")
def dataReceived(self, data):
print "Server said:", data
self.transport.loseConnection()
def connectionLost(self, reason):
print "connection lost"
class EchoFactory(protocol.ClientFactory):
def buildProtocol(self, addr):
return EchoClient()
def clientConnectionFailed(self, connector, reason):
print "Connection failed - goodbye!"
reactor.stop()
def clientConnectionLost(self, connector, reason):
print "Connection lost - goodbye!"
reactor.stop()
reactor.connectTCP("localhost", 8000, EchoFactory())
reactor.run()
写一个聊天的服务器:
服务器端:
#!/usr/bin/python
#encoding=utf-8
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor
class Chat(LineReceiver):
def __init__(self, users):
self.users = users
self.name = None
self.state = "GETNAME"
def connectionMade(self):
self.sendLine("What's your name?\n")
def connectionLost(self, reason):
if self.users.has_key(self.name):
del self.users[self.name]
def lineReceived(self, line):
if self.state == "GETNAME":
self.handle_GETNAME(line)
else:
self.handle_CHAT(line)
def handle_GETNAME(self, name):
if self.users.has_key(name):
self.sendLine("Name taken, please choose another."+"\n")
return
self.sendLine("Welcome, %s!\n" % (name,))
self.name = name
self.users[name] = self
self.state = "CHAT"
def handle_CHAT(self, message):
message = "<%s> %s\n" % (self.name, message)
for name, protocol in self.users.iteritems():
if protocol == self:
self.sendLine(message+"\n")
class ChatFactory(Factory):
def __init__(self):
self.users = {} # maps user names to Chat instances
def buildProtocol(self, addr):
return Chat(self.users)
if __name__ == '__main__':
reactor.listenTCP(1200, ChatFactory())
reactor.run()
客户端1:
#socket client end
from socket import *
s=socket(AF_INET,SOCK_STREAM)
remote_host=gethostname()
print 'remote_host:',remote_host
port=1200
s.connect((remote_host,port)) #发起连接
print("Connected from",s.getsockname()) ##返回本地IP和端口
print("Connected to",s.getpeername()) ##返回服务端IP和端口
s.send('Jack\r\n')#发送一行字符串(以\r\n 结束)到服务器端
s.send('hi\r\n')
s.send('hello\r\n')
print 'the msg i got from select server is:'
print s.recv(1200)
客户端2:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 1200))
import time
time.sleep(2)
sock.send('fosterwu\r\n')
print sock.recv(1020)
sock.send('hi\r\n')
print sock.recv(1020)
sock.send('good\r\n')
print sock.recv(120)
sock.send('gloryroad\r\n')
print sock.recv(120)
sock.close()
Twisted框架中对文件的操作
服务端:
工厂有startFactory和stopFactory两种方式来执行相关应用的创建与销毁,下述代码从客户端接收到的信息都会被写入文件中。
#!/usr/bin/env python
# coding: utf-8
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor
class LoggingProtocol(LineReceiver):
def lineReceived(self, line):
self.factory.fp.write(line+'\n')
self.factory.fp.flush()
self.sendLine("got it!")
class LogfileFactory(Factory):
protocol = LoggingProtocol
def __init__(self, fileName):
self.file = fileName
def startFactory(self):
self.fp = open(self.file, 'a')
def stopFactory(self):
self.fp.close()
print "close"
if __name__ == '__main__':
# 创建监听端口
FACTORY = LogfileFactory("e:\\a.txt")
reactor.listenTCP(8007, FACTORY)
# 开始监听事件
reactor.run()
客户端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8007))
import time
time.sleep(2)
sock.send('fosterwu\r\n')
print sock.recv(1020)
sock.send('hi\r\n')
print sock.recv(1020)
sock.send('good\r\n')
print sock.recv(120)
sock.send('gloryroad\r\n')
print sock.recv(120)
sock.close()
Udp Server 端:
from socket import *
from time import ctime
HOST = ''
PORT = 1200
BUFSIZ = 128
ADDR = (HOST, PORT)
#创建一个服务器端UDP套接字
udpServer = socket(AF_INET, SOCK_DGRAM)
#绑定服务器套接字
udpServer.bind(ADDR)
while True:
print 'waiting for message...'
#接收来自客户端的数据
data, addr = udpServer.recvfrom(BUFSIZ)
print "got message:",data
#向客户端发送数据
udpServer.sendto('[%s] %s' % (ctime(), data), addr)
print '...received from and returned to:', addr
udpServer.close()
Udp 的客户端:
#encoding=utf-8
from socket import *
HOST = 'localhost'
PORT = 1200
BUFSIZ = 128
ADDR = (HOST, PORT)
# 创建客户端UDP套接字
udpClient = socket(AF_INET, SOCK_DGRAM)
while True:
data = raw_input('>')
if not data:
break
# 向服务器端发送数据
udpClient.sendto(data, ADDR)
# 接收来自服务器端的数据
data, ADDR = udpClient.recvfrom(BUFSIZ)
print data
if not data:
break
udpClient.close()
参考文献:
http://blog.csdn.net/taiyang1987912/article/details/40376067
https://www.cnblogs.com/earendil/p/7411115.html
基于广播的聊天程序
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
SO_LINGER选项用来设置当调用closesocket时是否马上关闭socket;
SO_REUSEADDR用于对TCP套接字处于TIME_WAIT状态下的socket,才可以重复绑定使用。server程序总是应该在调用bind()之前设置SO_REUSEADDR套接字选项。TCP,先调用close()的一方会进入TIME_WAIT状态
服务器端:Linux可以执行
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__":
# List to keep track of socket descriptors
CONNECTION_LIST = []
RECV_BUFFER = 4096 # Advisable to keep it as an exponent of 2
PORT = 6001
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
broadcast_data(sockfd, "[%s:%s] entered room\n" % 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)
if data:
broadcast_data(sock, "\r" + '<' + str(sock.getpeername()) + '> ' + data)
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()
客户端:
import socket, select, string, sys
def prompt() :
sys.stdout.write('<You> ')
sys.stdout.flush()
#main function
if __name__ == "__main__":
if(len(sys.argv) < 3) :
print 'Usage : python telnet.py hostname port'
sys.exit()
host = sys.argv[1]
port = int(sys.argv[2])
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(6)
# connect to remote host
try :
s.connect((host, port))
except :
print 'Unable to connect'
sys.exit()
print 'Connected to remote host. Start sending messages'
prompt()
while 1:
rlist = [sys.stdin, s]
#s.connect((host, port))
# Get the list sockets which are readable
read_list, write_list, error_list = select.select(rlist , [], [])
for sock in read_list:
#incoming message from remote server
if sock == s:
data = sock.recv(4096)
if not data :
print '\nDisconnected from chat server'
sys.exit()
else :
#print data
sys.stdout.write(data)
prompt()
#user entered a message
else :
msg = sys.stdin.readline()
s.send(msg)
prompt()
执行:
需要多开几个客户端,才能看到广告效果,执行如下:
python client.py 127.0.0.1 6001
Unix下是sock,而windows下是winsock,中间语法有很多是不同的,所以windows运行client程序有问题
socket编程了解的更多相关文章
- Linux下的C Socket编程 -- server端的继续研究
Linux下的C Socket编程(四) 延长server的生命周期 在前面的一个个例子中,server在处理完一个连接后便会立即结束掉自己,然而这种server并不科学啊,server应该是能够一直 ...
- java socket编程(li)
一.网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输.在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以 ...
- Python Socket 编程——聊天室示例程序
上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...
- Linux下的C Socket编程 -- server端的简单示例
Linux下的C Socket编程(三) server端的简单示例 经过前面的client端的学习,我们已经知道了如何创建socket,所以接下来就是去绑定他到具体的一个端口上面去. 绑定socket ...
- Linux下的C Socket编程 -- 获取对方IP地址
Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...
- Linux下的C Socket编程 -- 简介与client端的处理
Linux下的C Socket编程(一) 介绍 Socket是进程间通信的方式之一,是进程间的通信.这里说的进程并不一定是在同一台机器上也有可能是通过网络连接的不同机器上.只要他们之间建立起了sock ...
- python网络编程-socket编程
一.服务端和客户端 BS架构 (腾讯通软件:server+client) CS架构 (web网站) C/S架构与socket的关系: 我们学习socket就是为了完成C/S架构的开发 二.OSI七层 ...
- Socket编程实践(2) Socket API 与 简单例程
在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程.该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端. socket()函 ...
- Socket编程实践(1) 基本概念
1. 什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间.用户空间的程序需要通 ...
- [转]C语言SOCKET编程指南
1.介绍 Socket编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措?等 ...
随机推荐
- Kendo MVVM 数据绑定(四) Disabled/Enabled
Kendo MVVM 数据绑定(四) Disabled/Enabled Disabled 和 Enabled 绑定可以根据 ViewModel 的某个属性值的 true,false 来设置 DOM 元 ...
- [20190620]日常学习记录(三)-初识promise及vuex
在学习promise之前重温了Ajax的原生js实现, 在原生js中发送一个http请求首先new XMLHttpRequest() 然后定义状态变更事件 浏览器监听请求的状态,触发不同状态下相应的代 ...
- IOS使用固定定位遇到的问题
近日需要实现移动端页面额外功能按钮,即点击加号弹出点赞与留言功能,通常这个按钮都会固定于页面的右下角,首先就想到使用固定定位来实现. 但是在测试时我们发现,在IOS中,当系统键盘弹出时,fixed会失 ...
- Webpack 入门学习
1.什么是Webpack? Webpack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等 ...
- 31全志r58平台Android4.4.2下打开USB摄像头
31全志r58平台Android4.4.2下打开USB摄像头 2018/10/26 16:00 版本:V1.0 开发板:SC5806 1.系统编译:(略) 2.需要修改的文件: W:\r58_andr ...
- IDEA创建maven项目的web.xml头
使用IDEA创建maven项目骨架是webapp时,软件自动创建的web.xml文件是2.3版本的,不能使用el表达式,所以可以手动换成4.0的文件头. <?xml version=" ...
- 【extjs6学习笔记】1.2 初始:MVC MVVM
模型 这表示数据层.该模型可以包含数据验证和逻辑来保持数据.在 ext js 中, 大多数模型都与一个数据存储一起使用. 视图 这表示用户界面. 是用户在屏幕上看到的组件. 在每次互动的用户与应用程序 ...
- [Java]获取byte数组的实际使用长度
背景:byte.length只能获取到初始化的byte数组长度,而不是实际使用的长度,因此想要获取到实际的使用长度只能靠其他方法实现. 方法一: public class ByteActualLeng ...
- Vue.js + Webpack + ECMAScript 6 入门教程
Vue.js学习教程 1.Vue.js——60分钟快速入门 2.Vue.js——60分钟组件快速入门(上篇) 3.Vue.js——60分钟组件快速入门(下篇) 4.Vue.js——基于$.ajax实现 ...
- python基础教程总结12——数据库
1. Python 数据库 API 很多支持SQL标准的数据库在Python中都有对应的客户端模块.为了在提供相同功能(基本相同)的不同模块之间进行切换(兼容),Python 规定了一个标准的 DB ...