网络编程-socket

2018-09-19  本文已影响0人  Snackk

网络开发的框架

osi七层模型

TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。

UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。

tcp协议的socket进行通信

server端
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字
sk.listen() #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024) #接收客户端信息
print(ret) #打印客户端信息
conn.send(b'hi') #向客户端发送信息
conn.close() #关闭客户端套接字
sk.close() #关闭服务器套接字(可选)

client端
import socket
sk = socket.socket() # 创建客户套接字
sk.connect(('127.0.0.1',8898)) # 尝试连接服务器
sk.send(b'hello!')
ret = sk.recv(1024) # 对话(发送/接收)
print(ret)
sk.close() # 关闭客户套接字

server端                                           client端
import socket                                      import socket
from socket import SOL_SOCKET,SO_REUSEADDR
sk = socket.socket() #实例化一个对象(买手机)
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#在bind前加(复用端口,测试用)
sk.bind(('ip地址',端口号)) #(买卡)
sk.listen(数字(1024)) #(开机)以上为启动一个socket服务

conn,addr = sk.accept()  # 三次握手                 sk.connect((服务端ip,服务端口号))
conn.send(bytes类型的内容) #发送消息                  sk.recv(数字)
msg = conn.recv(数字)    #接受消息                   sk.send(bytes的消息)
...打印 操作
conn.close()     #断开与client端的链接(4次挥手)        sk.close()

sk.close()   #关闭服务器端服务


#  server服务器端

import socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind(('127.0.0.1',10000))
sk.listen()
while True:
    try:
        conn,addr = sk.accept()
        while True:
            msg = input(':')
            conn.send(msg.encode('utf-8'))
            if msg == 'exit':break
            msg = conn.recv(1024)
            if msg == b'exit':break
            print(msg.decode('utf-8'))
        conn.close()
    except UnicodeDecodeError:
        pass
sk.close()

#  client端

import socket
sk = socket.socket()
sk.connect(('127.0.0.1',10000))
while True:
    mag = sk.recv(1024)
    if mag == b'exit':break
    print(mag.decode('utf-8'))
    mag = input(':')
    sk.send(mag.encode('utf-8'))
    if mag == 'exit':break
sk.close()

文件的一次性传送

#client端
import time
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',10000))
sk.send('文件名'.encode('utf-8'))
time.sleep(0.1)
with open('文件绝对路径','rb') as f:
    sk.send(f.read())
sk.close()

#server服务器端
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',9999))
sk.listen()
conn,addr = sk.accept()
filename = conn.recv(1024)
with open(filename,'wb') as f:
    while True:
        try:
            f1 = conn.recv(1024)
            f.write(f1)
        except Exception:
            break
    conn.close()
sk.close()

# 服务器端
import socket
def sky(ip,port):
    i = 0
    count = 1
    sk = socket.socket()
    sk.bind((ip,port))
    sk.listen()
    conn,addr = sk.accept()
    filename = conn.recv(1024)
    butter = int(conn.recv(1024))
    with open(filename,'wb') as f:
        while count:
            try:
                count = conn.recv(butter)
                f.write(count)
                i += 1
                print('\r%s' % i,end = '')
            except Exception:
                break
        conn.close()
    sk.close()

sky('127.0.0.1',9999)

# client 端
import os
import time
import socket
def use(filename,ip,port,butter = 1024):
    sk = socket.socket()
    sk.connect((ip,port))
    sk.send(os.path.basename(filename).encode('utf-8'))
    time.sleep(0.01)
    sk.send(str(butter).encode('utf-8'))
    time.sleep(0.01)
    file_size = os.path.getsize(filename)
    with open(filename,'rb') as f:
        while file_size:
            count = f.read(butter)
            sk.send(count)
            time.sleep(0.0000001)
            file_size -= len(count)
    sk.close()
use(r'D:\BaiduNetdiskDownload\day17\项目架构讲解.mp4','127.0.0.1',9999,102400)
udp协议的socket进行通信

server端
import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字
udp_sk.bind(('127.0.0.1',9000)) #绑定服务器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr) # 对话(接收与发送)
udp_sk.close() # 关闭服务器套接字

client端
import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)

import socket
sk = socket.socket(type = socket.SOCK_DGRAM)
sk.bind(('ip地址',端口号))
对方发给你的消息,对方的地址 = udp_sk.recvfrom(接受的字节数)
sk.sendto(b'要发给对方的消息',对方的地址)
sk.close()
# 服务端
import socket
sk = socket.socket(type = socket.SOCK_DGRAM)
sk.bind(('127.0.0.1',9999))
mag,addr = sk.recvfrom(1024)
print(mag.decode('utf-8'),end = '')
print(addr)
sk.sendto('你好'.encode('utf-8'),addr)
sk.close()

# 客户端
import socket
sk = socket.socket(type = socket.SOCK_DGRAM)
sk.sendto('你好'.encode('utf-8'),('127.0.0.1',9999))
mag,addr = sk.recvfrom(1024)
print(mag.decode('utf-8'),end='')
print(addr)
sk.close()
# 服务端
import socket
sk = socket.socket(type = socket.SOCK_DGRAM)
sk.bind(('127.0.0.1',9999))

while True:
    mag,addr = sk.recvfrom(1024)
    print(mag.decode('utf-8'))
    mag = input('>>:')
    sk.sendto(mag.encode('utf-8'),addr)
sk.close()

