网络编程【二】socket(套接字)初识

socket(套接字)

 

基于tcp协议的socket

tcp是基于连接的,必须先启动服务端,而后再启动客户端去连接服务端html

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()            # 关闭客户套接字
复制代码

问题:有时重启服务端可能会遇到

解决办法:git

复制代码
#加入一条socket配置,重用ip和端口
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
sk = socket.socket()
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
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()        #关闭服务器套接字(可选)
复制代码

完成一个socket实现的小程序:json

  1. 可以实现和同桌之间的通讯
  2. 可以实现本身向发送的任意内容
  3. 可以和你的同桌聊任意多句话,并设置退出

server端小程序

复制代码
import socket
sk = socket.socket()   # 建立一个对象
sk.bind(('127.0.0.1',9001))  # 绑定一个服务器的地址  192.168.16.46
sk.listen()  # 开始接受客户端给我链接
conn,addr = sk.accept()  # 阻塞 直到有人连我
while True:
    conn.send(b'hello')  # 发送内容
    msg = conn.recv(1024) # 收信
    if msg.decode('utf-8') == 'tuichu':
        conn.send("tuichu".encode("utf-8"))
        break
        conn.close()  # 关闭链接
        sk.close()  # 关闭服务器
    print(msg.decode("utf-8"))
复制代码

client端服务器

复制代码
import socket
sk = socket.socket() # 建立socket对象
sk.connect(('127.0.0.1',9001)) # 绑定链接server端的地址
while True:
    msg = sk.recv(1024)  # 接收服务器发来的信息
    print(msg.decode('utf-8'))  # 解码并打印消息内容
    if msg.decode('utf-8') == 'tuichu':
        break
        sk.close()  # 关机
    choice = input('请输入您要发送的内容>>>:')
    sk.send(choice.encode('utf-8'))
复制代码

 基于udp协议的socket

udp是无连接的,启动服务以后能够直接接受消息不须要提早创建连接socket

server端tcp

复制代码
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端ide

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)

 socket更多方法介绍

复制代码
服务端套接字函数
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()     设置套接字的阻塞与非阻塞模式
s.settimeout()      设置阻塞套接字操做的超时时间
s.gettimeout()      获得阻塞套接字操做的超时时间

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

 tcp验证登陆

import socket
import hashlib
import json
def md5_func(user,pwd):
    md5 = hashlib.md5(user.encode('utf-8'))
    md5.update(pwd.encode('utf-8'))
    return md5.hexdigest()

username = input('用户名')
password = input('密 码')
sk = socket.socket()
sk.connect(('127.0.0.1',9000))
res = {'opt':'login','username':username,'password':md5_func(username,password)}
ret = json.dumps(res)
sk.send(ret.encode('utf-8'))
msg = sk.recv(1024).decode('utf-8')  # {"opt": "login", "result": false}
a = json.loads(msg)
if a['result']:
    print('登录成功')
else:
    print('登录失败')
sk.close()

server
server
# -*- coding: utf-8 -*-
# @Time    : 2019/4/12 15:33
import os
import json
import hmac
import socket
import struct
import hashlib
sk = socket.socket()

# 登陆密码加密
def get_md5(user,pwd):
    md5 = hashlib.md5(user.encode('utf-8'))
    md5.update(pwd.encode('utf-8'))
    return md5.hexdigest()

# 认证加密
def get_hmac(secret_key,rand):
    hmac_t = hmac.new(secret_key, rand)
    res = hmac_t.digest()
    return res

# 发送数据
def pro_send(sk,dic,pro = True):
    bytes_dic = json.dumps(dic).encode('utf-8')
    if pro:
        len_bytes = struct.pack('i',len(bytes_dic))
        sk.send(len_bytes)
    sk.send(bytes_dic)

# 接收数据
def pro_recv(sk,pro=True,num=1024):
    if pro:
        num = sk.recv(4)
        num = struct.unpack('i', num)[0]
    str_dic = sk.recv(num).decode('utf-8')
    dic = json.loads(str_dic)
    return dic

