单词九连猜python编程_python_day9_socket

1、socket 套接字工作流程图

2、收发功能

3、不间断一发一收

4、多客户端连接

5、UDP:收发功能

6、UDP:实现时间功能

7、执行命令

8、黏包

socket 套接字工作流程图

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束

AF_UNIX: 基于文件编程

基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

AF_INET: 基于网络编程 有AF_INET6 ipv6

CS架构

SOCK_STREAM: TCP协议 数据流式通信

SOCK_DGRAM: UDP协议 数据报式的套接字

## 收发功能socket服务端

import socket

cat=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

cat.bind(('192.168.0.12',9090))

cat.listen(4)

conn,addr=cat.accept()

msg=conn.recv(1024)

conn.close()

print('接收到的信息: %s'%msg)

conn.send(msg.upper())

cat.close()

socket客户端

import socket

cat=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

cat.connect(('192.168.0.12',9090))

cat.send('xiong'.encode('utf-8'))

data=cat.recv(1024)

print('接收到的信息: %s'%data)

#服务端会主动断开 已经传输完客户端数据的连接,将状态改变为TIME_WAIT, 四次挥手之后确定数据已经完全传输完,直接断开并清理状态连接信息

# 收发都是在操作自己的缓存区

# recv 接收的字节, 由recv用户态的应用程序发起

# 回车: 当前 socket 内核态缓存没数据 对端内核态也就没法收到数据,自然也就卡死了

##不间断一发一收socket服务端

import socket

cat=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

cat.bind(('127.0.0.1',9009))

cat.listen(5)

conn,addr=cat.accept()

while True:

msg=conn.recv(1024) #回车: 当前 socket 内核态缓存没数据 对端内核态也就没法收到数据,自然也就卡死了

print('接收到的信息: %s'%msg.decode('utf-8'))

if msg == b'q' : break

inp = input('输入一个值: ')

conn.send(inp.encode('utf-8'))

continue

conn.close()

cat.close()

socket客户端

import socket

cat=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

cat.connect(('127.0.0.1',9009))

while True:

inp = input('输入一个值: ')

if inp == 'q':

cat.send(inp.encode('utf-8'))

break

cat.send(inp.encode('utf-8'))

msg = cat.recv(1024)

print('接收到的信息: %s' % msg.decode('utf-8'))

continue

cat.close()

## 多客户端连接

# 1、当客户端与服务端建立连接,每次最大客户端连接数由listen控制,我这里最大是5个连接

# 2、多个客户端与服务端建立连接,每次只能有一个客户端与服务端通信,其它队列都会保持在队列中

# 3、unix有些系统使用try except无法解决客户端conn连接突然中断, 可以使用 if not conn: break

socket服务端

import socket

cat=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

cat.bind(('192.168.2.192',9009))

cat.listen(5)

while True: # 客户端退出,如果队列中还有连接那么再重新建立连接

conn,addr=cat.accept() # 连接一次

while True: # 与单个用户建立连接

try:

msg=conn.recv(1024) # 当客户端关掉连接,而服务端连接却没有中断,它就直接报错 ConnectionResetError: [WinError 10054]

except Exception:

break

print('接收到的信息: %s'%msg.decode('utf-8'))

if msg == b'q' : break

conn.send(msg.upper())

conn.close() # 关闭连接

cat.close() # 关闭程序

socket客户端

import socket

cat=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

cat.connect(('192.168.2.192',9009))

while True:

inp = input('输入一个值: ')

if inp == 'q':

cat.send(inp.encode('utf-8'))

break

cat.send(inp.encode('utf-8'))

msg = cat.recv(1024)

print('接收到的信息: %s' % msg.decode('utf-8'))

continue

cat.close()

UDP:收发功能

1、udp不需要建立accept连接,因为无需三次握手建立一条固定的通道

2、多个客户端同时连接服务端,可同时收发信息

