D08——C语言基础学PYTHON
Posted m1racle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了D08——C语言基础学PYTHON相关的知识,希望对你有一定的参考价值。
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,))
1 #Author:ZhangKanghui 2 3 import socket 4 server =socket.socket() 5 #绑定要监听的端口 6 server.bind((\'localhost\',6969)) 7 server.listen() #监听 8 9 print("我的心在等待!") 10 #server.accept() 这个地方不能直接接听,如果这样的话就只能有一个接入 11 conn,addr = server.accept() #等待 12 #coon就是客户端连过来而在服务器端为其生成的一个链接实例 13 #print(conn,addr) 可以通过这样来看看接听了什么 14 print(conn,addr) 15 16 print("一直在等待!") 17 data =conn.recv(1024) 18 print("recv:",data) 19 20 21 conn.send(data.upper()) 22 server.close()
1 #Author:ZhangKanghui 2 3 import socket 4 #首先声明socket类型,同时生成socket链接对象 5 client = socket.socket() 6 # family =AF_INET他就是地址簇,默认就是IPv4,type=SOCK_STWEAM他就是TCP/TCP 7 8 client.connect((\'localhost\',6969)) #只能传一个参数,所以把链接地址端口放进一个元组。 9 #发送数据 10 client.send(b"hello world") #发送的数据必须是bytes类型 11 #那么能不能发送中文呢 12 #client.send("study 使我快乐".encode("utf-8")) 13 #如果想输出中文 14 #client.send("我想要mopney".encode("utf-8")) 15 #接受服务器的返回 16 data =client.recv(1024) #buffersize缓冲区空间容量,1024字节=1K 17 print("recv:",data) 18 #如果要输出中文 19 #print("recv:",data.decode()) 20 client.close()
接下来依次启动服务器端和客户端:
先启动服务器端:
再启动客户端:
再看服务器端接收了什么?
这么看来基本的功能已经实现了。那么这有没有什么漏洞呢?在执行一次客户端试试看?
这是因为现在还只能接收一个,不能实现并发。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头。
那么,现在对上面的代码开始进行优化:只能收发一条但是能够实现多用户收发。
1 #Author:ZhangKanghui 2 3 import socket 4 server =socket.socket() 5 6 server.bind((\'localhost\',6969)) 7 server.listen() 8 print("我的心在等待!") 9 10 while True: 11 conn,addr = server.accept() 12 print(conn,addr) 13 print("一直在等待!") 14 data =conn.recv(1024) 15 print("recv:",data) 16 conn.send(data.upper()) 17 server.close()
1 #Author:ZhangKanghui 2 3 import socket 4 5 client = socket.socket() 6 7 client.connect((\'localhost\',6969)) 8 while True: 9 msg = input(">>:").strip() 10 11 client.send(msg.encode("utf-8")) 12 data =client.recv(1024) 13 print("recv:",data) 14 15 client.close()
通过运行结果可以发现,每个客户端只能像服务器发送一条数据,下条会自动挂起,因为这个时候服务器在等待第二个响应。
那么接下来怎么再次优化呢?一个用户可以多次收发,但不支持多用户同时收发。
只需要对服务器端进行修改即可:
1 #Author:ZhangKanghui 2 3 import socket 4 server =socket.socket() 5 6 server.bind((\'localhost\',6969)) 7 server.listen() 8 print("我的心在等待!") 9 conn,addr = server.accept() 10 print(conn,addr) 11 print("一直在等待!") 12 13 while True: 14 data =conn.recv(1024) 15 print("recv:",data) 16 conn.send(data.upper()) 17 server.close()
问题又来了,不能支持多用户,如果先断开,那第二个客户端能不能连进来呢?不能
1 #Author:ZhangKanghui 2 3 import socket 4 server =socket.socket() 5 6 server.bind((\'localhost\',9999)) 7 server.listen() 8 conn,addr = server.accept() 9 print(conn,addr) 10 print("一直在等待!") 11 print("我的心在等待!") 12 13 while True: 14 try: 15 # conn,addr = server.accept() 16 # print(conn,addr) 17 # print("一直在等待!") 18 data =conn.recv(1024) 19 print("recv:",data) 20 conn.send(data.upper()) 21 except ConnectionResetError as e: 22 print("Error",e) 23 break 24 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:
1 #Author:ZhangKanghui 2 3 import socketserver 4 5 class MyTCPHandler(socketserver.BaseRequestHandler): 6 """ 7 The request handler class for our server. 8 9 It is instantiated once per connection to the server, and must 10 override the handle() method to implement communication to the 11 client. 12 """ 13 #跟客户端的所有交互都是在handle中完成的 14 def handle(self): 15 while True: 16 try: 17 # self.request is the TCP socket connected to the client 18 self.data = self.request.recv(1024).strip() 19 print("{} wrote:".format(self.client_address[0])) #打印客户端的ip地址 20 print(self.data) 21 # just send back the same data, but upper-cased 22 self.request.sendall(self.data.upper()) 23 except ConnectionResetError as e: 24 print("Error",e) 25 break 26 27 28 if __name__ == "__main__": 29 HOST, PORT = "localhost", 9999 30 31 # Create the server, binding to localhost on port 9999 32 server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) 33 #实例化 34 server.serve_forever()
1 #Author:ZhangKanghui 2 3 import socket 4 client = socket.socket() 5 client.connect((\'localhost\',9999)) 6 while True: 7 msg = input(">>:").strip() 8 if len(msg) == 0: continue 9 client.send(msg.encode("utf-8")) 10 data =client.recv(1024) 11 print("recv:",data) 12 13 client.close()
此时,能不能支持多并发呢?我们可以多次启动客户端,发现不能。只有先断开1才会执行2,断开2才会执行3。
那么如何实现多并发呢?其实很简单。 只需要在服务器端修改TCPServer为ThreadingTCPServer即可。
ThreadingTCPServer就是多线程。什么事多线程呢?看后续
3 socket实现简单的SSH服务器端和客户端
通过os模块调用命令行。
1 #Author:ZhangKanghui 2 import os 3 import socket 4 server = socket.socket() 5 server.bind((\'localhost\',9999)) 6 7 server.listen() 8 9 while True: 10 conn,addr = server.accept() 11 print("new coon",addr) 12 while True: 13 data = conn.recv(1024) 14 if not data: 15 print("客户端已断开") 16 break 17 print("执行指令:",data) 18 cmd_res = os.popen(data.decode()).read() 19 if len(cmd_res) == 0: 20 cmd_res = "cmd has no output" 21 22 conn.send(cmd_res.encode("utf-8")) 23 24 server.close()
1 #Author:ZhangKanghui 2 3 import socket 4 client = socket.socket() 5 6 client.connect((\'localhost\',9999)) 7 8 while True: 9 cmd = input(">>:").strip() 10 if len(cmd) == 0:continue 11 client.send(cmd.encode("utf-8")) 12 cmd_res = client.recv(1024) 13 14 print(cmd_res.decode()) 15 16 client.close()
当使用存在的指令时,会有正常输出,但是当指令不存在时,通过判断让程序正常进行了。这个以后会有更好的解决办法
可以通过以下几个指令试一下,看一下执行结果
dir pwd ipconfig
运行结果会发现竟然不全,那究竟是为什么呢?客户端接收1024剩下存在服务器端的内存里,就一直存在缓冲区中,如果不接收完会影响下一条指令的正常输出。
那么怎样能够接收完呢?
思路是这样的,在服务器端发送给客户端时先判断数据大小,客户端接收数据大小之后,可以通过循环每次1024接收,直至传输完毕。
修改之后代码是这样的。可以试试对比一下有什么不同?再次执行ipconfig
1 #Author:ZhangKanghui 2 import os 3 import socket 4 server = socket.socket() 5 server.bind((\'localhost\',9999)) 6 7 server.listen() 8 9 while True: 10 conn,addr = server.accept() 11 print("new coon",addr) 12 while True: 13 data = conn.recv(1024) 14 if not data: 15 print("客户端已断开") 16 break 17 print("执行指令:",data) 18 cmd_res = os.popen(data.decode()).read() 19 if len(cmd_res) == 0: 20 cmd_res = "cmd has no output" 21 conn.send(str(len(cmd_res.encode())).encode("utf-8")) #先发大小给客户端 22 conn.send(cmd_res.encode("utf-8")) 23 24 server.close()
1 #Author:ZhangKanghui 2 3 import socket 4 client = socket.socket() 5 6 client.connect((\'localhost\',9999)) 7 8 while True: 9 cmd = input(">>:").strip() 10 if len(cmd) == 0:continue 11 client.send(cm以上是关于D08——C语言基础学PYTHON的主要内容,如果未能解决你的问题,请参考以下文章