网络编程之套接字socket

一、套接字(socket)概述

套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。在这里插入图片描述

socket有很多种类型,但是咱们只需要知道两种就可以了

sk = socket.socket(family = AF_INET, type=SOCK_STREAM)

family :
1.AF_UNIX基于文件类型的套接字(早期socket是源自于unix系统而研发的一个功能,主要是为了同一台电脑上,多个程序直接通信) unix系统的中心思想是 : 一切皆文件
2.AF_INET基于网络类型的套接字

  type:
    一种是基于TCP协议   SOCK_STREAM
    一种是基于UDP协议   SOCK_DGRAM

  tcp 协议 : 可靠的,面向连接的,面向字节流形式的传输方式
  udp协议 : 不可靠的,不面向连接的,面向数据报的传输方式,但是它快

打开socket源码可以看到:

二、基于TCP协议的socket

在这里插入图片描述
所有的业务逻辑都在接收连接内进行,其他的都是一样的。

socket初使用

程序示例:
server_.py

import socket
import time
sk = socket.socket()# 不传参数,默认使用基于网络类型的套接字, 协议 : TCP
# 127.0.0.1
sk.bind(('127.0.0.1',8080))# 端口的范围是0-65535   但是 0-1023 这些是保留的,别用
sk.listen()# 监听,同时能接受的连接

print(123)
conn,addr = sk.accept()# 等待接受客户端的连接  阻塞等待
print(456) # 客户端运行连接之后才执行
ret = conn.recv(1024)  #接收客户端信息
print(ret)       #打印客户端信息
conn.send(b'hi')        #向客户端发送信息
print('conn:',conn)
print('addr:',addr)

time.sleep(20)

conn.close()
sk.close()

client_.py

import socket
import time
sk = socket.socket()

sk.connect(('127.0.0.1',8080))# 连接
sk.send(b'hello!')
ret = sk.recv(1024)         # 对话(发送/接收)
print(ret)
time.sleep(20)

sk.close()

刚开始运行服务器端(server_.py):
在这里插入图片描述
由于客户端还没连接上服务器端, 所以会先等待接受客户端的连接 (阻塞等待)

运行客户端(client_.py):
在这里插入图片描述
发现已经连接上服务器端,并接收到服务器端发来的数据(b’hi’)

再打开server_.py的控制台:
在这里插入图片描述
可以看到客户端连接之后,456被打印出来,并且收到了客户端发来的数据(b’hello!’)

conn显示的是连接信息,里面的laddr是服务器端的ip地址和端口号,raddr是客户端的ip地址和客户端运行之后自己产生的端口号
addr:客户端的ip地址和端口号, ip地址 + 端口 能唯一找到某台电脑上的某一个服务程序

如果发送的是中文数据,则使用编解码

程序示例

sever_.py

import socket
import time
sk = socket.socket()
sk.bind(('127.0.0.1',18080))
sk.listen()# 开机
print(123)
conn,addr = sk.accept()

msg_r = conn.recv(10)# 接受数据,接受10个字节
print(msg_r.decode('utf-8'),addr)
conn.close()
sk.close() 

client_.py

import socket
import time
sk = socket.socket()

sk.connect(('127.0.0.1',18080))# 连接

sk.send('中文'.encode('utf-8'))

sk.close()

服务器端收到效果:
在这里插入图片描述

简易聊天程序

基于以上代码,将此改成,可以一直聊天的逻辑,并且带 标识退出的的逻辑,程序示例:
sever_2.py

import socket

sk = socket.socket()

sk.bind(('127.0.0.1',10086))
sk.listen()
conn,addr = sk.accept()

while 1:
    mess = conn.recv(1024)
    print(mess.decode('utf-8'))
    if mess == 'q':
        break
    mess2 = input('服务器端发送消息:')
    conn.send(mess2.encode('utf-8'))
    if mess2 == 'q':
        break

conn.close()
sk.close()

client_2.py

import socket

sk = socket.socket()

sk.connect(('127.0.0.1',10086))

while 1:
    mess = input('客户端发送消息:')
    sk.send(mess.encode('utf-8'))
    if mess == 'q':
        break
    mess2 = sk.recv(1024)
    print(mess2.decode('utf-8'))
    if mess2 == 'q':
        break

sk.close()

效果展示:
客户端:

在这里插入图片描述