# 客户端
import socket
sk = socket.socket(type = socket.SOCK_DGRAM)
while True:
    mag = input('>>:')
    sk.sendto(mag.encode('utf-8'),('127.0.0.1',9999))
    mag,addr = sk.recvfrom(1024)
    print(mag.decode('utf-8'))
sk.close()

由于流式传输的特点 产生了数据连续发送的粘包现象
在一个conn建立起来的连接上传输的多条数据是没有边界的
数据的发送和接收实际上不是在执行send/recv的时候就立刻被发送或者接收
而是需要经过操作系统内核

tcp: 面向流的通信是无消息保护边界的,对于空消息:tcp是基于数据流的,于是收发的消息不能为空

Nagle算法 能够将发送间隔时间得很近的短数据合成一个包发送到接收端

拆包机制 ,当要发送的数据超过了网络上能传输的最大长度,就会被tcp协议强制拆包

udp:是无连接的,面向消息,基于数据报的,即便是你输入的是空内容(直接回车),也可以被发送,udp的recvfrom是阻塞的,一个recvfrom(x)必须对唯一一个sendinto(y)

如果是短数据 :只需要告诉对方边界就可以了
如果是长数据 :不仅要告诉对方边界,还要保证对面完整的接受了

# 服务端
import socket
ip_port=('127.0.0.1',8081)
sk = socket.socket()
sk.bind(ip_port)
sk.listen()
conn,addr = sk.accept()
data1=conn.recv(10)
data2=conn.recv(10)
print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))
conn.close()
# 客户端
import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8081)
sk = socket.socket()
sk.connect(ip_port)
sk.send('hello'.encode('utf-8'))
sk.send('egg'.encode('utf-8'))
# 客户端
import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)
s.send('hello egg'.encode('utf-8'))
# 服务端
from socket import *
ip_port=('127.0.0.1',8080)
tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)  #最多接受5个客户端的链接
conn,addr=tcp_socket_server.accept()
data1=conn.recv(2) #一次没有收完整
data2=conn.recv(10)#下次收的时候,会先取旧的数据,然后取新的
print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))
conn.close()

struct.pack('i',1111111111111)
struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围
i int 型
l long 型
f folat型

import struct
ret = struct.pack('i',1023800976)
print(ret,len(ret))
num = struct.unpack('i',ret)
print(num[0])# num是一个元祖,应该取缔一个元素
# 客户端
import socket
import struct
sk = socket.socket()
sk.connect(('127.0.0.1',9999))
mag = '借助struct模块,我们知道长度数字可以被转换成一个标准大小的4字节数字。'.encode('utf-8')
num = struct.pack('i',len(mag))
sk.send(num)
sk.send(mag)
sk.close()

# 服务端
import socket
import struct
sk = socket.socket()
sk.bind(('127.0.0.1',9999))
sk.listen(3)
conn,addr = sk.accept()
num = conn.recv(4)
num = struct.unpack('i',num)[0]
mag = conn.recv(num).decode('utf-8')
print(mag)
conn.close()
sk.close()

服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来

客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数
s.recv() 接收TCP数据
s.send() 发送TCP数据
s.sendall() 发送TCP数据
s.recvfrom() 接收UDP数据
s.sendto() 发送UDP数据
s.getpeername() 连接到当前套接字的远端的地址
s.getsockname() 当前套接字的地址
s.getsockopt() 返回指定套接字的参数
s.setsockopt() 设置指定套接字的参数
s.close() 关闭套接字

面向锁的套接字方法
s.setblocking() 设置套接字的阻塞与非阻塞模式 ,默认True(阻塞) 设置成False就不阻塞了,没人连就会报错(用try-excpet解决)
s.settimeout() 设置阻塞套接字操作的超时时间
s.gettimeout() 得到阻塞套接字操作的超时时间

面向文件的套接字的函数
s.fileno() 套接字的文件描述符
s.makefile() 创建一个与该套接字相关的文件

验证客户端链接的合法性
# 服务端
import os
import hmac
import socket
def auth(conn):
    msg = os.urandom(32)  # 生成一个随机的32位字节字符串
    conn.send(msg)  # # 发送到client端
    result = hmac.new(secret_key, msg)  # 处理这个随机字符串,得到一个结果
    client_digest = conn.recv(1024)  # 接收client端处理的结果
    if result.hexdigest() == client_digest.decode('utf-8'):
        print('是合法的连接')  # 对比成功可以继续通信
        return True
    else:
        print('不合法的连接')  # 不成功 close
        return False

secret_key = b'alex_sb'
sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.listen()
conn,addr = sk.accept()
if auth(conn):
    print(conn.recv(1024))
    # 正常的和client端进行沟通了
    conn.close()
else:
    conn.close()
sk.close()

# 客户端
import hmac
import socket
def auth(sk):
    msg = sk.recv(32)
    result = hmac.new(key, msg)
    res = result.hexdigest()
    sk.send(res.encode('utf-8'))

key = b'alex_s'
sk = socket.socket()
sk.connect(('127.0.0.1',9000))
auth(sk)
sk.send(b'upload')
# 进行其他正常的和server端的沟通
sk.close()

import socketserver
# tcp协议的server端就不需要导入socket,socketserver中导入了socket模块
class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        conn = self.request   #self.data = self.request.recv(1024).strip()
        while True:
            conn.send(b'hello')
            print(conn.recv(1024))

server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),Myserver)
server.serve_forever()

# 这个类不用实例化也不用调用类中的handle方法
上一篇下一篇

猜你喜欢

热点阅读