python笔记系列-day22:网络编程socket

目录

通信方法

Python对网络编程的支持

URL

urllib 模块

urllib.request 模块

urllib.error 模块

urllib.parse 模块

urllib.robotparser 模块

使用 urllib.parse 解析url

注意点:

ParseResult 属性 

需要注意的是 urllib.parse.ParseResult

urlunparse() 函数

parse_qs() 和 parse_qsl() 函数 和 urlencode() 函数

urljoin 函数

使用 urllib.request 模块读取资源

urllib.request.urlopen 使用例子

urllib.request.urlopen 函数传入参数 data

使用 request对象来组织 urlopen函数的请求参数

管理Cookie

使用步骤:

读取 cookie 的值

 基于TCP 协议的socket编程

socket模块

socket 模块方法

使用 socket 创建服务器端和客户端

服务器端的socket

客户端的socket

注意点:

实践

非阻塞式socket通信 

编写思路:

selectors模块

实践

基于UDP 协议的 socket

udp协议的socket 使用

udp之中的 服务端和客户端

UDP 的程序例子


通信方法

我们知道程序和程序之间是可以进行数据交换的。一个程序的输出是另外一个程序的输入。例如 linux中的管道

这实际上就是 进程之间的通信方法,进程之间的通信方法有 管道,信号,(操作系统)消息队列,共享内存,信号量。

这是一台主机上的程序通信的方法。

如果程序分布在不同主机上呢,如何进行通信呢?

rpc , 消息队列,数据库池,http协议等

那么这些都是使用了网络编程,需要借助 tcp/ip 协议 。 

但是这些协议模型有多个层,编程的时候如果是不同的协议就要使用不同的接口,会加大编程的复杂度。

因此 unix bsd 设计了 socket  , 有兴趣可以了解下 socket是如何设计的

当初如果学过java的 applet ,应该已经使用过socket 写过程序了,例如那个 聊天室

那么 python 呢,很类似的,python也提供socket ,而且对应了 unix bsd 设计的 socket 类型

  1. SOCK_STREAM 提供有序的、可靠的、双向的和基于连接的字节流服务,当使用Internet地址族时使用TCP。
  2. SOCK_DGRAM 支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务,当使用Internet地址族使用UDP。
  3. SOCK_RAW 原始套接字,允许对底层协议如IP或ICMP进行直接访问,可以用于自定义协议的开发。

Python对网络编程的支持

通过前面的学习python通过模块支持各类功能,那么网络编程也是,下面是python 提供的模块

URL

Uniform Resource locater , 统一资源定位器 , 是互联网资源的指针

资源可以是简单的文件或目录,也可以是对复杂对象的引用

urllib 模块

该模块中包含了多个用于处理url的子模块

urllib.request 模块

这个是核心模块,包含了打开和读取url 的各种函数

urllib.error 模块

主要包含了 urllib.request 模块中的各种异常

urllib.parse 模块

用于解析 url

urllib.robotparser 模块

用于解析 robots.txt 文件

 

使用 urllib.parse 解析url

 

 

注意点:

  1. 如果url 以 // 开头的,仍然可以识别出主机 , 只是 scheme 为空
  2. 但是如果url 既没有 shcheme 也没有 // 开头,那么 urlparse() 将这些url 都当成资源路径

 

ParseResult 属性 

 

需要注意的是 urllib.parse.ParseResult

如同这种结果 形式的结果成为 结构化结果

这种结果 可以从元组的角度看,也可以从 结构对象角度看.

因此这种对象 可以使用元组来构造它,同样可以将它视为元组传入给一个函数

 

urlunparse() 函数

如果从元组角度看,就可以使用 [] 来访问元素了

 

 

同理 ,上面的操作是可逆的,可以使用

urlunparse() 函数同样可以接收一个 urllib.parse.ParseResult 对象将

这个对象或者一个元组恢复成一个url 字符串

 

parse_qs() 和 parse_qsl() 函数 和 urlencode() 函数

这两个函数都用于解析查询字符串 , 只是返回值不同而已

parse_qsl() 的返回值是 list

而 parse_qs() 返回的是一个字典

urlencode() 则是他们的逆函数

上面使用的 urlparse() 结果中有一个 query 属性表示的是 url 中的查询字符串

可以使用 这两个函数来解析这个结果

 

urljoin 函数

urljoin() 函数负责将两个 url 拼接到一起 , 返回拼接的绝对地址

 

 

