C语言基础学习PYTHON——基础学习D08
20180829内容纲要:
socket网络编程
1 socket基础概念
2 socketserver
3 socket实现简单的SSH服务器端和客户端
4 粘包
5 小结
6 练习
0 我是小白
先认识一些关键词:
TCP(Transmission Control Protocol传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,
由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,用户数据报协议(UDP)是同一层内另一个重要的传输协议。
SSH(SecureShell 。安全外壳协议)由IETF的网络工作小组所指定;SSH为建立在应用层和传输层基础上的安全协议。
SSH是目前较为可靠,专为远程登录会话和其他网络服务提供安全的协议,利用SSH协议可以有效防止远程管理过程中的信息泄露问题,SSH最初是UNIX系统上的一个程序,后来又迅速扩展到其他操作平台。SSH在正确使用时可弥补网络中的漏洞。SSH客户端适用于多种平台。几乎所有UNIX平台—包括HP-UX、Linux、AIX、Solaris、DigitalUNIX、Irix,以及其他平台,都可运行SSH。
FTP(FileTransfer Protocol。文件传输协议) 是 TCP/IP 协议组中的协议之一。
FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。
其中FTP服务器用来存储文件,用户可以使用FTP客户端通过FTP协议访问位于FTP服务器上的资源。
在开发网站的时候,通常利用FTP协议把网页或程序传到Web服务器上。此外,由于FTP传输效率非常高,在网络上传输大的文件时,一般也采用该协议。
1 socket基础概念
socket的英文原义是“孔”或“插座”。作为4BDS UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。
socket本质上就是在2台网络互通的电脑之间,架设一个通道,两台电脑通过这个通道来实现数据的互相传递。
网络通信都是基于 ip+port 方能定位到目标的具体机器上的具体服务,操作系统有0-65535个端口,每个端口都可以独立对外提供服务
如果 把一个公司比做一台电脑 ,那公司的总机号码就相当于ip地址, 每个员工的分机号就相当于端口, 你想找公司某个人,必须 先打电话到总机,然后再转分机 。
建立一个socket必须至少有2端, 一个服务端,一个客户端, 服务端被动等待并接收请求,客户端主动发起请求, 连接建立之后,双方可以互发数据。
话不多先来看一段最简单的代码:
服务器端:
- Socket( ):第一步创建一个 socket 对象,这是用来封装 TCP/IP的过程,之后就可以利用它来发送 TCP 或者是 UDP. e.g. s = socket.socket( )
- bind( ):第二步是绑定 IP 和端口,它接受一个元组类型的数据。e.g. s.bind(('127.0.0.1',8088,))
- listen( ):第三步是定义最多能挂起的数目,e.g. s.listen(2),意思说你当前允许一个客户端在连接,两个客户端在等待发送消息(挂起)。
- accept( ):第四步是创建客户端和服务端之间的那条连接 conn,程序在连接前会处于挂起的状态。 e.g. conn, addr = s.accept( )
客户端:
- Socket( ):第一步创建一个 socket 对象,这是用来封装 TCP/IP的过程,之后就可以利用它来发送 TCP 或者是 UDP. e.g. s = socket.socket( )
- connect( ):第二步客户端用自己的对象来连接服务端,它接受一个元组类型的数据。e.g. s.connect(('127.0.0.1',8088,))
#Author:ZhangKanghui import socket
server =socket.socket()
#绑定要监听的端口
server.bind(('localhost',6969))
server.listen() #监听 print("我的心在等待!")
#server.accept() 这个地方不能直接接听,如果这样的话就只能有一个接入
conn,addr = server.accept() #等待
#coon就是客户端连过来而在服务器端为其生成的一个链接实例
#print(conn,addr) 可以通过这样来看看接听了什么
print(conn,addr) print("一直在等待!")
data =conn.recv(1024)
print("recv:",data) conn.send(data.upper())
server.close()
socket_server(基础版)
#Author:ZhangKanghui import socket
#首先声明socket类型,同时生成socket链接对象
client = socket.socket()
# family =AF_INET他就是地址簇,默认就是IPv4,type=SOCK_STWEAM他就是TCP/TCP client.connect(('localhost',6969)) #只能传一个参数,所以把链接地址端口放进一个元组。
#发送数据
client.send(b"hello world") #发送的数据必须是bytes类型
#那么能不能发送中文呢
#client.send("study 使我快乐".encode("utf-8"))
#如果想输出中文
#client.send("我想要mopney".encode("utf-8"))
#接受服务器的返回
data =client.recv(1024) #buffersize缓冲区空间容量,1024字节=1K
print("recv:",data)
#如果要输出中文
#print("recv:",data.decode())
client.close()
socket_client(基础版)
接下来依次启动服务器端和客户端:
先启动服务器端:
再启动客户端:
再看服务器端接收了什么?
这么看来基本的功能已经实现了。那么这有没有什么漏洞呢?在执行一次客户端试试看?
这是因为现在还只能接收一个,不能实现并发。c面会有学习。别急慢慢来~而且只能发送一条指令,接收一条指令,这能干毛线啊?
磨刀不误砍柴工,先来看一些基础:关于server =socket.socket()
Socket Families(地址簇)
socket.
AF_UNIX unix本机进程间通信
socket.
AF_INET IPV4
socket.
AF_INET6 IPV6
Socket Types
socket.
SOCK_STREAM #for tcp
socket.
SOCK_DGRAM #for udp
socket.
SOCK_RAW #原始套接字。
利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
那么,现在对上面的代码开始进行优化:只能收发一条但是能够实现多用户收发。
#Author:ZhangKanghui import socket
server =socket.socket() server.bind(('localhost',6969))
server.listen()
print("我的心在等待!") while True:
conn,addr = server.accept()
print(conn,addr)
print("一直在等待!")
data =conn.recv(1024)
print("recv:",data)
conn.send(data.upper())
server.close()
服务器端
#Author:ZhangKanghui import socket client = socket.socket() client.connect(('localhost',6969))
while True:
msg = input(">>:").strip() client.send(msg.encode("utf-8"))
data =client.recv(1024)
print("recv:",data) client.close()
客户端
通过运行结果可以发现,每个客户端只能像服务器发送一条数据,下条会自动挂起,因为这个时候服务器在等待第二个响应。
那么接下来怎么再次优化呢?一个用户可以多次收发,但不支持多用户同时收发。
只需要对服务器端进行修改即可:
#Author:ZhangKanghui import socket
server =socket.socket() server.bind(('localhost',6969))
server.listen()
print("我的心在等待!")
conn,addr = server.accept()
print(conn,addr)
print("一直在等待!") while True:
data =conn.recv(1024)
print("recv:",data)
conn.send(data.upper())
server.close()
服务器端
问题又来了,不能支持多用户,如果先断开,那第二个客户端能不能连进来呢?不能
#Author:ZhangKanghui import socket
server =socket.socket() server.bind(('localhost',9999))
server.listen()
conn,addr = server.accept()
print(conn,addr)
print("一直在等待!")
print("我的心在等待!") while True:
try:
# conn,addr = server.accept()
# print(conn,addr)
# print("一直在等待!")
data =conn.recv(1024)
print("recv:",data)
conn.send(data.upper())
except ConnectionResetError as e:
print("Error",e)
break
server.close()
服务器端改进
当客户端断开时可以通过捕获异常让程序正常进行。这样才能继续接收第二个。
2 socketserver
The socketserver
module simplifies the task of writing network servers.
There are five classes in an inheritance diagram, four of which represent synchronous servers of four types:
主要介绍TCPserver:
class socketserver.
TCPServer
(server_address, RequestHandlerClass, bind_and_activate=True)
This uses the Internet TCP protocol, which provides for continuous streams of data between the client and server. If bind_and_activate is true,
the constructor automatically attempts to invoke server_bind()
andserver_activate()
.
The other parameters are passed to the BaseServer
base class.
创建一个socketserver步骤:
1 first,you must create a request handler class by subclassing the BaseRequestHandler class and overrinding(覆盖) its handle() method ;
this method will process incoming request.
你必须创建一个请求处理类,并且这个类要继承BaseRequestHandler,并且还要覆盖父类里的handle()
2 second, you must instantiate one of the server classes, passing its server's address and the request handler class.
你必须实例化TCPServer,并且传递server ip和上面创建的请求处理类传给TCPServer。
3 third, call the handle_request() or server_forever() method of the server object to process one or many requests.
server.handle_request() #只处理一个请求
server.handle_forever() #处理多个请求,永远执行
4 finally, call server_close() to close the socket.
那么接下来就创建一个socketserver:
#Author:ZhangKanghui import socketserver class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The request handler class for our server. It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
#跟客户端的所有交互都是在handle中完成的
def handle(self):
while True:
try:
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0])) #打印客户端的ip地址
print(self.data)
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper())
except ConnectionResetError as e:
print("Error",e)
break if __name__ == "__main__":
HOST, PORT = "localhost", 9999 # Create the server, binding to localhost on port 9999
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
#实例化
server.serve_forever()
Socketserver
#Author:ZhangKanghui import socket
client = socket.socket()
client.connect(('localhost',9999))
while True:
msg = input(">>:").strip()
if len(msg) == 0: continue
client.send(msg.encode("utf-8"))
data =client.recv(1024)
print("recv:",data) client.close()
Client
此时,能不能支持多并发呢?我们可以多次启动客户端,发现不能。只有先断开1才会执行2,断开2才会执行3。
那么如何实现多并发呢?其实很简单。 只需要在服务器端修改TCPServer为ThreadingTCPServer即可。
ThreadingTCPServer就是多线程。什么事多线程呢?看后续
3 socket实现简单的SSH服务器端和客户端
通过os模块调用命令行。
#Author:ZhangKanghui
import os
import socket
server = socket.socket()
server.bind(('localhost',9999)) server.listen() while True:
conn,addr = server.accept()
print("new coon",addr)
while True:
data = conn.recv(1024)
if not data:
print("客户端已断开")
break
print("执行指令:",data)
cmd_res = os.popen(data.decode()).read()
if len(cmd_res) == 0:
cmd_res = "cmd has no output" conn.send(cmd_res.encode("utf-8")) server.close()
SSH服务器端
#Author:ZhangKanghui import socket
client = socket.socket() client.connect(('localhost',9999)) while True:
cmd = input(">>:").strip()
if len(cmd) == 0:continue
client.send(cmd.encode("utf-8"))
cmd_res = client.recv(1024) print(cmd_res.decode()) client.close()
SSH客户端
当使用存在的指令时,会有正常输出,但是当指令不存在时,通过判断让程序正常进行了。这个以后会有更好的解决办法
可以通过以下几个指令试一下,看一下执行结果
dir pwd ipconfig
运行结果会发现竟然不全,那究竟是为什么呢?客户端接收1024剩下存在服务器端的内存里,就一直存在缓冲区中,如果不接收完会影响下一条指令的正常输出。
那么怎样能够接收完呢?
思路是这样的,在服务器端发送给客户端时先判断数据大小,客户端接收数据大小之后,可以通过循环每次1024接收,直至传输完毕。
修改之后代码是这样的。可以试试对比一下有什么不同?再次执行ipconfig
#Author:ZhangKanghui
import os
import socket
server = socket.socket()
server.bind(('localhost',9999)) server.listen() while True:
conn,addr = server.accept()
print("new coon",addr)
while True:
data = conn.recv(1024)
if not data:
print("客户端已断开")
break
print("执行指令:",data)
cmd_res = os.popen(data.decode()).read()
if len(cmd_res) == 0:
cmd_res = "cmd has no output"
conn.send(str(len(cmd_res.encode())).encode("utf-8")) #先发大小给客户端
conn.send(cmd_res.encode("utf-8")) server.close()
SSH服务器端
#Author:ZhangKanghui import socket
client = socket.socket() client.connect(('localhost',9999)) while True:
cmd = input(">>:").strip()
if len(cmd) == 0:continue
client.send(cmd.encode("utf-8"))
cmd_res_size = client.recv(1024) #先接收命令结果的大小
print("命令结果大小:",cmd_res_size) received_size = 0
#received_data = b''
while received_size != int(cmd_res_size.decode()):
data = client.recv(1024)
received_size+=len(data)
print(received_size)
print(data.decode())
else:
print("cmd res received done",received_size) cmd_res = client.recv(1024) print(cmd_res.decode()) client.close()
SSH客户端
我们看一下服务器中的一段代码:
两次发送,系统有时会把它们合在一起发出去,就出现了粘包~!因为不知道缓冲区什么时候会把这两条命令放在一起。
那下面来了解一下粘包。
4 粘包
TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据。TCP通讯为何存在粘包呢?
主要原因是TCP是以流的方式来处理数据,再加上网络上MTU的往往小于在应用处理的消息数据,所以就会引发一次接收的数据无法满足消息的需要,导致粘包的存在。
那么我们如何解决呢?
第一种解决方法:
首先通过一种缓冲区接收数据超时,发送命令来间隔两次命令传输,但这样的后果就是延长数据传输时间,降低了传输效率。
这种方法显然不太可取。太low了~!
下面来个正确姿势解决粘包:
第二种解决方法:
让客户端给服务器端一个确认指令,通过确认指令间隔两次命令传输,这样就能阻隔两次命令的粘包。
#Author:ZhangKanghui
import os
import time
import socket
server = socket.socket()
server.bind(('localhost',9999)) server.listen() while True:
conn,addr = server.accept()
print("new coon",addr)
while True:
data = conn.recv(1024)
if not data:
print("客户端已断开")
break
print("执行指令:",data)
cmd_res = os.popen(data.decode()).read()
if len(cmd_res) == 0:
cmd_res = "cmd has no output"
conn.send(str(len(cmd_res.encode())).encode("utf-8")) #先发大小给客户端
#time.sleep(0.5)
client_ack = conn.recv(1024) #等待客户端确认,不然就在这里挂起,从而完成上一条发送命令与下一条隔开,解决粘包
print("ack from client:",client_ack)
conn.send(cmd_res.encode("utf-8")) server.close()
服务器端
#Author:ZhangKanghui import socket
client = socket.socket() client.connect(('localhost',9999)) while True:
cmd = input(">>:").strip()
if len(cmd) == 0:continue
client.send(cmd.encode("utf-8"))
cmd_res_size = client.recv(1024) #先接收命令结果的大小
print("命令结果大小:",cmd_res_size)
client("防止粘包发送的确定指令") received_size = 0
#received_data = b''
while received_size != int(cmd_res_size.decode()):
data = client.recv(1024)
received_size+=len(data)
print(received_size)
print(data.decode())
else:
print("cmd res received done",received_size) cmd_res = client.recv(1024) print(cmd_res.decode()) client.close()
客户端
第三种方法:
通过服务器发送给客户端需要接受数据的大小,然后客户端自己判断大小通过每次接1024,最后一次把剩下的接收。也就是说刚好接受文件大小,再继续接受下一个。
这样也能解决粘包问题。同时下面的代码中对文件的传输还进行了MD5加密。关于ftp
ftp server:
1 读取文件名
2 检测文件是否存在
3 打开文件
4 检测文件大小
5 发送文件大小给客户端
6 等待客户端确认
7 开始边读边发数据
8 发送md5
#Author:ZhangKanghui
import os
import time
import socket
import hashlib
server = socket.socket()
server.bind(('localhost',9999)) server.listen() while True:
conn,addr = server.accept()
print("new coon",addr)
while True:
data = conn.recv(1024)
if not data:
print("客户端已断开")
break
cmd,filename = data.decode().split()
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())) #send file size
conn.recv(1024) #wait for ack
for line in f:
m.update(line)
conn.send(line)
print("file md5:",m.hexdiggest())
f.close()
conn.send(m.hexdiggest().encode()) #send md5
server.close()
ftp服务器端
#Author:ZhangKanghui import socket
import os
import hashlib
client = socket.socket() client.connect(('localhost',9999)) while True:
cmd = input(">>:").strip()
if len(cmd) == 0:continue
if cmd.startswith("get"):
client.send(cmd.encode())
server_reponse = client.recv(1024)
print("server response:",server_reponse)
client.send(b"i am ready to recv file")
file_total_size =int(server_reponse.decode())
received_size = 0
filename = cmd.split()[1]
f =open(filename +'.new','wb')
m = hashlib.md5()
while received_size < file_total_size:
if file_total_size - received_size >1024: #表示要收不止一次
size =1024
else: #最后一次剩多少收多少
size =file_total_size - received_size
print("last recv:",size) data =client.recv(size)
received_size+=len(data)
m.update(data)
f.write(data)
#print(file_total_size,received_size)
else:
new_file_md5 =m.hexdigest()
print("flie recv done",file_total_size,received_size)
f.close()
server_file_md5 = client.recv(1024)
print("server file md5",server_file_md5)
print("client file md5",new_file_md5)
client.close()
ftp客户端
5 小结
真的不懂,可能以后会明白的。
安慰别人的话说服不了自己。
间歇性踌躇满志,持续性混吃等死~!
6 练习
作业:开发一个支持多用户在线的FTP程序
要求:
- 用户加密认证
- 允许同时多用户登录
- 每个用户有自己的家目录 ,且只能访问自己的家目录
- 对用户进行磁盘配额,每个用户的可用空间不同
- 允许用户在ftp server上随意切换目录
- 允许用户查看当前目录下文件
- 允许上传和下载文件,保证文件一致性
- 文件传输过程中显示进度条
- 附加功能:
新建目录mkdir
.查看当前工作目录的路径pwd
如果对一些基础概念还不够明确,或者这篇博客的逻辑不过清晰,可以看这里
python之socket_ssh实例 http://www.cnblogs.com/0zcl/p/6017088.html
python之socket_ftp实例 http://www.cnblogs.com/0zcl/p/6022774.html
6.1 程序目录结构
服务器端
客户端
都看到这里了,如果真的想弄明白,那就看这篇博客吧,我自己也不会。以后看明白了再补上。说出这句话自己心里都没底气。
点击这里:https://www.cnblogs.com/0zcl/p/6259128.html
我是尾巴~
这次推荐:以后买来新电脑请看这篇文章:https://mp.weixin.qq.com/s/OAQ3Vn3NRRHE4IfzoONjng
可以说是非常用心了!
虽不才,才要坚持。
D08——C语言基础学PYTHON的更多相关文章
-
D10——C语言基础学PYTHON
C语言基础学习PYTHON——基础学习D10 20180906内容纲要: 1.协程 (1)yield (2)greenlet (3)gevent (4)gevent实现单线程下socket多并发 2. ...
-
D16——C语言基础学PYTHON
C语言基础学习PYTHON——基础学习D16 20180927内容纲要: 1.JavaScript介绍 2.JavaScript功能介绍 3.JavaScript变量 4.Dom操作 a.获取标签 b ...
-
D15——C语言基础学PYTHON
C语言基础学习PYTHON——基础学习D15 20180926内容纲要: 1.CSS介绍 2.CSS的四种引入方式 3.CSS选择器 4.CSS常用属性 5.小结 6.练习 1 CSS介绍 层叠样式表 ...
-
D07——C语言基础学PYTHON
C语言基础学习PYTHON——基础学习D07 20180826内容纲要: 面向对象进阶学习 1 静态方法 2 类方法 3 属性方法 4 类的特殊成员方法(本节重点) 5 反射(本节重点) 6 异常(本 ...
-
D06——C语言基础学PYTHON
C语言基础学习PYTHON——基础学习D06 20180821内容纲要: 面向对象初级学习 1 面向对象 2 类 (1)封装 (2)继承 (3)多态 3 小结 4 练习:选课系统 5 课外拓展:答题系 ...
-
D05——C语言基础学PYTHON
C语言基础学习PYTHON——基础学习D05 20180815内容纲要: 1 模块 2 包 3 import的本质 4 内置模块详解 (1)time&datetime (2)datetime ...
-
D17——C语言基础学PYTHON
C语言基础学习PYTHON——基础学习D17 20181014内容纲要: 1.jQuery介绍 2.jQuery功能介绍 (1)jQuery的引入方式 (2)选择器 (3)筛选 (4)文本操作 (5) ...
-
D14——C语言基础学PYTHON
C语言基础学习PYTHON——基础学习D14 20180919内容纲要: 1.html认识 2.常用标签 3.京东html 4.小结 5.练习(简易淘宝html) 1.html初识(HyperText ...
-
D13——C语言基础学PYTHON
C语言基础学习PYTHON——基础学习D13 20180918内容纲要: 堡垒机运维开发 1.堡垒机的介绍 2.堡垒机的架构 3.小结 4.堡垒机的功能实现需求 1 堡垒机的介绍 百度百科 随着信息安 ...
随机推荐
-
ASP.NET Core的配置(4):多样性的配置来源[上篇]
较之传统通过App.config和Web.config这两个XML文件承载的配置系统,ASP.NET Core采用的这个全新的配置模型的最大一个优势就是针对多种不同配置源的支持.我们可以将内存变量.命 ...
-
C语言程序设计第四次作业
态度决定一切,我依然要说这句话,每次同学们提交的作业,我都会认真评阅,相比实验课而言,可以有更充足的时间来发现问题,很多同学的代码依然会存在一些语法错误或者考虑不周全的现象,我提出了,那么,你认真看了 ...
-
iOS开发工具-网络封包分析工具Charles
转自唐巧的技术博客:http://blog.devtang.com/blog/2013/12/11/network-tool-charles-intr/ Charles是在Mac下常用的截取网络封包的 ...
-
K2上海总部技术培训分享笔记
第一部门 WinDdg 入门指南 1.NGen.exe --> native code 预编译,省去了.NET程序编译器JIT过程,是程序第一次运行也非常快. NGen 参考资料:http:// ...
-
TCP协议RST:RST介绍、什么时候发送RST包
TCP协议RST:RST介绍.什么时候发送RST包 RST标示复位.用来异常的关闭连接. 1. 发送RST包关闭连接时,不必等缓冲区的包都发出去,直接就丢弃缓冲区中的包,发送RST. 2. 而接收端收 ...
-
Lua数组排序
代码 network = { {name = "grauna", IP = "210.26.30.34"}, {name = "arraial&quo ...
-
开源项目:底部动作条(BottomSheet)
底部动作条(BottomSheet)是一个从屏幕底部边缘向上滑出的一个面板,给用户呈现一组功能选项.底部动作条封装了一组简单.清晰.无需额外说明的操作.底部动作条(如下图)可以是列表样式的,也可以是宫 ...
-
python isinstance 函数
isinstance是Python中的一个内建函数 语法: isinstance(object, classinfo) 如果参数object是classinfo的实例,或者object是class ...
-
C++位运算
移位运算 要点 1 它们都是双目运算符,两个运算分量都是整形,结果也是整形. 2 " < <" 左移:右边空出的位上补0,左边的位将从字头挤掉,其值相当于乘2. ...
-
murongxixi的凸优化笔记
http://www.cnblogs.com/murongxixi/p/3598645.html 在前两节里已经涉及到集合的相对内部与闭包的概念,这一节我们深入研究它们的性质和计算,之后介绍凸函数的连 ...