Socket的简介

Socket别名"套接字"

两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket
TCP/IP 协议族的一种封装,是应用层与TCP/IP协议族通信的中间软件抽象层。

在网上扒的Socket的流程图(画这个图的作者思路非常清晰)

 

python 网络通信 更新UI python网络通信框架_TCP

TCP服务端

围绕上述的流程图,做一次刨析

①:创建套接字(Socket)

import socket
# 引入socket模块用默认的socket构造方法创建套接字
st = socket.socket()

源码默认构造:family=AF_INET,type=SOCK_STREAM

def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None):
        # For user code address family and type values are IntEnum members, but
        # for the underlying _socket.socket they're just integers. The
        # constructor of _socket.socket converts the given argument to an
        # integer automatically.
        _socket.socket.__init__(self, family, type, proto, fileno)
        self._io_refs = 0
        self._closed = False

没看懂什么意思,后来查了一下

family参数指的是host的种类:

AF_UNIX:也叫AF_LOCAL,基于本地文件的

AF_NETLINK:这是linux系统支持的一种套接字

AF_INET:这个套接字是基于网络的,对于IPV4协议的TCP和UDP(常用)

AF_INET6:这个套接字是基于网络的,对于IPV6协议的TCP和UDP

type参数指的是套接字类型:

SOCK_STREAM:流套接字,使用TCP socket(常用)

SOCK_DGRAM:数据包问套接字,使用UDP socket(常用)

SOCK_RAW:raw套接字

②:socket绑定地址 (ip+端口)

host = 'xx.xxx.xx.xxx'
port = xxxx
st.bind((host, port))

③:socket监听

注意:这里listen后面的参数代表不是最大监听数
而是服务器拒绝(超过限制数量)连接之前,可以挂起的最大连接数量,2可以看做成是"排队的数量"

也就是说此刻一个线程正在连接,剩下两个(listen参数是2)排队等待连接,是没问题。 

# 监听链接
st.listen(2)

④:接受客户端的连接

返回一个客户机socket,带有客户机端的地址信息。

调用accept方法的时候,socket会进入阻塞状态。客户请求连接时,方法建立连接并返回服务器。

accept方法返回一个双元素元组,形如(connection,address)。第一个元素是新的socket对象,第二个元素是客户的IP地址。

 此刻服务端在等待客户端发送连接

# 接受客户端链接
conn, addr = st.accept()

 ⑤:服务端读取或者发送消息
判断客户端有连接过来时,打印对方的ip地址

while 1:
  print(addr)
  print("Connection successfully!!!!!!".encode())

send和sendall区别:
send
返回发送的字节数,该字节数可能少于请求的字节数。 
sendall返回None成功传输所有数据,并引发错误的异常。

def send(self, data: bytes, flags: int = ...) -> int: ...
    def sendall(self, data: bytes, flags: int = ...) -> None: ...  # return type: None on success

而且这两个方法参数都是byte类型
我看其他文章有以下写法的,运行的时候必然报错(自己写完的文章至少得测试一下.......)

conn.sendall("xxxxx")

 这样才不会报错

conn.sendall("xxxxx".encode("utf-8"))

recv() 

从socket中接收数据,最多接收bufsize个字符,一般填写1024个

def recv(self, bufsize: int, flags: int = ...) -> bytes: ...

⑥:关闭socket连接


这个地方稍微注意一下,不要写在循环里面,写在外层. conn.close()


服务端源码:

import socket
# 此处换成自己的ip作为服务器端
host = 'xx.xxx.xx.xxx'
port = 9999
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((host, port))
s.listen(2)
conn,addr= s.accept()#创建socket和客户端通信;
if 1:
  print("Connection successfully!!!!!!".encode())
  conn.sendall("who are you??".encode("utf-8"))
  print(conn.recv(1024))
conn.close()

TCP客户端

①:创建套接字(Socket)

c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

②:建立指定连接(连接到服务端)

#建立到指定IP地址,端口的TCP连接
c.connect(('xx.xxx.xx.xxx', 9999))

③:同样接受或者发送消息(与服务端相同)

if 1:
  result=c.recv(1024)
  print(result)
  c.send("My name is yuwenwen".encode("utf-8"))

④:关闭连接

c.close

客户端源码:

import socket
#创建TCP类型的socket
c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#建立到指定IP地址,端口的TCP连接
c.connect(('10.143.50.227', 9999))
if 1:
  result=c.recv(1024)
  print(result)
  c.send("My name is yuwenwen".encode("utf-8"))
c.close

测试一下:

先开服务端,再开客户端

client端控制台

python 网络通信 更新UI python网络通信框架_python 网络通信 更新UI_02

Server端控制台

python 网络通信 更新UI python网络通信框架_网络_03

 如此可见,双方交互的信息都能接收到,一个丐版的聊天工具雏形就出来了