python核心-网络编程--socket

socket

1.socket简介

socket—套接字

python中提供socket标准库,非常底层的接口库。

socket是一种通用的网络编程接口,和网络层次没有一一对应的关系。

2.socket的理解

怎么理解socket呢?在我百度socket的时候,不小心点成了图片,出现了如下情景。

然后我还不相信,socket不就是网络通信的专用语,咋成了接插口、插座呢?

原来socket就是从插口来的,我们可以把它想象成插口。

3.socket的类型

名称含义
socket_stream面向连接的流套接字,tcp协议
socket_dgram无连接的数据报文套接字,udp协议

 

大家可以看到一种是tcp,一种是udp.

tcp:

面向连接的、可靠的、基于字节流的传输层通信协议,使用三次握手协议建立连接、四次挥手断开连接. 通过TCP连接传送的数据,无差错、不丢失、不重复,按序到达

udp:

无连接的基举数据报的传输层通信协议,不保证可靠交付,但具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。

相信大家在《计算机网络》这门课学到了很多,我在这里也不作赘述了。

3.socket通信

之前我们了解了socket是插座,那到底是怎么通信的呢?大家知道通信一般分为客户端和服务端。

如果是第一次了解的话,那么就可以理解成下面所示的样子。

4.socket流程图  (重点)

    4.1流程详解

下面我将详细介绍socket的密码连接,揭开他们之间的神秘面纱。对于学过的人来说可能是比较繁琐,如果你想用于查工具、看代码的话,可以直接跳到代码部分。如果第一次接触,希望你可以耐心的看完我的讲解。

    1.首先大家可以看到,客户端和服务端都是socket开头的。说明和我之前想的那个图是一样的。那大家会疑问?不用区分客户端和服务端么?

    其实客户端和服务端都承担着两个角色,当另一端发出请求的时候,这一段发送对方想要的信息,这一段就是服务端。反之,也对应。

    2.先看右边这个,第二个是bind().绑定的是啥呢? 通过了解,我们可以知道绑定的是ip和端口号。绑定ip我知道,就是把两个实体连接起来,安绑定端口是?下面我就给大家详细的讲一下端口的作用,熟悉这一部分的可以直接跳到代码。

    如果我们只绑定ip的话。当对方发来信息,那么ip下的所有应用程序都会收到这个信息,那不是乱了套了。如果我们使用端口这个中介的话,那就可以完美的解决这个问题。

之后就是listen(),它表示监听。当服务端绑定了ip和端口后,并没有立马工作,而是在进行了监听操作后才进行的。监听这个动作就相当于socket穿好了装备,然后到门口站着等来消息的到来。它和绑定是连在一起工作的。

    3.accept().accept这个操作的作用是,进行调度。不知道大家想过没有,当我们访问网站时,也就是访问它的80端口。一个用户过来了,请问访问然后三次握手,就和80端口通信了。假如这时候又一个用户访问80端口要访问网站怎么办,上一个用户还没断。难道我就等着上一个用户离开?

其实是这样的,当一个用户访问80端口时,经过三次握手之后。80端口说:”我这挺忙的,还要招呼其他客人呢,我给你分配个人,我信任他,你和他通信就是和我通信。然后80端口分配了一个端口让他和那个用户通信。之后有来了个用户,也是这样。我画个示例图大家感受一下。

    4.connet()  connect的作用就是和accept()建立连接。当服务端主动发来信息时,我们客户端也需要响应,这时候就是connect()在起作用。connect()里面的参数和服务端的bind差不多,也是ip和端口。但不同的是,bind绑定的自身的ip和端口,它要告诉其他人我在这里。而connect填的是对方ip和端口号,它要的是我要去哪里。

    5.write() 、read() 之后就简单了。大家看流程图可以看到对称了write() 和 read(),其实她两就是发消息和收消息。当通信建立好了之后,不就是发消息—共享数据。在socket里面没有write和read 只有 send()和recv() .他们的作用是一样的。

    6.close()当客户端进行全部的通信后,说:“我完事了,我走了!”就向服务端发送一个close()请求。当服务端收到消息之后,就明白了:它走了,我也省事了,休息会。

5.代码部分

server端:

import socket
import threading
import datetime    

# 1. 先实现整体的功能框架
# 2. 再去根据流程图完成每个功能


