网络编程

python基础之socket编程

阅读目录

 ==============================

一.C/S、B/S架构client<------>server  browser<------>server

 

学习socket编程就是要编写一个客户端软件和服务端软件
然后实现服务端与客户端基于网络通信

 

二.osi七层

 

目前我们掌握5层就够了

引子:

须知一个完整的计算机系统是由硬件、操作系统、应用软件三者组成,具备了这三个条件,一台计算机系统就可以自己跟自己玩了(打个单机游戏,玩个扫雷啥的)

如果你要跟别人一起玩,那你就需要上网了,什么是互联网?

互联网的核心就是由一堆协议组成,协议就是标准,比如全世界人通信的标准是英语

网络通信原理:详点此处

三.socket层

四.socket是什么

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,

对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只要遵循socket的规定去编程,写出的程序自然就是遵循 tcp/udp标准的。

也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序

而程序的pid是同一台机器上不同进程或者线程的标识
扫盲篇

五.tcp与udp协议(传输层)

tcp三次握手和四次挥手
p.s四次挥手客户端和服务端位置对调

listen(监听)和rcvd(收消息)和established(建立连接)三者相互转化,
受到syn洪水攻击的时候,服务器上会有大量的syn 状态,服务器三种状态会进行切换
fin_wait_1表示主动断开连接的一方进入的状态(通常是服务端来发)
高并发的情况下服务端会进入time_wait状态

六.TCP协议

6.1基于TCP协议的套接字通信

import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 买电话 AF_INET基于网络通信的套接字
# SOCK_STREAM流氏协议(tcp) SOCK_DGRAM报氏协议(udp)

phone.bind(('127.0.0.1',8081)) #插手机卡,补充:0-65535 0-1024给系统用的
phone.listen(5) # 开机  这个5是控制最大的请求数,不是链接数,同一时间能来的请求数只有5

print('start...')
conn,client_addr=phone.accept() # 等电话连接
print('连接来了:',conn,client_addr)#conn是一个套接字对象,定制了一个打印格式

# 收发消息
msg=conn.recv(1024) #收消息,1024是一个最大的限制 1024个字节
print('客户端的消息: ',msg)
conn.send(msg+b'SB')

# 挂电话
conn.close()
# 关机
phone.close()
服务端相关代码
import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 买电话
phone.connect(('127.0.0.1',8081)) # 拨电话,地址为服务端的ip和端口

phone.send('你好'.encode('utf-8')) # 发消息b'hello'
data=phone.recv(1024) #收消息

print(data.decode('utf-8'))

phone.close()
客户端相关代码

6.2通信循环

import  socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8081))
phone.listen(5)

print('start')
conn,client_addr=phone.accept() #conn是管道,对应的是双向链接
print('客户端',client_addr)

while True:#通信循环
    try:
        msg=conn.recv(1024)
        print('客户端的消息:',msg)
        conn.send(msg+b'SB')
    except Exception:
        break

conn.close() #操作系统把无用的链接回收掉
phone.close()
服务端相关代码
import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 买电话
phone.connect(('127.0.0.1',8081)) # 拨电话,地址为服务端的ip和端口
while True:
    msg=input('>>:').strip()
    phone.send(msg.encode('utf-8'))#发消息b'hello
    data=phone.recv(1024)#收消息

    print(data.decode('utf-8'))

phone.close()
客户端相关代码

6.3通信循环和连接循环

import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8081))
phone.listen(5)

print('start...')
while True: # 连接循环
    conn,client_addr=phone.accept()
    print('客户端 ',client_addr)

    while True: # 通信循环
        try:
            msg=conn.recv(1024)
            print('客户端的消息: ',msg)
            conn.send(msg+b'SB')
        except ConnectionResetError:
            break
    conn.close()
phone.close()
服务端相关代码
import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 买电话
phone.connect(('127.0.0.1',8081)) # 拨电话,地址为服务端的ip和端口

while True:
    msg=input('>>>: ').strip()
    phone.send(msg.encode('utf-8')) # 发消息b'hello'
    data=phone.recv(1024) #收消息

    print(data.decode('utf-8'))

phone.close()
客户端相关代码
import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 买电话
phone.connect(('127.0.0.1',8081)) # 拨电话,地址为服务端的ip和端口

while True:
    msg=input('>>>: ').strip()
    phone.send(msg.encode('utf-8')) # 发消息b'hello'
    data=phone.recv(1024) #收消息

    print(data.decode('utf-8'))

phone.close()
客户端1相关代码
import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 买电话
phone.connect(('127.0.0.1',8081)) # 拨电话,地址为服务端的ip和端口

while True:
    msg=input('>>>: ').strip()
    phone.send(msg.encode('utf-8')) # 发消息b'hello'
    data=phone.recv(1024) #收消息

    print(data.decode('utf-8'))

phone.close()
客户端2相关代码
import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 买电话
phone.connect(('127.0.0.1',8081)) # 拨电话,地址为服务端的ip和端口

while True:
    msg=input('>>>: ').strip()
    phone.send(msg.encode('utf-8')) # 发消息b'hello'
    data=phone.recv(1024) #收消息

    print(data.decode('utf-8'))

phone.close()
客户端3相关代码
import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 买电话
phone.connect(('127.0.0.1',8081)) # 拨电话,地址为服务端的ip和端口

while True:
    msg=input('>>>: ').strip()
    phone.send(msg.encode('utf-8')) # 发消息b'hello'
    data=phone.recv(1024) #收消息

    print(data.decode('utf-8'))

phone.close()
客户端4相关代码
import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 买电话
phone.connect(('127.0.0.1',8081)) # 拨电话,地址为服务端的ip和端口

while True:
    msg=input('>>>: ').strip()
    phone.send(msg.encode('utf-8')) # 发消息b'hello'
    data=phone.recv(1024) #收消息

    print(data.decode('utf-8'))

phone.close()
客户端5相关代码

只能连接1个客户端,但是可以建立6个请求

6.4远程执行命令的程序

from socket import *
import subprocess

server=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)

while True:
    conn,client_addr=server.accept() #(连接对象,客户端的ip和端口)
    print(client_addr)
    while True:
        try:
            cmd=conn.recv(1024)
            obj=subprocess.Popen(cmd.decode('utf-8'),
                                 shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE
                                 )
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()

            # 先发送数据的长度
            total_size=len(stdout) + len(stderr)
            conn.send(total_size)

            # 发送真实的数据
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:
            break

    conn.close()
server.close()
服务端相关代码
from socket import *

client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
# print(client)

while True:
    cmd=input('>>>: ').strip()
    if not cmd:continue
    client.send(cmd.encode('utf-8'))
    # print('has send')
    res=client.recv(14744)
    # print('has recv')
    print(len(res))
    print(res.decode('gbk'))

client.close()
客户端相关代码

七.UDP协议

详情见这里

八.粘包现象

8.1什么是粘包?

pass

 十.粘包问题的解决

10.1补充struct模块

pass

10.2粘包问题解决初级版

pass

10.3粘包问题解决终极版

流程是这样
服务端先做一个报头,把报头信息都丢到一个报头字典里面,字典序列化得到json串,json串encode转成bytes,得到bytes就可以发报头了
但不能立马发,先把报头长度发过去,报头长度被struct打包成了4个bytes,然后接收端先收4个bytes出来,然后可以解包出来报头的长度
发送端接下来发报头,我接收端直接可以根据报头的长度把报头收到,发送端再发真实的数据,我接收端根据报头里面提供的信息,你发多少我都能收完整.
posted @ 2018-12-28 23:19  王苗鲁  阅读(101)  评论(0编辑  收藏  举报