使用 urllib.request 模块读取资源

urllib.request 子模块非常有用

有一个非常实用的 函数 urllib.request.urlopen (url , data=None) 方法读取资源

根据请求的url 不同,该函数的返回值返回的东西不同

如果请求的 url 是一个 HTTP 地址 , 那么返回值就是 http.client.HTTPResponse 对象

urllib.request.urlopen 使用例子

from urllib.request import *

with urlopen('https://blog.csdn.net/fk002008/article/details/87553034') as f:

    data = f.read(2326)
    print(data.decode(encoding='utf-8'))

urllib.request.urlopen 函数传入参数 data

可以使用 data来给请求的 url 传入参数 , 比如一个 web 应用

 

使用 request对象来组织 urlopen函数的请求参数

使用 urllib.request 模块中的 Request 对象来构建请求参数

urllib.request.Request(url , data = None , headers={} ,origin_req_host = None,unverifiable=False,method=None )

from urllib.request import *

params = 'request data'.encode(encoding='utf-8')

req = Request(url='https://blog.csdn.net/fk002008/article/details/87553034',
    data=params)

req.add_header('Referer','https://blog.csdn.net')

with urlopen(req) as f:
	print(f.status)
	#print(f.read().decode('utf-8'))

 

管理Cookie

使用 urlopen 可以发送 get , post , put ,delete patch 等请求

绝大多数情况下可以使用 urllib.request 模块替代这个 http.client 模块

但是如果想要一些保护的信息,比如 session 等此时要借助 cookie 管理器来实现需要 http.cookiejar 模块:

使用步骤:

  1. 创建 http.cookiejar.CookieJar 对象或其子类的对象
  2. 以 CookieJar 对象为参数创建 urllib.request.HTTPCookieProcessor 对象,该对象负责调用 CookieJar 对象来管理 cookie
  3. 以 HTTPCookieProcessor 对象为参数调用 urllib.request.build_opener() 函数创建一个 OpenerDirector 对象
  4. 使用 OpenerDirector 对象发送请求


from urllib.request import *
import http.cookiejar  , urllib.parse

cookie_jar = http.cookiejar.MozillaCookieJar('a.txt')

cookie_processor = HTTPCookieProcessor(cookie_jar)

opener = build_opener(cookie_processor)

request = Request('https://blog.csdn.net/fk002008/article/details/87553034')

#使用 opener 发送请求
response = opener.open(request)
print(response.read().decode('utf-8'))


##将kookie 写入到文件中
cookie_jar.save(ignore_discard=True ,ignore_expires=True)

读取 cookie 的值

cookie_jar.load('a.txt',ignore_discard=True , ignore_expires=True)



from urllib.request import *
import http.cookiejar , urllib.parse

cookie_jar = http.cookiejar.MozillaCookieJar('a.txt')

cookie_jar.load('a.txt',ignore_discard=True , ignore_expires=True)

for item in cookie_jar:
	print('name=',item.name)
	print('value=',item.value)

 

 

 

 

 

 

 基于TCP 协议的socket编程

python提供socket模块 支持tcp协议网络程序开发

tcp协议称为端对端协议,建立虚拟链路用于发送和接收数据。也是请求-响应模式的

必须接受到对方的确认消息不然会进行重发。

可以不通过socket类支持,请求方创建自己的socket,响应方创建自己的socket

使用 socket来进行数据的通信交互就如同具有虚拟链路一样

socket阻塞式交互如同打电话一样,可靠性很高

一个作为 服务器端使用的 socket : 需要绑定 ip地址和端口,并在ip地址和端口进行监听

一个是客户端使用的 socket ,该 socket和 服务器端的socket 进行连接

这样可以创建一个基于 tcp协议的网络连接

 

 

socket模块

使用 socket模块的 socket函数创建socket

socket.socket(family=AF_INET,type =SOCK_STREAM,proto=0,fileno=None)

参数说明:

family: 用于指定网络类型

  1. socket.AF_UNIX (UNIX 网络)
  2. socket.AF_INET (ipv4 网络) 默认的
  3. socket.AF_INET6 (ipv6 网络)

type: 指定 socket的类型

  1. SOCK_STREAM 基于 tcp协议的socket ,是默认的
  2. SOCK_DGRAM 基于UDP 协议的 socket
  3. SOCK_RAW 创建原始的 socket

proto: 用于指定协议号,没有特殊要求的话传入0

 

