套接字

上一节简单提到过套接字,本篇阐述如何使用套接字进行数据传输。任意两台计算机都可以,简便起见,服务端和客户端均以自己计算机为例。

1.创建套接字

一般,用户认为数据可以直接在应用之间进行传输,实际则是应用调用套接字,数据通过套接字进行传输。

简单套接字实例

In [1]: import socket

In [2]: sock = socket.socket()

In [3]: sock
Out[3]: <socket.socket fd=512, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>

三种套接字

服务端监听套接字持续监听客户端套接字的状态,当客户端套接字发送连接请求时,监听套接字生成对应对等连接套接字(此时监听套接字可继续监听其他客户端套接字),实现与客户端套接字的对等连接,数据可互相传输。

2.建立套接字连接

   流程:1)服务端监听套接字创建,并启动监听

              2)客户端套接字创建,连接监听套接字并绑定

              3)生成对等连接套接字

提示:打开两个CMD,或者两个Xshell,一个模拟服务端,一个模拟客户端。

服务端套接字绑定

首先监听套接字创建,这里先不监听

In [2]: import socket

In [3]: address = ('127.0.0.1', 8080) #127.0.0.1是本地计算机IP,8080是端口号(可以为其他如 
                                       8081)

In [4]: socket_server = socket.socket()

In [5]: socket_server.bind(address)  #bind函数即绑定套接字,参数是一个元组

In [6]: socket_server
Out[6]: <socket.socket fd=992, 
family=AddressFamily.AF_INET,
type=SocketKind.SOCK_STREAM, 
proto=0, 
laddr=('127.0.0.1', 8080)> #绑定后,socket就获得了address

In [7]: socket_server.close() #使用完之后记得关闭

然后客户端连接

In [1]: import socket

In [2]: socket_client = socket.socket()

In [3]: socket_client
Out[3]: <socket.socket fd=924, family=AddressFamily.AF_INET, 
type=SocketKind.SOCK_STREAM,
 proto=0>  #注意,此时客户端还不具备自己的address

In [4]: socket_client.connect(('127.0.0.1', 8080))
---------------------------------------------------------------------------
ConnectionRefusedError                    Traceback (most recent call last)
<ipython-input-4-05bbfed8720f> in <module>()
----> 1 socket_client.connect(('127.0.0.1', 8080))

ConnectionRefusedError: [WinError 10061] 由于目标计算机积极拒绝,无法连接。
#之所以拒绝,是因为监听套接字还没有监听

服务端监听套接字监听

服务端启动监听

In [9]: import socket

In [10]: address = ('127.0.0.1', 8080)

In [11]: socket_server = socket.socket()

In [12]: socket_server.bind(address)

In [13]: socket_server.listen(5) #监听数5,最多允许5个客户端连接

客户端连接

In [5]: socket_client.connect(('127.0.0.1', 8080))

In [6]: socket_client #此时再连接就不再报错了
Out[6]: <socket.socket fd=924, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, 
laddr=('127.0.0.1', 59311), #连接后随机分配了address
raddr=('127.0.0.1', 8080)>  #也有了对应服务端套接字地址

此时服务端套接字内容变为

In [14]: socket_server
Out[14]: <socket.socket fd=736, 
family=AddressFamily.AF_INET, 
type=SocketKind.SOCK_STREAM, 
proto=0, l
addr=('127.0.0.1', 8080)> #raddr消失

服务端接受连接并生成对等连接套接字

In [15]: res = socket_server.accept() #接受连接

In [16]: type(res) #查看类型,返回一个元组
Out[16]: tuple

In [17]: conn, addr = res

In [18]: conn #元组第一个是对等连接套接字
Out[18]: <socket.socket fd=972, 
family=AddressFamily.AF_INET, 
type=SocketKind.SOCK_STREAM, 
proto=0, 
laddr=('127.0.0.1', 8080), 
raddr=('127.0.0.1', 59311)>  #远端对等套接字地址

In [19]: addr #元组第二个是对等套接字地址元组
Out[19]: ('127.0.0.1', 59311)