# tcp server
class ChatServer:    # 服务端搭建起来有四步 大家对照流程图来理解我的代码
    def __init__(self, ip='127.0.0.1', port=9000):  # 初始化,每初始化一个socket,就要给它绑定ip和端口
        self.addr = (ip, port)
        self.sock = socket.socket()  1.这是第一步socket.socket()
        self.clients = {}

    def start(self):      
        self.sock.bind(self.addr)   2. 去绑定
        self.sock.listen()  # 服务启动了  3.开启监听
        s, raddr = self.sock.accept()  # 阻塞 4.accept,开始调度。  注意点1 
        threading.Thread(target=self.accept).start()

    def accept(self):
        while True:
            s, raddr = self.sock.accept()
            self.clients[raddr] = s
            threading.Thread(target=self.recv, args=(s,)).start()

    def recv(self, sock):
        while True:
            data = sock.recv(1024)  # 阻塞       注意点2
            msg = "{}.{}: {}".format(sock.getpeername(),datetime.datetime.now().strftime("%H:%M:%S"),data.decode()).encode()
            for s in self.clients.values():
                s.send(msg)#  encode是转换成byte,传输的是字节流,decode是编码

    def stop(self):
        for s in self.clients.values():
            s.close()
        self.sock.close()


cs = ChatServer()
cs.start()

client端: 

# 设计客户端
# 1.首先把整体内容写下来
# 2. 再去补充框架里的具体内容
import socket
import sys
import threading
import time

class ChatClient:
    def __init__(self, ip='127.0.0.1', port=9000):
        self.addr = (ip, port)
        self.sock = socket.socket()
        try:
            self.sock.connect(self.addr)  # 若要连接的服务端没有启动,这里会抛出异常
        except Exception as e:  # 不知道这里捕获什么具体的异常了,就用的一个比较基层的类Exception
            print('出现异常:', e)

    def chat_fa(self):
        print('欢迎来到聊天室')
        while True:
            data = bytes(input().encode('utf-8'))
            self.sock.send(data)
            if data == b'quit':
                print('%s退出'%self.sock.getpeername()[0])
                break
        self.sock.close()


    def chat_shou(self):
        while True:
            data = self.sock.recv(1024)
            if data != b'':
                print(data.decode('utf-8'))

    def stop(self):
        self.sock.close()


cc = ChatClient()
threading.Thread(target=cc.chat_fa).start()
threading.Thread(target=cc.chat_shou).start()

上面的客户端和服务端说实话并不理想,我一开始只想写个简单的。于是我就去别的博主上面找他们写的客户端和服务端,发现他们的太长了,根本看不下去啊!可是找了半天也没有简单的,于是我就硬着头皮去理解他们的代码,理解过后敲出了属于自己的客户端和服务端。结果发现自己的也这么老长,可能是因为理解的不透彻吧。

服务端的注意点

注意点1

这个就是我们上面提到的,accept招呼客人,这里用多线程的方式,产生一个线程去进行调度,否则第二个用户来了就会阻塞。

注意点2

也就是第二个阻塞地方。一开始我还想不明白,为什么在这里要加一个线程啊。这里就可以直接等待接收消息的功能啊,仔细想了想如果这里等待消息产生阻塞,那么服务端就不能实现其他功能了,这很鸡肋。如果我们使用多线程的方式,就可以轻松脱身,保持服务器的稳定状态。

客户端注意点

注意点1:

一开始我就是奔着收消息和发消息是用两个线程去实现。但是当我想到用两个线程去实现,同时的收信息和发信息。第一次我是用发消息的同时产生一个线程去实现接收,结果没成功。总结了原因是,发了消息才能接收,可能消息已经发过去了,收消息的线程还没打开/这样只能是先发消息才能收到,不符合逻辑。第二次,我同时开启收发消息的线程,这样就解决了。

知识点

当同时用两个线程去实现时,发现会一直收到消息为空的情况。因为他俩同时工作,我一看这情况顿时就否决了自己。然后东弄西调浪费了一下午,突然我在用qq的时候,想到了消息不能为空。顿时,灵机一动把消息为空的情况捕获到,程序就正常了。有时候成功就离我们一步远,然后我们稍不注意它就会溜走。

这就是  if data != b''这么几个代码,把我的程序盘活了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值