3、udp可以接受空 (直接回车)???

recv在自己这端的缓冲区为空时,阻塞

recvfrom在自己这端的缓冲区为空时,就收一个空

UDP_socket服务端

import socket

ip_port=('127.0.0.1',9999)

udp_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

udp_sock.bind(ip_port)

while True:

data,addr=udp_sock.recvfrom(1024)

print(data)

udp_sock.sendto(data,addr)

UDP_socket客户端

import socket

ip_port=('127.0.0.1',9999)

udp_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while True:

inp = input('>>: ')

udp_sock.sendto(inp.encode('utf-8'),ip_port)

data,addr=udp_sock.recvfrom(1024)

print(data)

UDP:实现时间功能

# 1、实现时间功能

# 2、data传递进来是二进制的格式,在strftime之前需要先将格式转换回来

# 3、注意格式转换,发送都是encode,接受基本都是recvfrom# udp_socket_server端

import socket

import time

ip_port=('127.0.0.1',9001)

udp_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

udp_sock.bind(ip_port)

while True:

data,addr=udp_sock.recvfrom(1024)

print(data)

if not data:

default_time='%Y-%m-%d %X'

else:

default_time=data.decode('utf-8')

udp_back_time=time.strftime(default_time)

udp_sock.sendto(udp_back_time.encode('utf-8'),addr)

# udp_socket_client端

import socket

udp_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

ip_port=('127.0.0.1',9001) #TypeError: sendto() takes 2 or 3 arguments (1 given) 需要带地址

while True:

inp=input('>>: ')

udp_client.sendto(inp.encode('utf-8'),ip_port)

data,addr=udp_client.recvfrom(1024)

print('现在时间是: %s'%data.decode('utf-8'))# 1、先运行服务端

# 2、再运行客户端打印结果如下:

现在时间是: 18

>>: %Y

现在时间是: 2018

>>:

现在时间是: 2018-01-04 20:49:53

>>:

### 执行命令# socket TCP服务端

import socket

import subprocess

ip_port=('127.0.0.1',9001)

ip_connect=5

buff_size=1024

command=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

command.bind(ip_port)

command.listen(ip_connect)

while True:

# 客户端断开之后保持重连

data,addr=command.accept()

print('客户端连接信息: %s' %data)

while True:

try:

# 接收客户端传递过来的值

cmd_value=data.recv(buff_size)

except Exception:

break

# 执行传递过来的命令,将结果保存到管道对象中赋值给res

res=subprocess.Popen(cmd_value.decode('utf-8'),shell=True,

stdout=subprocess.PIPE,

stdin=subprocess.PIPE,

stderr=subprocess.PIPE)

# 取出stderr的值,如果是空那么执行stdout,不为空说明报错了

err=res.stderr.read()

if err:

cmd_res=err

else:

cmd_res=res.stdout.read()

if not cmd_res: # 判断如果是类似 cd .. 的命令,它到subprocess值为空

cmd_res='命令为空'.encode('gbk')

data.send(cmd_res)

continue

data.close()

command.close()

# socket_客户端

import socket

ip_port=('127.0.0.1',9001)

ip_connect=5

buff_size=1024

command=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

command.connect(ip_port)

while True:

cmd_inp=input('请输入命令: ').strip()

if not cmd_inp: command

if cmd_inp == 'quit': break

command.send(cmd_inp.encode('utf-8'))

data=command.recv(buff_size)

print(data.decode('gbk'))

command.close()

# 最后在客户端这边输入 dir就能看到结果了# 最后在客户端这边输入 dir就能看到结果了

黏包

udp不会黏包,一个recvfrom对应一个sendto,每发送一个包就接收一个包

tcp黏包:优化算法合并了每次send发送的数据, 更优的减少网络负载量, 如果第一次没有收完,那么第二次发送过来的,还是第一次缓存冲区的数据

###### 黏包解决办法

解决粘包的思路:

