林海峰博客:https://www.cnblogs.com/linhaifeng/articles/6129246.html

 

第6个视频,什么是socket

网络通信

物理层:

    光缆、双绞线、无线电波

数据链路层:

    ethernet,数据分为报头和数据

网络层:MAC地址用于标识本局域网内的计算机

    IP地址+子网掩码相与可以计算出网关
    如果发送数据中的发送和接收不在一个局域网内,则将数据交给网关,网关进行转发
传输层:
    跑TCP或UDP协议,都是基于端口的,
应用层:
    开启软件,监听一个端口,端口是和网卡上的IP绑定的


Socket就是将各层封装在一起并隐藏,只提供一个接口

Socket套接字起源:伯克利分校研究的多个程序之间的进程之间通信使用
AF_INET address Family Internet

六、套接字工作流程
import socket
s1=socket.socket(socket.AF_INET,socket_STREAM)#socket.AF_INET代表基于网络通信,socket_STREAM代表TCP
s1.bind(('127.0.0.1',8000))#绑定IP和端口,监听本机的网卡
s1.listen(5)#最多5个连接
conn,addr=s1.accept()#等待连接
msg=conn.recv(1024)#接收数据
print("收到客户端信息",msg)
conn.send(msg)
conn,close()
s1.close()

TCP建立连接要3次握手,断开连接要4次挥手
backlog 半连接池
SYN洪水攻击
cmd中netstat可以查看活动的连接

 

七、

1、socket收发信息的原理

内核态:存放操作系统代码的区域

用户态:内存中存放用户软件代码的区域

 

socket工作于用户态内存

网卡硬件工作于内核态

 

TCP socket发送空,可以发送,但是网卡不会将空发出去

内核态中缓存没有数据,程序就卡在recv()处

 

recv(1024)一次读最多1024个字节,如果缓冲区中只有一个字节,则只读一个字节

 

队列:吃了拉

堆栈:吃了吐

 

2、使用异常捕获处理,当客户端断开连接,服务端会一直卡在recv,此时客户端断开,服务端会报错:connectiontResetError[WinError 10054]远程主机强迫关闭了一个现有的连接

 

使用异常捕获

try:

 

except Exception:

     break

 

3.unix 系统下客户端断开连接后服务端不会报错,而是recv接收到的是空数据,则处理方法是添加空判断

if no data:break

 

4、服务端关闭重启可能会遇到的错误:

OSERROR :[Error 48]Address already in use

在cmd中可以查看此端口的状态:netstat -an |grep 8080

查到改端口还处于FIN_WAIT2状态,所以地址不可以用

 

方法1:绑定端口之前,添加一句socket配置,重用IP和端口

phone = socket(AF_INET,sock_STREAM)

phone.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)#phone.setsocket(SOC_SCOKET,SO_REUSEADDR,1)#重新使用该地址

phone.bind('127.0.0.1',8080)

方法2:修改内核中FIN_WAIT2的等待时间

 

八、UDP套接字

UDP不急于链接,直接向某个地址发送,不管是否收到

recvform

sendto

 服务端:

from socket import *

ip_port=('127.0.0.1',8080)

buffer_size=1024

udp_server=socket(AF_INET,sock_GGRAM)#数据报

udp_server.bind(ip_port)

while True:

    data=udp_server.recvfrom(buffer_size)

    print(data)

 客户端:

from socket import *

ip_port=('127.0.0.1',8080)

buffer_size=1024

udp_clinet=socket(AF_INET,sock_GGRAM)

while True:

    msg=input('>>').strip()#移除字符串头尾的空格

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

 

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

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

 

远程执行cmd命令,使用subprocess

使用管道方式实现两个程序之间通信

res=subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE)#将返回数据交给管道

res.stdout.read().decode()#读出的是字节需要解码

 

如果命令不合法则返回的错误信息需要在stderr中读取

res.stderr.read()

 

如果发送cd..

应为cd..没有返回值,此时再读取cmd_res=res.stdout.read()卡死

 

九、粘包

 只有TCP有粘包的现象;UDP没有粘包现象

 

TCP两种粘包:1.多个短包合并;2.一次发送量大,1024读不完

UDP上次没接收完,下次直接丢掉

 

TCP协议是面向流的协议,应用程序不知道一条消息中有多少字节,所以容易产生粘包

TCP发送字节流

UDP发送字节,但UDP会添加消息头

UDP支持一对多模式,所以接收端的socket(套接字缓冲区)采用了链式结构记录每一个到达的UDP包

在每个UDP包中有消息头(消息的来源地址、端口等信息)

对接收端可以根据消息头区分,在下一次IP地址之前的全是一条消息

 

TCP是基于数据流的,收发消息不能为空,这就需要在客户端和服务端都添加空消息处理机制,防止程序卡死
而UDP是基于数据报的,即使你输入的是空,那也不是空消息,UDP协议会帮你添加消息头

TCP是面向连接,面向流,使用了Nagle优化算法,将多次间隔较小且数据量小的数据,合成一个大的数据块,然后进行封包。这样接收端就很难分辨,必须提供科学的拆包机制,即面向流的通信是无消息保护边界的

连续发送了3个短字符串,接收端会只接收到一个合并的字符串,粘包

UDP不会粘包,但会丢数据
TCP会粘包,但不会丢数据

怎样避免粘包

方法1:
需要将需要接收的数据长度发给接收端,
服务端先发送len(cmd_res),等待对方发送ready回信
收到ready再次发送实际的数据
接收数据一侧则使用while循环读,一直读到接收完
方法2:
将字节数直接封包为4byte的字节流,直接发送,而不是像方法1中转为字符串发送后再确认
import struct
struct.pack('i',12)#将12封装成4个字节的字节流,'i'代表int ,4个字节
结果为:b'\x0C\X00\X00\X00
方法3:
iteration

 day31 上节课复习

MTU 1500byte

如果是ftp发送文件

fileinfo={

    'finename':'a.txt',

    'filesize':13333

    }

将字典转换为字节流

import pickle

pickle.dunp(fileinfo)

 

将字节流还原为字典:pickle.loads()

 

发送文件

     1.发送文件报文头长度

     2.发报文头内容

     3.发真实的数据

 

day31

socket_servet并发

import scoketserver

class Myserver(socketserver.BaseRequestHandler):

     def handle(self):#必须重定义一个handle函数

         print('conn is ',self.request)

         print('addr is ',self.client_address)

         while True:

             data=self.request.recv(1024)

             self.request.sendall(data.upper())

if _name_ =='_main_':

    s=socketserver.ThreadingTCPServer(('127.0.0.1',8080),Myserver)

    s.serve_forever()

 

 

scoketserver类中

server类:处理链接

request类:处理通信

ForKingUDPServer进程并发(在Unix下可运行,window下不可用)

ThreadingUDPServer线程并发

进程并发比线程并发系统开销大

只看到第5个