OSI(Open System Interconnect),即开放式系统互联。
ISO(International Standards Organization)国际标准化组织
OSI七层模型:
TCP/IP协议:
TCP三次握手:
TCP四次挥手:
TCP通信:
模拟简单的客户端-服务器TCP通信
socket_server.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: wanghuafeng import socket
ip_port = ('127.0.0.1', 9999)
#买手机
s = socket.socket()
#买手机卡,bind中的参数为元祖
s.bind(ip_port)
#开机
s.listen(5)
#等待电话,conn为通信链路
conn, addr = s.accept()
#收消息,1024--代表字节
recv_data = conn.recv(1024)
print(str(recv_data, encoding='utf-8'))
#发消息
send_data = recv_data.upper()
conn.send(send_data)
#挂电话
conn.close()
socket_client.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: wanghuafeng import socket
ip_port = ('127.0.0.1', 9999)
#买手机
sk = socket.socket()
#拨号
sk.connect(ip_port)
#发消息
send_data = input(">>>: ").strip()
sk.send(bytes(send_data, encoding='utf-8'))
#收消息
recv_data = sk.recv(1024)
print(recv_data)
#挂电话
sk.close()
UDP通信
# 服务端
import socketip_port = ('127.0.0.1',9999)
sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
sk.bind(ip_port)
while True:
data,(host,port) = sk.recvfrom(1024)print(data,host,port)
sk.sendto(bytes('ok', encoding='utf-8'), (host,port))
#客户端
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
while True:inp = input('数据:').strip()
if inp == 'exit':
break
sk.sendto(bytes(inp, encoding='utf-8'),ip_port)
data = sk.recvfrom(1024)
print(data)
sk.close()
更多功能
sk.bind(address)
s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。
sk.listen(backlog)
开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
这个值不能无限大,因为要在内核中维护连接队列sk.setblocking(bool)
是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。
sk.accept()
接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
接收TCP 客户的连接(阻塞式)等待连接的到来
sk.connect(address)
连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
sk.connect_ex(address)
同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061
sk.close()
关闭套接字
sk.recv(bufsize[,flag])
接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。
sk.recvfrom(bufsize[.flag])
与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
sk.send(string[,flag])
将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。
sk.sendall(string[,flag])
将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
内部通过递归调用send,将所有内容发送出去。
sk.sendto(string[,flag],address)
将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。
sk.settimeout(timeout)
设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )
sk.getpeername()
返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
sk.getsockname()
返回套接字自己的地址。通常是一个元组(ipaddr,port)
sk.fileno()
套接字的文件描述符
循环发送消息
socket_server.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: wanghuafeng import socket
ip_port = ('127.0.0.1', 9999)
#买手机
s = socket.socket()
#买手机卡,bind中的参数为元祖
s.bind(ip_port)
#开机
s.listen(5)
#等待电话,conn为通信链路
conn, addr = s.accept()
while True:
try:
#收消息,1024--代表字节
recv_data = conn.recv(1024)
print(str(recv_data, encoding='utf-8'))
if str(recv_data, encoding='utf-8') == 'exit':break
#发消息
send_data = recv_data.upper()
conn.send(send_data)
except Exception:
break
#挂电话
conn.close()
socket_client.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: wanghuafeng import socket
ip_port = ('127.0.0.1', 9999)
#买手机
sk = socket.socket()
#拨号
sk.connect(ip_port)
#发消息
while True:
send_data = input(">>>: ").strip()
if len(send_data) == 0:continue
sk.send(bytes(send_data, encoding='utf-8'))
if send_data == 'exit':break
#收消息
recv_data = sk.recv(1024)
print(recv_data)
#挂电话
sk.close()
发送windows命令并返回结果
socket_server.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: wanghuafeng import socket
import subprocess
ip_port = ('127.0.0.1', 9999)
#买手机
s = socket.socket()
#买手机卡,bind中的参数为元祖
s.bind(ip_port)
#开机
s.listen(5)
#等待电话,conn为通信链路
#重复接收多个连接
while True:
conn, addr = s.accept()
while True:
try:
#收消息,1024--代表字节
recv_data = conn.recv(1024)
print(str(recv_data, encoding='utf8'))
if str(recv_data, encoding='utf8') == 'exit':break
#发消息
# 在Windows服务器获取命令
p = subprocess.Popen(str(recv_data, encoding='utf8'), shell=True, stdout=subprocess.PIPE)
res = p.stdout.read()
if len(res) == 0:
send_data = "cmd error"
else:
send_data = str(res, encoding='gbk')
print(send_data)
conn.send(bytes(send_data, encoding='utf8'))
except Exception:
break
#挂电话
conn.close()
socket_client.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: wanghuafeng import socket
ip_port = ('127.0.0.1', 9999)
#买手机
sk = socket.socket()
#拨号
sk.connect(ip_port)
#发消息
while True:
send_data = input(">>>: ").strip()
if len(send_data) == 0:continue
sk.send(bytes(send_data, encoding='utf8'))
if send_data == 'exit':break
#收消息
recv_data = sk.recv(1024)
print(str(recv_data, encoding='utf8'))
#挂电话
sk.close()
解决粘包
socket_server.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: wanghuafeng import socket
import subprocess #导入执行命令模块
ip_port = ('127.0.0.1', 9999) #定义IP和端口的元组
#买手机
s = socket.socket() #绑定协议,生成套接字
#买手机卡,bind中的参数为元祖
s.bind(ip_port) #绑定IP+协议+端口,用来唯一标识一个进程,ip_port必须是元组格式
#开机
s.listen(5) #定义最大可以挂起链接数
#等待电话,conn为通信链路
#重复接收多个连接
while True: #用来重复接收新的链接
conn, addr = s.accept() #接收客户端链接请求,返回conn(相当于通信信道,addr是客户端IP+Port)
while True: #用于基于一个链接重复收发消息
try: #捕捉客户端异常关闭(ctrl+c)
#收消息,1024--代表字节
recv_data = conn.recv(1024) #收消息,阻塞
if len(recv_data) == 0:break #客户端如果退出,服务端将收到空消息,退出
if str(recv_data, encoding='utf8') == 'exit':break #客户端发送exit消息,表示退出,服务端将退出
#发消息
# 在Windows服务器获取命令,并执行命令,Windows平台标准输出是gbk编码需要转换
p = subprocess.Popen(str(recv_data, encoding='utf8'), shell=True, stdout=subprocess.PIPE)
res = p.stdout.read() #获取标准输出
if len(res) == 0: #执行错误命令,标准输出为空
send_data = "cmd error"
else:
send_data = str(res, encoding='gbk') #执行ok,字符编码转换,gbk-->str-->utf8字节
send_data = bytes(send_data, encoding='utf8') # 解决粘包问题
#准备发送之前,先发送‘Ready|内容长度’
ready_tag = 'Ready|%s' %len(send_data)
conn.send(bytes(ready_tag, encoding='utf8'))
#得到客户端返回‘Start’表示客户端已经接收到‘Ready|内容长度’
feedback = conn.recv(1024) #Start
#接收的内容为bytes类型,需要转换为str类型
feedback = str(feedback, encoding='utf8')
if feedback.startswith('Start'):
conn.send(send_data) #发送命令执行结果
except Exception:
break
#挂电话
conn.close()
socket_client.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: wanghuafeng import socket
ip_port = ('127.0.0.1', 9999)
#买手机
sk = socket.socket()
#拨号
sk.connect(ip_port) #连接服务端,若服务端有一个已经存在的链接,则连接挂起
#发消息
while True: #基于connect建立的链接来循环发送消息
send_data = input(">>>: ").strip()
if len(send_data) == 0:continue
sk.send(bytes(send_data, encoding='utf8'))
if send_data == 'exit':break #解决粘包问题
#接收消息,其实是为了得到‘Ready|内容长度’
ready_tag = sk.recv(1024)
ready_tag = str(ready_tag, encoding='utf8')
#收到‘Ready|内容长度’
if ready_tag.startswith("Ready"):
#获取待接收数据的长度
msg_size = int(ready_tag.split('|')[-1])
#准备一个‘Start’
start_tag = 'Start'
#发送一个‘Start’消息,通知服务端可以发送消息了
sk.send(bytes(start_tag, encoding='utf8')) recv_size = 0
recv_msg = b''
while recv_size < msg_size:
recv_data = sk.recv(1024)
recv_msg += recv_data
recv_size += len(recv_data)
print('MSG SIZE %s RECV SIZE %s' % (msg_size, recv_size))
print(str(recv_msg, encoding='utf8'))
#挂电话
sk.close()
socketserver解决粘包问题
import socketserver
import subprocessclass Myserver(socketserver.BaseRequestHandler):
def handle(self):while True:
conn=self.request
conn.sendall(bytes("欢迎登录","utf8"))
while True:
client_bytes=conn.recv(1024)
if not client_bytes:break
client_str=str(client_bytes,"utf8")
print(client_str)
command=client_str
result_str=subprocess.getoutput(command)
result_bytes = bytes(result_str,encoding='utf8')info_str="info|%d"%len(result_bytes)
conn.sendall(bytes(info_str,"utf8"))
# conn.recv(1024)
conn.sendall(result_bytes)
conn.close()
if __name__=="__main__":
server=socketserver.ThreadingTCPServer(("127.0.0.1",9998),Myserver)server.serve_forever()
#####################################client
import socket
ip_port=("127.0.0.1",9998)
sk=socket.socket()
sk.connect(ip_port)print("客户端启动...")
print(str(sk.recv(1024),"utf8"))
while True:
inp=input("please input:").strip()sk.sendall(bytes(inp,"utf8"))
basic_info_bytes=sk.recv(1024)
print(str(basic_info_bytes,"utf8"))
# sk.send(bytes('ok','utf8'))
result_length=int(str(basic_info_bytes,"utf8").split("|")[1])
print(result_length)
has_received=0content_bytes=bytes()
while has_received<result_length:
fetch_bytes=sk.recv(1024)
has_received+=len(fetch_bytes)
content_bytes+=fetch_bytes
cmd_result=str(content_bytes,"utf8")
print(cmd_result)
sk.close()
实现简单FTP功能
ftp_server.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:WangHuafeng import socketserver
import subprocess
import json class MyServer(socketserver.BaseRequestHandler):
def handle(self):
self.request.sendall(bytes("欢迎使用Python Server!", encoding='utf-8'))
while True:
data = self.request.recv(1024)
if len(data) == 0:break
print('data', data)
print("[%s] says:%s" % (self.client_address, data.decode()))
task_data = json.loads(data.decode())
task_action = task_data.get('action')
if hasattr(self, 'task_%s' % task_action):
func = getattr(self, 'task_%s' % task_action)
func(task_data)
else:
print('task action is not supported', task_action) def task_put(self, *args, *kwargs):
print("---put", args, kwargs)
filesize = args[0].get('file_size')
filename = args[0].get('filename')
server_response = {'status':200}
self.request.send(bytes(json.dumps(server_response), encoding='utf-8'))
recv_size = 0
with open(filename, 'wb') as f:
while recv_size < filesize:
data = self.request.recv(4096)
f.write(data)
recv_size += len(data)
print("file recv sucess") if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('192.168.6.130', 8999), MyServer)
server.serve_forever()
ftp_client.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-# Author: wanghuafeng
import socket
import os, json
ip_port = ('192.168.6.130', 8999)
sk = socket.socket()
sk.connect(ip_port)
welcome_msg = sk.recv(1024)
while True:
send_data = input(">>>: ").strip()if len(send_data) == 0:continue
cmd_list = send_data.split()
if len(cmd_list) < 2:continue #ftp命令put /usr/bin/file.txt
task_type = cmd_list[0]
if task_type == 'put':
abs_filepath = cmd_list[1]
if os.path.isfile(abs_filepath):
#MD5,留着
file_size = os.stat(abs_filepath).st_size
file_name = abs_filepath.split('\\')[-1]
print('file:%s size:%s' % (abs_filepath, file_size))
msg_data = {'action':'put', 'filename':file_name, 'file_size':file_size}
sk.send(bytes(json.dumps(msg_data), encoding='utf-8'))
server_confirmation_msg = sk.recv(1024)
confirm_data = json.loads(server_confirmation_msg.decode())
if confirm_data['status'] == 200:
print('start sending file', file_name)
with open(abs_filepath, 'rb') as f:for line in f:
sk.send(line)
print("send file done")
else:
print("\033[31;1mfile [%s] is not exist\033[0m" % abs_filepath)
else:
print("doesn't support task type", task_type)
continue
#sk.send(bytes(send_data, encoding='utf8'))
recv_data = sk.recv(1024)
print(str(recv_data, encoding='utf-8'))
sk.close()