socket 模块方法

socket 如下方法:

函数名称

功能描述

socket.accept()

服务器端使用的socket , 被动接受TCP客户端连接,(阻塞式)等待连接的到来 。返回值是元组,返回客户端socket和addr

addr是一个元组,包含了 ip和端口

socket.bind(address)

服务器端使用的socket,将socket绑定到指定的adress

该address 可以是一个元组 ,包含ip 和端口

socket.close()

关闭连接释放资源

socket.connect(address)

客户端使用的socket,连接远程服务器

socket.connect_ex(address)

和上面的方法功能相同,只是出错时候不会抛出异常

只是返回一个错误标识符

socket.listen([backlog])

服务器端socket调用该方法进行监听

socket.makefile(mode='r')

创建一个和该socket关联的文件

socket.recv(bufsize[,flags])

接收socket中的数据 , 该方法返回 bytes对象作为结果

所以得到结果需要进行 decode 转换为 字符串

socket.recvfrom(bufsize[,flags])

和上面方法功能相同,但是返回值是一个(btyes,address)

元组

socket.recvmsg(bufsize[ancbufsize[,flags]])

不仅接收socket中数据还接收socket中辅助数据,因此返回值是一个

长度为4的元组 , (data,ancdata,msg_flags,address)

socket.recvmsg_into(buffers[ancbufsize[,flags]])

和上面的方法功能相同,但是将受到的数据存入 buffers中

  

socket.recvfrom_into(buffer[,nbytes[,flags]])

类似于 socket.recvfrom 方法,但是将收到数据放入到

buffer中

socket.recv_into(buffer[,nbytes[,flags]])

类似于socket.recv 方法,但是收到的数据放入到了 buffer中

socket.send(bytes[,flags])

向 socket 发送数据,该socket必须与远程的socket建立了连接

基于 tcp 协议发送数据

注意发送的数据是 字节串 bytes,如果是字符串需要进行 译码 encode

socket.sendto(bytes,address)

基于 udp 协议发送数据 , 所以该socket没有和远程的socket建立连接

socket.sendfile(file,offset=0,cont=None)

将整个文件发送出去直到文件的EOF

socket.shutdown(how)

关闭连接,其中 how 指明了关闭方式

socket.sendall()

完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常

socket.getpeername()

返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)

socket.getsockname()

返回套接字自己的地址。通常是一个元组(ipaddr,port)

socket.setsockopt(level,optname,value)

设置给定套接字选项的值。

socket.getsockopt(level,optname[.buflen])

返回套接字选项的值

socket.settimeout(timeout)

设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())

socket.gettimeout()

返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。

socket.fileno()

 

返回套接字的文件描述符。

socket.setblocking(flag)

如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常

  

 

使用 socket 创建服务器端和客户端

服务器端的socket

  1. 创建socket 对象      socket.socket()
  2. 绑定 IP地址和端口   socket.bind(addr)
  3. 开始监听  socket.listen
  4. 接收请求 accept() 得到 客户端的 socket , 如果有多个客户端那么就循环接收客户端发送的数据都在 客户端的 socket 中, 服务器端的socket只是用于 accept() 得到客户端的套接字
  5. 将要推送到客户端的消息用 客户端socket 发送即可

 

客户端的socket

  1. 创建 socket 对象
  2. 连接服务器端  socket.connect(addr)
  3. 使用自己的socket获取数据 
  4. 使用自己的socket发送数据

 

注意点:

socket对象用来发送数据和接收数据

客户端发送的数据,服务器端一定要给响应的

也就是 recv方法会阻塞 ,服务器端一定要 send 与之配对

 

accept():

一旦建立一个连接之后,双方可以使用这个 socket进行通信了

accept() 表示服务器和一个 客户端socket 套接了

只要调用一次,服务器端就可以套接一个客户端 socket

调用多次,服务器端可以接收多个客户端的 socket

socket关闭:

不管是哪一方的 socket 关闭之后 ,那么这个虚拟连接就中断了

ConnectionAbortedError: [WinError 10053] 您的主机中的软件中止了一个已建立的连接。

 

发送数据:

一旦客户端的 socket 和服务器端的 socket 建立连接之后 ,网络连接就建立了

建立连接之后就不用区分 服务器端和客户端了,使用各自的 socket 进行通信

那么 你使用了哪个 socket 对象 那么就 用它发送数据和获取数据

