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端, 一个服务端,一个客户端, 服务端被动等待并接收请求,客户端主动发起请求, 连接建立之后,双方可以互发数据。

话不多先来看一段最简单的代码:

服务器端:

  1. Socket( ):第一步创建一个 socket 对象,这是用来封装 TCP/IP的过程,之后就可以利用它来发送 TCP 或者是 UDP. e.g. s = socket.socket( )
  2. bind( ):第二步是绑定 IP 和端口,它接受一个元组类型的数据。e.g. s.bind(('127.0.0.1',8088,))
  3. listen( ):第三步是定义最多能挂起的数目,e.g. s.listen(2),意思说你当前允许一个客户端在连接,两个客户端在等待发送消息(挂起)。
  4. accept( ):第四步是创建客户端和服务端之间的那条连接 conn,程序在连接前会处于挂起的状态。 e.g. conn, addr = s.accept( )

客户端:

  1. Socket( ):第一步创建一个 socket 对象,这是用来封装 TCP/IP的过程,之后就可以利用它来发送 TCP 或者是 UDP. e.g. s = socket.socket( )
  2. 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程序

要求:

  1. 用户加密认证
  2. 允许同时多用户登录
  3. 每个用户有自己的家目录 ,且只能访问自己的家目录
  4. 对用户进行磁盘配额,每个用户的可用空间不同
  5. 允许用户在ftp server上随意切换目录
  6. 允许用户查看当前目录下文件
  7. 允许上传和下载文件,保证文件一致性
  8. 文件传输过程中显示进度条
  9. 附加功能: 

      新建目录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的更多相关文章

  1. D10——C语言基础学PYTHON

    C语言基础学习PYTHON——基础学习D10 20180906内容纲要: 1.协程 (1)yield (2)greenlet (3)gevent (4)gevent实现单线程下socket多并发 2. ...

  2. D16——C语言基础学PYTHON

    C语言基础学习PYTHON——基础学习D16 20180927内容纲要: 1.JavaScript介绍 2.JavaScript功能介绍 3.JavaScript变量 4.Dom操作 a.获取标签 b ...

  3. D15——C语言基础学PYTHON

    C语言基础学习PYTHON——基础学习D15 20180926内容纲要: 1.CSS介绍 2.CSS的四种引入方式 3.CSS选择器 4.CSS常用属性 5.小结 6.练习 1 CSS介绍 层叠样式表 ...

  4. D07——C语言基础学PYTHON

    C语言基础学习PYTHON——基础学习D07 20180826内容纲要: 面向对象进阶学习 1 静态方法 2 类方法 3 属性方法 4 类的特殊成员方法(本节重点) 5 反射(本节重点) 6 异常(本 ...

  5. D06——C语言基础学PYTHON

    C语言基础学习PYTHON——基础学习D06 20180821内容纲要: 面向对象初级学习 1 面向对象 2 类 (1)封装 (2)继承 (3)多态 3 小结 4 练习:选课系统 5 课外拓展:答题系 ...

  6. D05——C语言基础学PYTHON

    C语言基础学习PYTHON——基础学习D05 20180815内容纲要: 1 模块 2 包 3 import的本质 4 内置模块详解 (1)time&datetime (2)datetime ...

  7. D17——C语言基础学PYTHON

    C语言基础学习PYTHON——基础学习D17 20181014内容纲要: 1.jQuery介绍 2.jQuery功能介绍 (1)jQuery的引入方式 (2)选择器 (3)筛选 (4)文本操作 (5) ...

  8. D14——C语言基础学PYTHON

    C语言基础学习PYTHON——基础学习D14 20180919内容纲要: 1.html认识 2.常用标签 3.京东html 4.小结 5.练习(简易淘宝html) 1.html初识(HyperText ...

  9. D13——C语言基础学PYTHON

    C语言基础学习PYTHON——基础学习D13 20180918内容纲要: 堡垒机运维开发 1.堡垒机的介绍 2.堡垒机的架构 3.小结 4.堡垒机的功能实现需求 1 堡垒机的介绍 百度百科 随着信息安 ...

随机推荐

  1. Devexpress VCL Build v2013 vol 13.2.5 发布

    支持xe6 了,但是承诺的功能在哪里? What's New in 13.2.5 (VCL Product Line)   New Major Features in 13.2 What's New ...

  2. 41 Pain and Pain Management 疼痛与疼痛管理

    Pain and Pain Management 疼痛与疼痛管理 ①Years ago,doctors often said that pain was a normal part of life.I ...

  3. 实现WIFI MAC认证与漫游

    前言 单位里有10来个网件的AP(WNAP210),需要对接入端(主要是手机)进行MAC认证,原来采用AP本地MAC认证,但是人员经常变动(离职),另外人员的岗位(流水线)也经常调整,这样就需在变动后 ...

  4. 03-vue-router

    前端路由的实现原理 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  5. linxu ssh 双端认证 不成功之authorized_keys

    linxu ssh 双端认证 不成功之authorized_keys liunx双端认证可以让我们更简便的在两台服务器之间传输文件,配置暂且不说,网上有大部分的文章可以搜索到,今天我要说的是在不成功的 ...

  6. AngularJS标准Web业务流程开发框架—1.AngularJS模块以及启动分析

    前言: AngularJS中提到模块是自定义的模块标准,提到这不得不说AngularJS是框架中的老大哥,思想相当的前卫..在这框架满天横行的时代,AngularJS有些思想至今未被超越,当然仁者见仁 ...

  7. 代码自动生成工具MyGeneration之一

    前段时间用C#做网站,用到了大量数据库相关的东西.网站采用3层结构,即数据访问层(Data Access Layer),业务逻辑层(Business Logic Layer),页面表现层().做了一段 ...

  8. 记录:Web相关政策之备案号、视频播放

    (一)备案号链接: 服务器在国内的网站受工信部监管,并受其颁布的<管理办法>约束.根据<互联网信息服务管理办法>及<非经营性互联网信息服务备案管理办法>的法律法规, ...

  9. 小强 ROS 机器人教程

    首先请您自行依据线标提示将小强接线连接好,完整结构如下两图所示: 小强是属于Turtlebot机器人.它由底盘.主机.Kinect相机(通过USB连接主机)组成,没有显示屏.如果要通过显示器查看主机的 ...

  10. IT人员如何有效规划自己时间

          中午和任职 DBA 的朋友聊天,他说:"老板老是要我把工作时间填长一点,让整个部门的工作时间比较好看,所以本来做 1 个小时的事要写 3 个小时."但我回答说若这件事情 ...