socket模块实现socket协议

python内置模块socket模块可解决基于tcp和ucp协议的网络传输

一:基于udp协议进行网络传输

例1 :

server端:

import socket
sk = socket.socket(type=socket.SOCK_DGRAM)  # 声明udp协议
sk.bind(('127.0.0.1', 9090))   # udp和tcp都是绑定自己的ip地址和端口号。udp没有listen和接收对方的地址。谁都可以给我发,只要在线就接。

while True:
    msg, addr = sk.recvfrom(1024)  # 收信息,最多1024个字节   # 服务端一定要先收到客户端的地址,因为他如果不知道地址也不知道发给谁
    print(addr)  # ('127.0.0.1', 63045)  发给我信息的ip地址和对方的端口
    print(msg.decode('utf-8'))   # 从物理层发过来的都是byte类型
    msg_send = input('>>>')
    sk.sendto(msg_send.encode('utf-8'), addr)  # 发信息

sk.close()

client端

import socket
sk = socket.socket(type = socket.SOCK_DGRAM)
while True:
    msg_send = input('>>>')
    sk.sendto(msg_send.encode('utf-8'),('127.0.0.1',9090))
    msg,addr = sk.recvfrom(1024)
    print(msg.decode('utf-8'))
    print('server addr :',addr)  # server addr : ('127.0.0.1', 9090) 服务端的地址

sk.close()

运行结果

>>>吃了吗
吃了
>>>天气怎么样
挺好的
>>>听说明天有台风,做好防范的准备哦
好的
>>>

socket模块实现socket协议_第1张图片

例2:多人同时向服务器发消息:

服务器端:

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(('127.0.0.1', 9847))  # 一旦绑定一个地址就是服务器

friends = {'wendy':'\033[1;32;40mwendy : %s\033[0m', 'laura':'\033[1;33;44mlaura : %s\033[0m'}  # 设置颜色等
while True:
    msg, addr = sk.recvfrom(1024)  # 从addr收的信息msg
    user, msg = msg.decode('utf-8').split(':')
    if msg.strip() == 'q':
        continue
    else:
        print(friends[user]%msg)
    send_msg = input('s>>>')
    sk.sendto(send_msg.encode('utf-8'), (addr))

sk.close()

客户端1

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)
addr = ('127.0.0.1', 9847)
user = 'laura'
while True:
    send_msg = input('l>>>')
    sk.sendto((('%s:%s' % (user, send_msg)).encode('utf-8')), addr)
    if send_msg == 'q':
        break
    msg, _ = sk.recvfrom(1024)  # 这里的变量用下划线接收代表此变量是没有用的
    print(msg.decode('utf-8'))

sk.close()

客户端2

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)
addr = ('127.0.0.1', 9847)
user = 'wendy'
while True:
    send_msg = input('w>>>')
    sk.sendto((('%s:%s' % (user, send_msg)).encode('utf-8')), addr)
    if send_msg == 'q':
        break
    msg, _ = sk.recvfrom(1024)
    print(msg.decode('utf-8'))
sk.close

例3 向服务器访问时间

服务器端

import time
import socket

sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(('127.0.0.1', 8848))

while True:  # 服务器24小时服务
    msg, addr = sk.recvfrom(1024)
    fmt_time = time.strftime(msg.decode('utf-8'))
    sk.sendto(fmt_time.encode('utf-8'), addr)

sk.close()

客户端

import  socket
sk = socket.socket(type=socket.SOCK_DGRAM)

sk.sendto('%Y-%m-%d %H:%M:%S'.encode('utf-8'), ('127.0.0.1', 8848))
msg, _ = sk.recvfrom(1024)
print(msg.decode('utf-8'))  # 运行结果:2018-09-14 21:28:09

sk.close()

二 基于tcp协议进行网络传输

例1 一台终端两个程序通信(基于tcp)普通会话

步骤

服务器端:(先启动)

  • 1 在服务器端创建一个对象。相当于总服务台,总机。可以接收很多客户端的请求,sk = socket.socket()

  • 2 绑定本地回环地址,端口,以便别人找到我,因为很多端口已占用。给新手机装卡。sk.bind(‘127.0.0.1’, 9000) # 借用了操作系统的一个端口

  • 3 开机 sk.listen() # 执行完这句后顺利地开启了一个服务端,以ip和端口为标识的服务端,别人连接我的时候触发的是我的accept方法,

  • 4 等电话,如果有人来电话,会显示conn,(给我打电话接收的链接)addr(对方的地址)。sk.accept() # 这个方法会
    阻塞,和input方法一样会一直等到别人来连我时,
    否则没人连我时不会往下走。拿到conn的链接和addr的地址。而大部分时间都是用conn,于是所有的通话都基于conn来说了,
    如conn.send(bytes类型),给对面发信息 conn.recv(数字)收信息,最大能接受1024个字节的数据。如果对面超过1024个字节我只收1024个字节
    msg= conn.recv(数字)然后打印等等操作。conn.close()表示和之前的客户端连接已经断开了,sk.close()表示服务已经关掉了。正常情况下一个server端
    是不应该轻易关掉的,

  • 5 拿到了conn就可以说话了,拿着链接send消息,conn.send(b’hello’) # 在网络上只能传字节,不能传字符串。

  • 5 开机后用完后要关机。close时返回给系统了。