比如: 服务器端 accept之后可以得到客户端的 socket , 那么使用 客户端的 socket 就可以

发送数据给客户端了,然后客户端使用自己的 socket 从里面拿去数据即可

 

实践

编写  server.py 充当服务器

编写 client.py 充当客户端

需要注意:

1.发送的数据和接收到的数据都是 字节串因此中间需要一些 编码和译码的操作字符串本身有  decode 译码 和 encode 编码等方法

2.这个例子服务器是单机的,并不是多线程的,一次只能接收一个客户端请求

   服务器使用了 客户端的 socket 发送数据和接收客户端的数据并处理

3.客户端使用客户端的socket进行数据的发送以及收到服务器端响应的内容

客户端可以输入内容,服务器接收后并响应客户端收到的信息

server.py


import socket
# 创建一个socket对象,使用 默认值进行
s = socket.socket() 
## s = socket.socket(socket.AF_INET6,socket.SOCK_DGRAM)  创建一个基于 ipv6 的UDP 连接
addr = ('127.0.0.1',9999)
s.bind(addr)
s.listen(20) #设置最大连接数,超过后排队

print('服务器已经启动@',addr)
c_s , addr = s.accept()
print('客户端的地址是 :' ,addr)

## 服务器端发送一个响应数据,
##因为客户端要获取这个数据只能从客户端的socket中获取
c_s.send('response_code=200'.encode('utf-8'))

while True:
	## 继续接受数据
	print('服务器正在等待接收数据...')
	msg = c_s.recv(1024)
	print('服务器端收到请求:',msg.decode('utf-8'))
	msg = '服务器收到请求 %s' % msg.decode('utf-8')
	c_s.send(msg.encode('utf-8'))

client.py



import socket

s = socket.socket()

addr = ('127.0.0.1',9999)
print('客户端正在连接服务器 ',addr)
s.connect(addr)
msg = s.recv(1024) ## 返回的结果是 bytes对象 
print('服务器端的响应 : ' ,msg.decode('utf-8'))

while True:
    print('请输入要发送的内容:...')
    param = input()
    if param=='exit':
        break
    print('客户端发送数据 :',param)
    s.send(param.encode('utf-8')) 
    msg = s.recv(1024)
    print('收到服务器响应:',msg.decode('utf-8'))
s.close()

 打开两个 cmd窗口 

分别输入  python  server.py 启动服务期

                 python client.py 启动客户端

 

 

 

 

 

 

 

非阻塞式socket通信 

上面的例子都是 阻塞式的 socket通信,服务器如果没有响应当前的客户端,另外一个客户端的请求会被阻塞

当前客户端的程序也会被阻塞

如果想使用非阻塞的socket ,可以借鉴这个  事件编程的思想。

事件编程: javascript 中注册事件以及事件函数,当事件触发之后调用这个函数 回调,属于事件驱动的

                    java中 awt编程时候注册监听器实现监听函数也是回调函数的一种

那么程序的服务端设计成事件注册,由客户端来进行事件的触发,这样通过事件驱动

写的多了你会发现 以往的循环逻辑可以通过事件驱动来代替成了 没有循环体的存在

 

编写思路:

1.服务器端可以接收多个客户端,因此需要一个列表来存储这个 客户的socket

2.服务器端需要事件注册的功能 , python里面是通过 selectors 模块提供支持的

   java中注册事件比如 button  ,button.addActionListener(一个匿名的actionListener内部类,实现了监听方法) 

   如果我们有一个专门事件对象也可以转化为

                                   事件对象.注册事件(需要注册事件的对象,事件名称,回调函数)

python中的 selectors模块就是这样的,通过 该模块的 DefaultSelector 类对象可以按照上面的方式给对象注册事件

 

3.服务器端在启动的时候,就给服务器端的 socket注册一个事件,该事件用于 只要客户端连接服务器,就会触发该事件

  因此可以将 服务器端 原来处于循环中的 accept() 转换为 事件回调函数内部逻辑

该事件回调函数 会调用 accept() 获取当前触发事件的客户端的  socket,并且给该客户端的socket注册一个事件用于

客户端数据发送和读取的处理

4. 客户端同理,可以将客户端循环发送的代码写入到事件驱动回调函数里面

   这样客户端在 connect 服务器端之后,就注册一个读取的事件,为客户端的socket注册事件,一旦这个客户端socket对象上

   有数据存在的时候就会触发事件自动处理

