python基础9.1.socket网络编程 --> 实现代码与示例

 

Socket编程2.0

 


      Socket通信流程图示例

 

描述

# 流程描述:
# 
# 1 服务器根据地址类型(ipv4,ipv6)、socket类型、协议创建socket
# 
# 2 服务器为socket绑定ip地址和端口号
# 
# 3 服务器socket监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket并没有被打开
# 
# 4 客户端创建socket
# 
# 5 客户端打开socket,根据服务器ip地址和端口号试图连接服务器socket
# 
# 6 服务器socket接收到客户端socket请求,被动打开,开始接收客户端请求,直到客户端返回连接信息。这时候socket进入阻塞状态,所谓阻塞即accept()方法一直等到客户端返回连接信息后才返回,开始接收下一个客户端连接请求
# 
# 7 客户端连接成功,向服务器发送连接状态信息
# 
# 8 服务器accept方法返回,连接成功
# 
# 9 客户端向socket写入信息(或服务端向socket写入信息)
# 
# 10 服务器读取信息(客户端读取信息)
# 
# 11 客户端关闭
# 
# 12 服务器端关闭

 

参数

SOCK_STREAM : TCP
SOCK_DGARM : UDP

family = AF_INET : 服务器之间通信
family = AF_INET6 : 服务器之间通信
family = AF_UNIX : unix不同进程之间通信

server下的方法:
bind()
listen()
accept()

recv()
send()
sendall()

close()

client下的方法:
cinnect()

recv()
send()
sendall()

close()

<socket.socket fd=640, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8000), raddr=('127.0.0.1', 52981)>
------------
('127.0.0.1', 52981)

(<socket.socket fd=684, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8000), raddr=('127.0.0.1', 53398)>, ('127.0.0.1', 53398))

部分方法及参数

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()
  #套接字的文件描述符

TCP协议简单操作


# 粘包现象
# 只出现在tcp协议中,因为tcp协议 多条消息之间没有边界,并且还有一大堆优化算法
# 发送端 : 两条消息都很短,发送的间隔时间也非常短
# 接收端 : 多条消息由于没有及时接收,而在接收方的缓存短堆在一起导致的粘包

# 解决粘包问题的本质 :设置边界 一接一发

基本连接方式:

import  socket

# 创建socket
sk = socket.socket()

# 为socket绑定ip和端口号
address = ('127.0.0.1',8000)
sk.bind(address)

# 监听设置端口等待客户端的请求
sk.listen(3)

# accept阻塞,直到有客户端连接进来
conn,addr = sk.accept()

# 接收并显示接受到的内容
date = conn.recv(1024)
print(str(date,'utf8'))

# 关闭该用户的连接
conn.close()
Sever端
import socket

# 创建socket
sk = socket.socket()

# 连接到指定计算机的端口
ip_port = ('127.0.0.1',8000)
sk.connect(ip_port)

# 以bytes类型发送数据
inp = input('>>>')
sk.send(bytes(inp, 'utf8'))

# 关闭连接
sk.close()
client端

 进阶版本:

import socket

sk = socket.socket()

ip_port = ('127.0.0.1',8000)
sk.bind(ip_port)

sk.listen(3)

while True:
    conn, addr = sk.accept()
    print(f'用户{addr[0]}接入,占用端口{addr[1]}')

    while True:
        date = input('>>>')
        conn.send(bytes(date,'utf8'))

        msg = conn.recv(1024)
        print(f'ID{addr[0]}: ',str(msg,'utf8'))
    conn.close()
sever
import socket

sk = socket.socket()

ip_port = ('127.0.0.1',8000)
sk.connect(ip_port)
while True:
    msg = sk.recv(1024)
    print(str(msg,'utf8'))

    date = input('>>>')
    sk.send(bytes(date,'utf8'))
client

实现远程指令版本:

import socket
import subprocess
import time

sk = socket.socket()

ip_pots = ('127.0.0.1',8001)
sk.bind(ip_pots)

sk.listen(3)

while True:

    conn,addr = sk.accept()
    print('用户已经连接,IP:%s -- 端口:%s'%(addr[0],addr[1]))

    while True:
        try:
            date = conn.recv(1024)
        except Exception:
            break

        if not conn:break

        obj = subprocess.Popen(str(date,'utf8'),shell=True,stdout=subprocess.PIPE)
        cmd_result = obj.stdout.read()

        result_len = bytes(str(len(cmd_result)),'utf8')

        print('发送字节流>>>:',result_len)
        conn.sendall(result_len)    #粘包现象

        # time.sleep(0.2)           #lowb版解决办法 不建议 影响效率
        dapa = conn.recv(1024)      #建议使用方法
        print(str(dapa, 'gbk'),'\nprocessing......\nsend up!')

        conn.sendall(cmd_result)
server端
import socket

sk = socket.socket()

ip_post = ('127.0.0.1',8001)
sk.connect(ip_post)

print('Sever连接成功,请发送指令')