In [20]: conn is socket_server
Out[20]: False #对等连接套接字和服务端套接字是不同的套接字

服务端accept会阻塞

若服务端监听并处于accept状态而无客户端连接,便会阻塞

若此时有客户端连接,立刻结束阻塞变为可执行

连接的常见错误

In [28]: socket_server.bind(('127.0.0.1', 8080))
---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
<ipython-input-28-b4da0b656584> in <module>()
----> 1 socket_server.bind(address)

OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。

每个端口只允许使用一次,此时需要更换端口号8080为其他端口(如8081)

3.使用套接字传输

客户端发送数据,

In [7]: socket_client.send(b'hello python') #只能发送bytes类型
Out[7]: 12 #返回发送出去的字节数

服务端接收数据

In [21]: conn.recv(1024) #指明一次最多接受的字节数量
Out[21]: b'hello python'

服务端也可发送数据

In [22]: conn.send('你好'.encode()) #发送中文需要编码
Out[22]: 6

客户端接收

In [8]: data = socket_client.recv(1024)

In [9]: data.decode()
Out[9]: '你好'

recv阻塞

若无数据传输而进行recv接收则会阻塞

4.断开套接字连接

客户端主动断开连接

In [9]: socket_client.close()

主要操作

  • 操作一: 服务器套接字绑定IP端口:bind
  • 操作二: 服务器套接字监听:listen
  • 操作三: 客户端套接字连接服务器:connect
  • 操作六: 套接字发送消息:send
  • 操作七: 套接字接受消息:recv
  • 操作八: 套接字关闭连接:close

命令行简单版本完整代码

服务端

In [9]: import socket

In [10]: address = ('127.0.0.1', 8080)

In [11]: socket_server = socket.socket()

In [12]: socket_server.bind(address)

In [13]: socket_server.listen(5)

In [14]: socket_server
Out[14]: <socket.socket fd=736, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080)>

In [15]: res = socket_server.accept()

In [16]: type(res)
Out[16]: tuple

In [17]: conn, addr = res

In [18]: conn
Out[18]: <socket.socket fd=972, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 59311)>

In [19]: addr
Out[19]: ('127.0.0.1', 59311)

In [20]: conn is socket_server
Out[20]: False

In [21]:

In [21]: conn.recv(1024)
Out[21]: b'hello python'

In [22]: conn.send(b'hi')
Out[22]: 2

客户端

In [1]: import socket

In [2]: socket_client = socket.socket()

In [3]: socket_client
Out[3]: <socket.socket fd=924, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>

In [4]: socket_client.connect(('127.0.0.1', 8080))

In [5]: socket_client
Out[5]: <socket.socket fd=924, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 59311), raddr=('127.0.0.1', 8080)>

In [6]: socket_client.send(b'hello python')
Out[6]: 12

In [7]: socket_client.recv(1024)
Out[7]: b'hi'

In [8]: socket_client.close()

python版本完整代码(pycharm)

服务端


import  socket

sock= socket.socket() #生成套接字
sock.bind(('',8086)) #绑定
sock.listen(10) #监听
while True:
    while True:
        try: #无连接,进入except异常,pass,继续循环判断有无连接,有连接break去接收数据
            con,addr=sock.accept() 
            print(addr,'已连接')
            break
        except BlockingIOError:
            pass
    while True:
        try:
            data = con.recv(1024) #查询有无数据发送,try捕获异常
            if data:
                print(data.decode())
                con.send(data) #将客户端发送的数据返还
            else:
                con.close() #接收数据为空,则关闭连接,无数据发送
                break
        except Exception as e:
            print(e)
            print(addr,'用户退出')
            break

客户端

import socket

client = socket.socket()

client.connect(('127.0.0.1',8086))

while True:
    data = input('please write the message:')
    client.send(data.encode())
    print(client.recv(1024).decode()) #接收服务端返还的数据
    if data == 'Q' or data == 'q': #输入q或Q退出
        break

client.close()

运行结果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值