1、判断数据的长度,

2、封闭消息头

UDP 数据报: TCP数据流的方式 封装了一个消息头(消息来源地址,发送端的IP+端口)

应用程序永远不能操作硬件,能操作硬件的只能是操作系统的内核

用户态内存: socket程序

内核态内存: 内核操作硬件

Forking 进程

threading 线程

多进程比多线程的开销更大### 服务端配置

#!/usr/bin/env python

# -*- coding:utf-8 -*-

from socket import *

import subprocess

import struct

ip_port=('127.0.0.1',9002)

ip_listen=5

buff_size=1024

# TCP 流式协议

tcp_server=socket(AF_INET,SOCK_STREAM)

# 绑定并监听端口

tcp_server.bind(ip_port)

# 允许back_log 允许有多少个队列

tcp_server.listen(ip_listen)

while True:

# 客户端连接信息,当客户端断开之后,服务端应允许其它客户端连接

conn,addr=tcp_server.accept()

print('客户端连接信息: %s' %conn)

while True:

try:

# 如果是空的,或者客户端非法退出,那么就直接退出程序

data=conn.recv(buff_size)

if not data:break

except Exception:

break

# subprocess接收到的命令应该是str格式,客户端发送过来的值是bytes格式,

res=subprocess.Popen(data.decode('utf-8'),shell=True,

stdout=subprocess.PIPE,

stderr=subprocess.PIPE,

stdin=subprocess.PIPE)

err=res.stderr.read()

# 如果这个值里有东西,说明它报错了直接返回它

if err:

cmd_res=err

else:

cmd_res=res.stdout.read()

# 如果为空比如cd .. 那么应该给它返回一个空值

if not cmd_res:

cmd_res='value is null '.encode('utf-8')

# 取出这个值最大的长度,并传递给客户端

length=len(cmd_res)

# --------------- 方法一 ---------------

# # length=str(length).encode('utf-8')

# conn.send(length)

# 将最大值给客户端,返回一个值过来,避免黏包

# retu_data=conn.recv(buff_size)

# if retu_data == b'ok':

# conn.send(cmd_res)

# --------------- 方法二 ---------------

# 黏包发送,第一次发送的值,固定为4字节,客户端接收的时候先接收4字节,再取数据

length_data=struct.pack('i',length)

conn.send(length_data)

conn.send(cmd_res)

conn.close()

tcp_server.close()

### 客户端配置

#!/usr/bin/env python

# -*- coding:utf-8 -*-

from socket import *

import struct

ip_port=('127.0.0.1',9002)

ip_listen=5

buff_size=1024

tcp_client=socket(AF_INET,SOCK_STREAM)

tcp_client.connect(ip_port)

while True:

inp=input('请输入命令: ')

tcp_client.send(inp.encode('utf-8'))

# 如果直接回车,为空,那么应该直接让它退出循环重新再来一次

if not inp:continue

# 如果输入quit,那么直接退出程序

if tcp_client == 'quit': break

# --------------- 方法一 ---------------

# 解决黏包

# length=tcp_client.recv(buff_size)

# tcp_client.send('ok'.encode('utf-8'))

#

# length=int(length.decode('utf-8'))

# --------------- 方法二 ---------------

# 服务端发送的第一个值是4字节大小的,手动调置一下

length=tcp_client.recv(4)

# 取出大小

length=struct.unpack('i',length)[0]

# 设置初始值,用于保存

recv_size=0

# 配置二进制的大小

recv_msg=b''

while recv_size < length:

recv_msg+=tcp_client.recv(buff_size)

recv_size = len(recv_msg)

# data=tcp_client.recv(buff_size)

print(recv_msg.decode('gbk'))

# 结果

请输入命令: cd ..

value is null

请输入命令: ipconfig

Windows IP 配置

以太网适配器 本地连接: xxxxxxxxxxxxxxxxxxxx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值