客户端:

  • 1 要想给别人打电话,要买个手机 sk = socket.socket()

  • 2 要想给别人打电话自己不需要有卡,输入别人的链接然后连他。sk.connect((‘127.0.0.1’, 9000)) # 服务端的ip地址,服务端的端口

  • 3 用sk接收消息,打印该消息。msg = sk.recv(1024)

socket模块实现socket协议_第2张图片
tcp协议
socket模块实现socket协议_第3张图片

例2 基于Tcp协议传文件

socket模块实现socket协议_第4张图片
tcp 传文件
socket模块实现socket协议_第5张图片
示例代码
server端

import os
import socket

sk = socket.socket()
sk.bind(('127.0.0.1', 8461))
sk.listen()

conn, addr = sk.accept()  # 前面套路都是一样,建服务器,等待有人连我

file_path = r'C:\Users\TR\Desktop\python.mp4'  # 要传的文件路径
filename = os.path.basename(file_path)  # 获取路径的最后名字,以便对方好存储
conn.send(filename.encode('utf-*'))  # 传文件名

file_size = os.path.getsize(file_path)  # 获取文件大小
with open(file_path, 'rb') as f: # 视频文件用bytes模式打开
    while file_size:   # 定义自变量file_size
        content = f.read(1024)  # 一次读1024个字节,下一次循环时光标自动跳到上次已经读完的末尾
        file_size -= len(content)  # 每次循环自变量减读取的大小
        conn.send(content)  # 每次读多少发送多少,知道file_size为0
conn.close()  # 循环结束关闭与这个ip地址的链接
sk.close()

client端


import os
import socket

sk = socket.socket()
sk.connect(('127.0.0.1', 8461))  # 固定格式,连接服务器

filename = sk.recv(1024)  #  接收发过来的文件名
filename = filename.decode('utf-8')  # 解码文件名
with open(filename, 'wb') as f:  # 写模式打开会新建一个文件或覆盖已有同名文件
    while True:
        content = sk.recv(1024)  # 不断接收服务器发过来的信息
        if content:  # 如果接收的字节不为空
            f.write(content)  # 就写入文件
        else:
            break
sk.close()

三 tcp协议中的粘包问题

粘包现象

合包现象
数据很短
时间间隔短

拆包现象
大数据会发生拆分
不会一次性全部发生到对方
对方在接受的时候很可能没有办法一次性接收到所有信息
那么没有接受完的信息很可能和后面的信息粘在一起

粘包现象只发生在tcp协议
tcp协议的传输是流式传输
每一条信息与信息之间是没有边界的

udp协议中是不会发生粘包现象的
适合短数据的发送
不建议发送过长的数据
会增大你数据丢失的几率(因为他抛出去没有回应,也不管有没有那个地址)

在程序中会出现粘包:
收发数据的边界不清晰(分清界限)
接收数据这一段不知道要接收数据的长度到底是多少(告诉接收方这段内容长度是多少)

import socket
sk = socket.socket()
sk.bind(('127.0.0.1', 9090))
sk.listen()

conn, addr = sk.accept()

conn.send(b'hello')
conn.send(b'world')
conn.close()

sk.close

import socket

sk = socket.socket()
sk.connect(('127.0.0.1', 9090))

ret = sk.recv(1024)
print(len(ret), ret)  # 2 b'he'
# ret1 = sk.recv(5)
# print(ret1)  # b'llowo'  tcp属于流传输,没有分隔的界限
sk.close()

解决粘包现象

socket模块实现socket协议_第6张图片
server端

import struct
import socket
sk = socket.socket()
sk.bind(('127.0.0.1', 9090))
sk.listen()

conn, addr = sk.accept()

while True:
    s = input('>>>').encode('utf-8')  # 参数中默认编码方式为utf-8,返回一个byte格式的obj,encode和decode就是bytes和unicode的相互转化,python中字符串是unicode,转化为utf-8格式的bytes类型
    pack_num = struct.pack('i', len(s))  # 之所以用struct,就是为了数字保证转化后只占4个字节,然后发送方发送这个bytes数据,接收方接收的前4个字节就是要传输的文件大小,
    conn.send(pack_num)
    conn.send(s)

conn.close()
sk.close()

client 端

import socket
import struct
sk = socket.socket()
sk.connect(('127.0.0.1', 9090))

while True:
    pack_num = sk.recv(4)
    num = struct.unpack('i', pack_num)[0]
    ret = sk.recv(num)
    print(ret.decode('utf-8'))
sk.close()

你可能感兴趣的:(python)