关于socket知识整理
一个完整的计算机系统是由硬件、操作系统、应用软件三者组成,具备了这三个条件,一台计算机系统就可以玩单机游戏。如果你想上网(访问个黄色网站,发个黄色微博啥的),就需要遵守网络协议,即计算机之间交流的标准,按照分工不同可以把互联网协议从逻辑上划分层级,即ios七层协议,详见另一篇博客。参考博客(网络通信原理):http://www.cnblogs.com/linhaifeng/articles/5937962.html
先从服务器端说起,服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。
服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来
客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
s.recv() 接收TCP数据
s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom() 接收UDP数据
s.sendto() 发送UDP数据
s.close() 关闭套接字
根据相关流程和函数,先来实现一个简单的通讯。
server端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import socket server = socket.socket() server.bind(( '127.25.0.0' , 8080 )) #绑定ip,端口 server.listen( 3 ) #python3中listen必须有参数,次数设为3,排队等待个数为3 while True : #循环接收不同ip,端口信息 print ( '等待联机...' ) conn, addr = server.accept() #阻塞 print ( 'new conn:' ,addr) while True : #循环接收同个ip,端口的信息 try : #异常处理,在服务端接收、发送失败时,提示客户端断开 data = conn.recv( 1024 ).decode( 'utf-8' ) print ( 'recv:' ,data) while True : #若输入为空,重新输入 msg = input ( '>>>:' ).strip() if len (msg) = = 0 : continue else : break conn.send(msg.encode( 'utf-8' )) #发送 except ConnectionResetError as reason: print ( 'client has lost...\n' ) break server.close() |
client端
1
2
3
4
5
6
7
8
9
10
11
12
|
import socket client = socket.socket() client.connect(( '127.25.0.0' , 8080 )) #连接服务端ip,端口 while True : #为空,重新输入 msg = input ( '>>>:' ).strip() if len (msg) = = 0 : continue client.send(msg.encode( 'utf-8' )) #发送 data = client.recv( 1024 ).decode( 'utf-8' ) #接收 print ( 'recv:' ,data) client.close() |
这样,在服务端先运行的情况下,客户端能进行连接,实现一收一发的通讯方式,第一个连接未断开时,后面的连接只能等待前者断开,否则客户端无法接收后者的消息。要注意的是,当第一个连接断开时,会出现异常。在Windows中,出现ConnectionResetError的报错,可以利用try...except...进行异常捕捉处理。在linux中,客户端断开时,服务端无限接收空值,我们可以利用if进行判断。除了简单通讯,利用一收一发的原理,我们可以实现客户端命令服务端,并接收返回的命令结果。
server端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
import socket,os server = socket.socket() server.bind(( 'localhost' , 9999 )) server.listen( 3 ) while True : print ( '等待联机...' ) conn, addr = server.accept() print ( 'new conn:' ,addr) while True : print ( '准备接收指令...' ) cmd = conn.recv( 1024 ).decode( 'utf-8' ) if not cmd: #在linux中,客户端断开即无限发送空值 print ( '客服端已断开...\n' ) break print ( '执行指令:' ,cmd) data = os.popen(cmd).read() #接收字符串,执行结果也是字符串 data = data.encode( 'utf-8' ) data_size = len (data) conn.send( str (data_size).encode( 'utf-8' )) #发送数据容量大小 if data_size = = 0 : #若指令不存在,则返回数据大小为0,只发送数据大小,不发送内容 print ( '指令不存在...' ) continue conn.send(data.encode( 'utf-8' )) #发送数据 print ( '执行完毕...\n' ) server.close() |
client端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
import socket client = socket.socket() client.connect(( 'localhost' , 9999 )) while True : cmd = input ( '输入指令,q退出\n>>>:' ) if len (cmd) = = 0 : print ( '指令不能为空...' ) continue if cmd = = 'q' : break #退出 client.send(cmd.encode( 'utf-8' )) #发送指令 data_size = client.recv( 1024 ) #接收数据容量大小 data_size = int (data_size.decode( 'utf-8' )) if data_size = = 0 : #若接收到的数据容量为0,说明指令不存在 print ( '指令不存在...' ) continue print ( '接收数据大小' ,data_size) received_size = 0 #已接收的数据大小 received_data = b'' #已接收的数据 while data_size > received_size: #循环接收数据,并计算接收到的数据大小 #最后一次接收数据为剩余数据,防止连续接收导致出现粘包现象 if data_size - received_size > 1024 : #接收不止一次 size = 1024 else : size = data_size - received_size #最后一次接收,剩多少收多少 data = client.recv(size) received_size + = len (data) received_data + = data else : print ( '执行完毕' ,received_size) #接收的数据大小 print () #为了美观,数据单独打印 print (received_data.decode( 'utf-8' )) #打印数据 client.close() |
特别注意,在TCP中,当两次连续接收时会出现粘包的现象,即(1)发送数据时间间隔很短,数据了很小,会合到一起,产生粘包(2)客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包。解决粘包问题的方法,即(1)利用time模块,time.sleep(0.5)增长大约0.5的发送间隔(2)在两次发送之间插入一次接收(3)严格限制每次接收的数据大小。下面,我们来模拟客户端在服务端中下载文件。
server端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
import socket,os,hashlib server = socket.socket() server.bind(( '127.0.0.1' , 8080 )) server.listen( 3 ) while True : print ( '等待联机...' ) conn, addr = server.accept() print ( 'new conn:' ,addr) while True : print ( '准备接收指令...' ) cmd = conn.recv( 1024 ).decode( 'utf-8' ) #接收客户端指令 if not cmd: #在linux中,客户端断开即无限发送空值 print ( '客服端已断开...\n' ) break cmd, filename = cmd.split() #如:文件发送,send filename,get filename print (filename) if os.path.isfile(filename): f = open (filename, 'rb' ) m = hashlib.md5() file_size = os.stat(filename).st_size #获得文件大小 conn.send( str (file_size).encode( 'utf-8' )) #发送文件大小 conn.recv( 1024 ) #等待客户端确认 for line in f: m.update(line) conn.send(line) f.close() conn.send(m.hexdigest().encode( 'utf-8' )) #发送md5 print ( '发送完毕...\n' ) server.close() |
client端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
import socket,hashlib client = socket.socket() client.connect(( '127.0.0.1' , 8080 )) while True : cmd = input ( '>>>:' ).strip() #执行下载 if len (cmd) = = 0 : continue if cmd.startswith( 'get' ): client.send(cmd.encode( 'utf-8' )) data_size = int (client.recv( 1024 ).decode( 'utf-8' )) print ( '文件大小为:' ,data_size) client.send( '准备接收...' .encode()) #客户端确认接收 received_size = 0 filename = cmd.split()[ 1 ] f = open (filename + '.new' , 'wb' ) m = hashlib.md5() while data_size > received_size: if data_size - received_size > 1024 : #不止一次接收,接收1024 size = 1024 else : size = data_size - received_size #最后一次接收,接收全部 data = client.recv(size) received_size + = len (data) m.update(data) f.write(data) else : f.close() new_md5 = m.hexdigest() #客户端md5 print ( '接受完毕...' ) old_md5 = client.recv( 1024 ).decode( 'utf-8' ) #接收服务端md5 print ( 'server file md5:' ,old_md5) #验证md5 print ( 'client file md5:' ,new_md5) client.close() |
此时,依然是一收一发的方式,限制了我们的交流的效率,总觉得太low了。
server端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import socketserver,time class MyTCPHandler(socketserver.BaseRequestHandler): def handle( self ): while True : try : self .data = self .request.recv( 1024 ).decode( 'utf-8' ) print ( '来自:' , self .client_address) print ( '[%s]:%s\n' % (time.ctime(), self .data)) self .request.send( self .data.upper().encode( 'utf-8' )) except ConnectionResetError as reason: print ( '{}已下线...\n' . format ( self .client_address)) break if __name__ = = '__main__' : HOST, PORT = 'localhost' , 9999 ADDR = (HOST, PORT) server = socketserver.ThreadingTCPServer(ADDR, MyTCPHandler) server.serve_forever() |
client端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import socket HOST, PORT = 'localhost' , 9999 ADDR = (HOST, PORT) client = socket.socket() client.connect(ADDR) while True : msg = input ( '>>>:' ).strip() if len (msg) = = 0 : continue client.send(msg.encode( 'utf-8' )) data = client.recv( 1024 ) print ( 'recv:' ,data.decode( 'utf-8' )) client.close() |
效果图
这样我们实现了单服务端与多客户端进行交流,此处用到的是多线程。此外,关于多线程,多进程,协程的原理和方法,之后再重新开篇单独记叙。
此处留连接地址将来用。
经过了多次实践,已经掌握了简单通讯,远程命令,文件下载,一对多交流。可以开发一个支持多用户在线的FPT程序,要求如下:
1.用户加密认证
2.允许多用户同时登录
3.每个用户拥有自己的家目录,且只能访问自己的家目录
4.对用户进行磁盘配额,每个用户可用空间不同
5.允许用户在ftp server上随意切换目录
6.允许用户查看当前的目录下的文件
7.允许上传和下载文件,保证文件的一致性
8.文件传输过程中显示进度条
9.附加功能:支持文件的断点续传及下载
此处留连接地址将来用。
关于socket知识整理的更多相关文章
- Linux进程管理知识整理
Linux进程管理知识整理 1.进程有哪些状态?什么是进程的可中断等待状态?进程退出后为什么要等待调度器删除其task_struct结构?进程的退出状态有哪些? TASK_RUNNING(可运行状态) ...
- Redis知识整理
Redis知识整理 转自:https://www.cnblogs.com/rjzheng/p/9096228.html 1.单线程模型 Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返 ...
- js事件(Event)知识整理
事件(Event)知识整理,本文由网上资料整理而来,需要的朋友可以参考下 鼠标事件 鼠标移动到目标元素上的那一刻,首先触发mouseover 之后如果光标继续在元素上移动,则不断触发mousemo ...
- Kali Linux渗透基础知识整理(四):维持访问
Kali Linux渗透基础知识整理系列文章回顾 维持访问 在获得了目标系统的访问权之后,攻击者需要进一步维持这一访问权限.使用木马程序.后门程序和rootkit来达到这一目的.维持访问是一种艺术形式 ...
- Kali Linux渗透基础知识整理(二)漏洞扫描
Kali Linux渗透基础知识整理系列文章回顾 漏洞扫描 网络流量 Nmap Hping3 Nessus whatweb DirBuster joomscan WPScan 网络流量 网络流量就是网 ...
- wifi基础知识整理
转自 :http://blog.chinaunix.net/uid-9525959-id-3326047.html WIFI基本知识整理 这里对wifi的802.11协议中比较常见的知识做一个基本的总 ...
- 数据库知识整理<一>
关系型数据库知识整理: 一,关系型数据库管理系统简介: 1.1使用数据库的原因: 降低存储数据的冗余度 提高数据的一致性 可以建立数据库所遵循的标准 储存数据可以共享 便于维护数据的完整性 能够实现数 ...
- 【转载】UML类图知识整理
原文:UML类图知识整理 UML类图 UML,进阶必备专业技能,看不懂UML就会看不懂那些优秀的资料. 这里简单整理 类之间的关系 泛化关系(generalization) 泛化(generalize ...
- js事件(Event)知识整理[转]
事件注册 平常我们绑定事件的时候用dom.onxxxx=function(){}的形式 这种方式是给元素的onxxxx属性赋值,只能绑定有一个处理句柄. 但很多时候我们需要绑定多个处理句柄到一个事件上 ...
随机推荐
- 【转】Graphics.DrawImage 方法 IntPtr 结构 GDI 句柄 知识收集
Graphics.DrawImage 方法 在指定的位置使用原始物理大小绘制指定的 Image. 命名空间:System.Drawing 程序集:System.Drawing(在 system.dra ...
- Activity相关知识点总结
一.Activity状态 Activity有三种状态:active/running.paused.stopped. 1.active/running状态,在当前屏幕时,即用户可见的Activity,位 ...
- 2016.6.24——vector<vector<int>>【Binary Tree Level Order Traversal】
Binary Tree Level Order Traversal 本题收获: 1.vector<vector<int>>的用法 vector<vector<int ...
- mybatis模糊查询防止SQL注入
SQL注入,大家都不陌生,是一种常见的攻击方式.攻击者在界面的表单信息或URL上输入一些奇怪的SQL片段(例如“or ‘1’=’1’”这样的语句),有可能入侵参数检验不足的应用程序.所以,在我们的应用 ...
- jenkins 和 git 的每日构建
没有太难的技术含量,只要按照步骤操作就可以成功 step 1:全局工具配置git.exe 首先,登录 Jenkins ,在首页找到 “系统管理 -> Global Tool Configurat ...
- Tslib移植与分析【转】
转自:http://blog.csdn.net/water_cow/article/details/7215308 目标平台:LOONGSON-1B开发板(mips32指令集)编译平台:x86PC-- ...
- 如何使用vs2012单步调试uGUI(unity3d 5.3f4)
下载uGUI源代码 uGUI源代码地址:https://bitbucket.org/Unity-Technologies/ui 下载代码工具:tortoisehg-3.6.2-x64.msi http ...
- HTML5学习--SVG全攻略(基础篇)
明天高级篇 一.什么是SVG? SVG 指的是可伸缩矢量图形 (Scalable Vector Graphics),它用来定义用于网络的基于矢量的图形,使用 XML 格式定义图形.SVG 图像在放大或 ...
- Motan
https://github.com/weibocom/motan/wiki/zh_userguide http://www.cnblogs.com/mantu/p/5885996.html(源码分析 ...
- 数据库-mysql中文显示问题
一:在mysql 下面查看带中文的记录显示乱码 mysql> select * from role; +----+------+ | id | name | +----+------+ | 1 ...