while True:

    inp = input('>>>')
    if inp == 'exit':
        print('已经断开连接')
        break
    sk.send(bytes(inp, 'utf8'))

    result_len = int(str(sk.recv(1024),'utf8'))
    print('收到字节流>>>:',result_len)

    sk.sendall(bytes('accept up!','utf8'))

    date = bytes()

    while len(date) != result_len:

        dat = sk.recv(1024)
        date += dat

    print(str(date, 'gbk'))
client端

文件传输版本

import socket

sk = socket.socket()

ip_port = ('127.0.0.1',8012)
sk.bind(ip_port)

sk.listen(3)

conn,addr = sk.accept()

date = conn.recv(1024).decode('utf8')
filename,filesize = date.strip().split('|')

file = open(filename,'wb')
sumber = 0

while sumber != int(filesize):
    size = conn.recv(1024)
    file.write(size)
    sumber += len(size)
    print(filesize, '-->', sumber)

file.close()
conn.close()
sk.close()
Sever端
import socket
import os
FILEPATH = r'C:\Users\Admin-yue\Documents\1.mp4'

sk = socket.socket()
#
ip_port = ('127.0.0.1',8012)
sk.connect(ip_port)

f1 = open(FILEPATH,'rb')

filename = os.path.basename(FILEPATH)
filesize = os.stat(FILEPATH).st_size

file = filename+'|'+str(filesize)
sk.send(bytes(file,'utf8'))
sumber = 0

while sumber != filesize:
    size = f1.read(1024)
    sk.send(size)
    sumber += len(size)
f1.close()
sk.close()
Client端

TCP粘包处理方案

import json
import struct
import socket
# 接收
sk = socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()

conn,_ =sk.accept()

msg_len = conn.recv(4)  # 只接受四个长度

dic_len = struct.unpack('i',msg_len)[0] # 抓换成int型

msg = conn.recv(dic_len).decode('utf-8')
msg = json.loads(msg)

with open(msg['filename'],'wb') as f:
    while msg['filesize'] > 0:
        content = conn.recv(1024)
        msg['filesize'] -= len(content)
        f.write(content)
conn.close()
sk.close()
Sever
import os
import json
import struct
import socket
# 发送
sk = socket.socket()
# sk.connect(('192.168.14.109',9012))
sk.connect(('127.0.0.1',9001))

# 文件名\文件大小
abs_path = r'D:\python22期\day28 课上视频\3.网络基础概念.mp4'
filename = os.path.basename(abs_path)
filesize = os.path.getsize(abs_path)

# 数据在网络中传输使用json
dic = {'filename':filename,'filesize':filesize}
str_dic = json.dumps(dic)
b_dic = str_dic.encode('utf-8')

mlen = struct.pack('i',len(b_dic))

sk.send(mlen)   # 4个字节 表示字典转成字节之后的长度
sk.send(b_dic)  # 具体的字典数据

with open(abs_path,mode = 'rb') as f:
    while filesize>0:
        content = f.read(1024)
        filesize -= len(content)
        sk.send(content)
sk.close()
Client

 SocketServer实现并发效果

import time
import socketserver

class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        conn = self.request
        while True:
            try:
                content = conn.recv(1024).decode('utf-8')
                conn.send(content.upper().encode('utf-8'))
                time.sleep(0.5)
            except ConnectionResetError:
                break
server = socketserver.ThreadingTCPServer(('127.0.0.1',9001),Myserver)
server.serve_forever()
SocketServer
import socket

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

while True:
    sk.send(b'hello')
    content = sk.recv(1024).decode('utf-8')
    print(content)
client

Struct模块

import struct
sumber = 'asd'
a= struct.pack('i',len(sumber.encode()))
print(a,type(a))
# b'\x03\x00\x00\x00' <class 'bytes'>
b = struct.unpack('i',a)[0]
print(b,type(b))
# 3 <class 'int'>

UDP协议的简单操作

基础操作

import socket

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

ip_port = ('127.0.0.1',8000)
sk.bind(ip_port)

msg,addr = sk.recvfrom(1024)
print(msg)
sk.sendto(b'reas',addr)
Sever端
import socket

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

ip_port = ('127.0.0.1',8000)
sk.sendto(b'asd',ip_port)
msg = sk.recv(1024)
print(msg)
Client端

 进阶版本

import socket
import struct

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

ip_port = ('192.168.14.183',9001)
sk.bind(ip_port)
myname = input('为自己起个名字:')
while True:
    name,ioi= sk.recvfrom(1024)
    conn,addr = sk.recvfrom(1024)
    date = str(name,'utf8')+':'+str(conn,'utf8')
    print(date)
    set = input('>>>')
    date1 = myname+':'+set
    sk.sendto(bytes(date1,'utf8'),addr)
Sever端
import socket

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

ip_port = ('192.111.23.1',8101)
username = input('为自己创建一个昵称:')
while True:

    date = input('>>>')
    sk.sendto(bytes(username, 'utf8'), ip_port)
    sk.sendto(bytes(date,'utf8'),ip_port)

    date = sk.recv(1024)
    date = str(date,'utf8')
    print(date)
Client端

 

posted @ 2019-03-11 20:20  CatdeXin  阅读(308)  评论(0)    收藏  举报