# 客户端认证
def auth():
    sk.connect(('127.0.0.1', 9000))
    secret_key = '宋治学'.encode('utf-8')
    rand = sk.recv(1024)
    res = get_hmac(secret_key,rand)
    sk.send(res)
    return sk   # 将对象return出来方便全局使用

# 下载文件
def write_file(filename):
    md5 = hashlib.md5()
    filesize = json.loads(sk.recv(1024).decode('utf-8'))
    with open(filename, 'wb') as f:
        print('正在传输')
        while filesize['file_size'] > 0:
            content = sk.recv(4096)
            md5.update(content)
            f.write(content)
            filesize['file_size'] -= len(content)
        print('传输完成')
    return md5.hexdigest()
# D:\PycharmProjects\s20\爬虫学习\server.py
def download(sk):
    dic = {'operate': 'download'}
    pro_send(sk, dic)
    lis = pro_recv(sk, pro=False, num=1024)
    for index,i in enumerate(lis,1):
        print(index,i)
    choice = input('请选择要下载的文件').strip()
    if lis[int(choice)-1]:
        sk.send(lis[int(choice)-1].encode('utf-8')) # 发送用户选择的文件名
        md5_file = write_file(lis[int(choice) - 1]) # 下载文件后获得的md5值
        ret_md5 = sk.recv(1024).decode('utf-8')   # 服务器传过来的md5值
        if md5_file == ret_md5:
            print('文件校验成功')
        else:
            print('文件校验失败')
    else:
        print('输入有误请从新输入')

# 上传文件
def upload(sk):
    file_path = input('请输入文件路径').strip()
    file_name = os.path.basename(file_path)
    file_size = os.path.getsize(file_path)
    dic = {'filename': file_name, 'filesize': file_size, 'operate': 'upload'}
    pro_send(sk, dic)
    with open(file_path,'rb') as f:
        while file_size > 4096:
            content = f.read(4096)
            sk.send(content)
            file_size -= len(content)
        else:
            content = f.read()
            sk.send(content)
# 退出
def exsit(sk=None):
    exit()

# 登陆
def login(user,pwd):
    sk = auth()
    dic = {'user': user, 'passwd': get_md5(user,pwd), 'operate': 'login'}
    pro_send(sk,dic)
    ret = pro_recv(sk)
    return sk,ret

# 用户登陆成功选择操做
def choice(sk):
    while True:
        operate = [('上传', upload), ('下载', download),('退出',exsit)]
        for index,opt in enumerate(operate,1):
            print(index,opt[0])
        num = input('请选择操做:>>>').strip()
        if num.isdigit() and int(num) in range(1,len(operate)+1):
            operate[int(num)-1][1](sk)
        else:print('输入有误请从新输入')

while True:
    username = input('username:').strip()
    password = input('password:').strip()
    sk,ret = login(username,password)
    if ret['flag']:
        print('登陆成功')
        choice(sk)
    else:
        print('登录失败')
        sk.close()
        break

client
client

udp多人聊天

# -*- coding: utf-8 -*-
# @Time    : 2019/4/9 16:35

import socket

server_socket = socket.socket(type=socket.SOCK_DGRAM)

server_socket.bind(('127.0.0.1',9000))

while True:
    conn,addr = server_socket.recvfrom(1024)
    print('消息来自%s:%s %s'%(addr[0],addr[1],conn.decode('utf-8')))
    msg = input('回复消息>>>:')
    server_socket.sendto(msg.encode('utf-8'),addr)

server
server
import socket
client_socket = socket.socket(type=socket.SOCK_DGRAM)
dic = {
    '华哥':('127.0.0.1',9000),
    '黑哥':('127.0.0.1',9000),
    '李健':('127.0.0.1',9000),
    '钱羽':('127.0.0.1',9000),
    '雄哥':('127.0.0.1',9000),
    '王月阳':('127.0.0.1',9000),
}

while True:
    name = input('请选择聊天对象').strip()
    name = dic[name]
    while True:
        msg = input('>>>:').strip()
        client_socket.sendto(msg.encode('utf-8'),name)
        a,b = client_socket.recvfrom(1024)
        print(a.decode('utf-8'))

client
client
相关文章
相关标签/搜索