服务器端:
在这里插入图片描述
能实现聊天,但是,这样也有一种问题,如果再开一个客户端给服务器发消息,则服务器端收不到第二个客户端发的消息,就像服务器在接电话,第三者给他打电话就不行。那如果想接第三者的电话,必须先挂断正在进行的通话。
服务器端的代码可以试着这样改:

import socket

sk = socket.socket()

sk.bind(('127.0.0.1',10086))
sk.listen()
while 1:
    conn,addr = sk.accept() # 一直接电话
    while 1:
        mess = conn.recv(1024)
        print(mess.decode('utf-8'))
        if mess == 'q':
            break
        mess2 = input('服务器端发送消息:')
        conn.send(mess2.encode('utf-8'))
        if mess2 == 'q':
            break

    conn.close()
sk.close()

效果:
客户1:
在这里插入图片描述
服务器端:
在这里插入图片描述
客户2:
在这里插入图片描述
这样能实现服务器与多个客户端轮流聊天,但是这里明显有一个缺陷,服务器端需要先挂断当前聊天才能与下一个客户端聊天,不能实现同时与多个客户端聊天。udp 就没有这个问题,可以说是相当强大了。

三、基于UDP协议的socket

在这里插入图片描述
可以看出,udp相比于tcp逻辑更简单,这意味着代码量会更少,并且最最重要的是服务器端可以同时和多个客户端通信。

基于UDP协议的简易聊天程序

sever_3.py

import socket

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

while 1:
    s ,addr =sk.recvfrom(1024)
    print(s.decode('utf-8'))
    mess = input('服务器端发送消息:')
    sk.sendto(mess.encode('utf-8'), addr)

sk.close()

client_3.py

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)
while 1:
    mess = input('客户端发送消息:')
    sk.sendto(mess.encode('utf-8'),('127.0.0.1',8990))
    s, addr = sk.recvfrom(1024)
    print(s.decode('utf-8'))


sk.close()

效果:
客户端1:
在这里插入图片描述
服务器端:
在这里插入图片描述
客户2:
在这里插入图片描述
这样一个服务器端可以和多个客户端同时通信,从实用性上看,可以明显感觉到UDP用着更便捷。

改进,署名,输出带颜色

上面的客户端看着比较丑陋,咱们把客户端名字调整一下,并在服务器端输出文字带颜色,看起来更炫酷。
首先看一下颜色表,在这里插入图片描述
图片截自:pycharm控制台输出带颜色
示例:

print('\033[31m你好~\033[0m')

效果:
在这里插入图片描述
万事具备,开搞:

sever_.py

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)# udp协议
sk.bind(('127.0.0.1',8090))
# 需求: 根据每个客户端的名字,去加上颜色
dic = {'悟空':'\033[32m','张三丰':'\033[33m','赵敏':'\033[35m'}
# 收发
while 1:
    msg_r,addr = sk.recvfrom(1024)# 接收来自于哪里的消息

    msg_r = msg_r.decode('utf-8')
    name = msg_r.split(':')[0].strip()
    color = dic.get(name,'')# get(key,default)  获取字典中key对应的value,如果没有key则返回default
    print('%s %s \033[0m'%(color,msg_r))
    msg_s = input(('>>>'))
    sk.sendto(msg_s.encode('utf-8'),addr)# 发给谁的消息

sk.close()

client_.py

import socket
sk = socket.socket(type=socket.SOCK_DGRAM)# udp协议
name = input('请输入您的名字:')
# 收发
while 1:
    msg_s = input(('>>>'))
    info = name + ' : ' + msg_s
    sk.sendto(info.encode('utf-8'), ('127.0.0.1',8090))  # 发给谁的消息
    msg_r,addr = sk.recvfrom(1024)# 接收来自于哪里的消息
    print(msg_r.decode('utf-8'))


sk.close()

客户1:
在这里插入图片描述
客户2:
在这里插入图片描述
最终服务器端效果:
在这里插入图片描述

读书正能量分享

越来越糟,还是会侥幸变好,取决于你的努力,以及面对大方向的选择,这基本取决于一个人一生的浮沉。做一个努力的人的好处在于,人人见了你都想帮你,如果你自己不做出一点努力的样子,人家想拉你一把,都不知道你的手在哪里。
——刘同 《向着光亮那方》

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰履踏青云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值