5.采用了事件驱动之后,服务器端就可以接收多个客户端的请求了

   因此客户端可以采用多线程模式来请求服务器端,python中对于多线程的支持是threading 模块

  通过threading 模块的 Thread类来描述线程

   threading.Thread(target = 多线程运行的方法,args=(该方法入参,)).start()

6.因为要采用异步非阻塞,因此 socket要 使用 socket.setblocking(False) 设置 socket是非阻塞

 

selectors模块

selectors 相当于一个事件注册中心,程序只需将 socket 的所有事件注册给 selectors管理

当 selectors 检测到 socket 中的特定事件之后,程序就调用相应的监听方法进行处理

selectors 支持两种事件:

  1. selectors.EVENT_READ : 当socket 有数据可读时触发该事件 , 客户端连接也会触发
  2. selectors.EVENT_WRITE: 当 socket 写数据时候触发

注册的时候 的步骤:

1.通过 selectors模块的DefaultSelector 类创建一个 selector对象 例如 sel

2.设置 socket为非阻塞的 sock.setblocking(False)

3.sel.register(socket,selectors的事件,事件函数)

4.取消注册 sel.unregister(socket)

5.回调函数获取:

     事件的获取可以采用 Selector对象的 select() 方法监测事件

     例如 :

	## 获取当前 selectots 的事件
	events = sel.select()
	for key , mask in events:
		callback = key.data
		callback(key.fileobj,mask)

实践

例如下面例子:

服务器程序可以接收多个客户端的socket程序的

一旦关闭其中某个客户端,那么只会关闭对应的socket,服务端仍然可以运行的

这样可以模拟一个简单的聊天程序

selector-server.py


import selectors , socket

#创建一个 selector对象

sel = selectors.DefaultSelector()

# 存储多个客户端的 socket
socket_list = []

# 创建一个 读取 数据的 监听函数

def read(skt , mask):
	print('服务端read()调用...')
	try:
		data = skt.recv(1024)
		if data:
			for s in socket_list:
				s.send(data)  #类似这个发布订阅模式
		else:
			print('关闭',skt)
			sel.unregister(skt)
			skt.close()
			socket_list.remove(skt)
	except:
		print('关闭',skt)
		sel.unregister(skt)
		skt.close()
		socket_list.remove(skt)

# 创建一个负责连接的监听函数

def accept_customer(sock,mask):
	print('服务端accept()调用...')
	conn,addr = sock.accept()
	socket_list.append(conn)
	conn.setblocking(False)
	## 注册读事件 read , 准备读取数据
	sel.register(conn,selectors.EVENT_READ,read)

# 创建服务器ip地址和端口
sock = socket.socket()
sock.bind(('127.0.0.1',30000))
sock.listen()
print('server is starting...')

#设置当前服务端的 socket为非阻塞的
sock.setblocking(False)
## 注册 READ事件,该事件会触发accept_customer函数,可以调用 服务端socket的 accept方法接收客户端 
sel.register(sock,selectors.EVENT_READ,accept_customer)

while True:
	events = sel.select() ##第一次会收到上面注册的 READ 事件并调用  accept_customer
	for key,mask in events:
		callback = key.data
		print('当前请求的回调函数  callback:',callback)
		callback(key.fileobj,mask)

		

 

selector-client.py 


import selectors , socket , threading

sel = selectors.DefaultSelector()

#创建读取的监听函数

def read(conn , mask):
	print('客户端 read()方法调用....')
	data = conn.recv(1024)
	if data:
		print(data.decode('utf-8'))
	else:
		print('closing',conn)
		sel.unregister(conn)
		conn.close()

s = socket.socket()
print('客户端',s,'正在连接服务器...')
s.connect(('127.0.0.1',30000))
s.setblocking(False)
sel.register(s,selectors.EVENT_READ,read)

def key_board_input(s):
	while True:
		line = input('请输入:')
		if line is None or line == 'exit':
			break
		s.send(line.encode('utf-8'))

##采用线程不断读取用户的键盘输入数据--每次运行一个客户端就启动一个线程
threading.Thread(target = key_board_input,args=(s,)).start()

while True:
	## 获取当前 selectots 的事件
	events = sel.select()
	for key , mask in events:
		callback = key.data
		callback(key.fileobj,mask)

 

实验的时候,打开 3 个 cmd 窗口

第一个窗口 运行 python selector-server.py

第二个和第三个窗口都运行  python  selector-client.py

由于我们是一个ip打开的两个客户端窗口,因此获取到的客户端socket是一个

那么其中一个客户端窗口发送数据的时候,另外一个客户端窗口也是可以看到的

如果我们在多台机器上运行客户端程序,就是一个聊天室的雏形,此时一个客户端的socket关闭不会影响其他客户端socket的

 

 

基于UDP 协议的 socket

UDP: user datagram protocol 用户数据协议 , 是网络传输层协议 , 常应用于 网络游戏,视频会议等。 这是一种非面向连接的协议 , 通信前不必和对方建立连接,不管对方状态就直接发送数据

至于对方是否可以收到这些数据udp协议无法控制

相比tcp ,udp是一种不可靠的协议,它在两端也建立这个 socket , 但是这两个 socket 之间没有虚拟链路,他们只是发送和接收数据报的对象 。udp 如同寄信,只要知道目标地址就可以发送数据了.

和tcp协议一样 , udp也是建立在 ip 协议基础上,但是ip 协属于 网络层协议 ,而tcp 和 udp 属于传输层协议的

udp是面向非连接的协议,因此通信效率比较高。但是不可靠,udp传输数据大小限制在 64kB以下

 

 

 

python 同样使用 socket 来支持 udp

前面创建socket的时候可以选择参数的,如果是 SOCK_DGRAM 那么表示该socket 就是一个udp协议的 socket

 

udp协议的socket 使用

由于 udp协议类型的 socket 没有建立连接,因此接收数据和发送数据不能得到对方的socket的

因此只能使用 如下两个方法进行:

  1. socket.sendto(bytes,adress) : 将 bytes 数据发送到 address 地址
  2. socket.recvfrom(bufsize,[,falgs]) : 接收数据

udp协议的scoket 发送数据的时候必须选择这个 sendto 方法进行 , 由这个 address指定目标

这个目的地址会被附加在所发送的数据上。

使用 udp 协议接收数据的时候,既可以使用普通的 recv 方法,也可以使用 recvfrom() 方法

如果程序需要得到数据报的来源,那么必须使用 recvfrom() 方法

 

udp之中的 服务端和客户端

使用udp进行网络编程的时候,没有明显的服务端和客户端,因为双方都要建立一个 socket 用来接收或发送数据。 实际应用中,通常具有固定 ip 和端口的 socket 对象所在程序被称为服务器

这样其他的 socket 才知道固定的ip和端口 作为 address 来给它发送数据

作为服务端的socket 仍然是需要 bind 地址的

但是作为客户端的 socket 是不用进行 connect 的

如果服务端socke想给 这些客户端socket发送数据的话,那么必须获取数据报的来源信息

使用 recvfrom() 可以获取数据报来源的地址的。知道了地址就可以发送数据了

一般来说,服务端的 ip 和 端口是固定的

 

UDP 的程序例子

使用udp时候主要注意 这个发送数据时候必须使用 明确的目标地址

而且创建的 socket 类型必须是 socket.SOCK_DGERAM 类型的

至于接收数据可以使用 recv或者 recvfrom 都行,因为数据已经发送到了目标地址上的 socket了

例如下面的例子中,

udp-socket_server.py 充当了服务端,接收客户端的数据,并且也发送数据给客户端

udp-socket-client.py 充当了客户端,给服务端发送数据,并且接收服务端发送数据

 

他们之间的交互是没有阻塞的,可以直接访问

udp-socket_server.py


import socket

PORT = 30000
#数据大小为 4KB
DATA_LEN = 4096

lan = ['java','c','nodejs','python']

sock = socket.socket(type=socket.SOCK_DGRAM)
sock.bind(('127.0.0.1',PORT))
print('server is started....')

for i in range(1000):
	data,addr = sock.recvfrom(DATA_LEN)
	print('receive data:',data.decode('utf-8'))
	send_data = lan[i%4]
	sock.sendto(send_data.encode('utf-8'),addr)

sock.close()
	

 

udp-socket-client.py


import socket

PORT=30000
DATA_LEN = 4096

DEST_IP = '127.0.0.1'

sock = socket.socket(type=socket.SOCK_DGRAM)

while True:
	line = input('please input:')
	if line is None or line =='exit':
		break
	data = line.encode('utf-8')
	sock.sendto(data,(DEST_IP,PORT))
	print('reveive data from :')
	data = sock.recv(DATA_LEN)
	print(data.decode('utf-8'))

sock.close()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值