Quer escrever um software que possa se comunicar entre dois hosts diferentes? ? Quer imitar um software semelhante à loja de aplicativos? ? ?
Se você planeja desenvolver programas relacionados à rede, como software de bate-papo, você absolutamente não pode ignorar a programação de rede, e a comunicação de soquete é o foco da programação de rede.
1. Definição de Soquete
Dois programas na rede trocam dados por meio de uma conexão de comunicação bidirecional, e uma extremidade dessa conexão é chamada de soquete. (Citado da Enciclopédia Baidu) Emmmm... Para os alunos que querem entender a parte teórica, sugiro que verifiquem o conteúdo relevante em outros artigos, vamos focar nos aspectos práticos.
2. Um Exemplo Simples de Comunicação de Soquete
# -*-coding:utf-8 -*-
# Server端
# Server.py
import socket
HOST = '127.0.0.1'
PORT = 33333
ADDR = (HOST,PORT)
# AF_INET 表示连接使用ipv4地址族 SOCK_STREAM表示用流式套接字
tcpSerSock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcpSerSock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 将套接字绑定该地址
tcpSerSock.bind(ADDR)
# 参数1表示阻塞模式 0表示非阻塞模式 默认为阻塞模式
tcpSerSock.setblocking(1)
# 开始监听TCP传入连接。参数指定在拒绝连接之前,操作系统可以挂起的最大连接数量。
tcpSerSock.listen(5)
print "Waiting connect..."
# tcpCliSock 是该链接的套接字,addr表示对方的地址
tcpCliSock, addr = tcpSerSock.accept()
# 设置超时时间
tcpCliSock.settimeout(20.0)
print '...connected from', addr
# recv(param)用于接收对方发送的数据 param为缓冲区大小
data = tcpCliSock.recv(1024)
print data
tcpCliSock.sendall("here is server")
# 关闭套接字
tcpCliSock.close()
# -*- coding: utf-8 -*-
# client端
# Client.py
import socket
address = ('127.0.0.1', 33333)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.connect(address)
s.sendall("here is client")
data = s.recv(1024)
print data
s.close()
resultado da operação
resultados do lado do servidor
Resultados da execução do cliente
O método listen(x) é usado para escutar conexões TCP de entrada. O parâmetro especifica o número máximo de conexões que o sistema operacional pode suspender antes de rejeitar a conexão. Supondo que um host se conecte ao servidor, o servidor estabelecerá uma conexão com um para comunicação de soquete. Se o host b também se conectar ao servidor antes de a se desconectar do servidor, o servidor suspenderá a conexão com b e o servidor não se conectará a b até que a se desconecte do servidor. O parâmetro x do método listen é o número máximo de conexões que o servidor pode suspender.
Amigos que querem saber mais sobre os métodos do módulo socket, os métodos e parâmetros de uso detalhados, podem ler este artigo
No entanto, em aplicações práticas, o servidor geralmente precisa se conectar a vários clientes ao mesmo tempo, então o que devemos fazer? ? ? ? A solução pode ser habilitar multi-threading ou multi-processo. O multiprocessamento é mais caro do que o multithreading, mas para as configurações de servidor atuais (baseadas em Linux), esses custos são em grande parte insignificantes.
Vamos falar primeiro sobre servidores multithread hoje.
3.1 Multithreading
Multithreading refere-se à tecnologia que realiza a execução simultânea de vários threads de software ou hardware. Computadores com recursos multi-threading têm suporte de hardware para poder executar mais de um thread por vez, aumentando assim o desempenho geral do processamento.
Entendendo o multithreading com um exemplo simples
# -*- coding:utf-8 -*-
# simpleThread_1.py
import threading
import time
def process():
n = 0
while(n<5):
print str(n)+": the threading %s is running"%threading.current_thread().name
n = n + 1
time.sleep(1)
# 创建新线程 target为线程要执行的方法 name为进程的名字,name参数可以省略,其实名字基本无多大用处
t1 = threading.Thread(target=process,name="One")
t2 = threading.Thread(target=process,name="Two")
t1.start()
t2.start()
então os threads t1 e t2 poderão ser executados ao mesmo tempo
Resultados de execução de SimpleThread_1.py
Se você adicionar vários: t1.join() entre t1.start() e t2.start(), então o thread t2 não começará a ser executado até que t1 termine de ser executado
3.2 Servidor multithread
#-*- coding:utf-8 -*-
# 多线程服务器
# threads_Server.py
import socket
import select
import threading
import time
def process(tcpCliSock,addr):
print "connect from "+str(addr)
pattern_data = tcpCliSock.recv(1024)
print data
tcpCliSock.sendall("here is server")
def run():
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("127.0.0.1",33333))
server.listen(5)
while True:
r,w,e = select.select([server,],[],[],1)
# enumerate()分别列举出list r中的序号和内容
for i,server in enumerate(r):
conn,addr = server.accept()
t = threading.Thread(target=process,args=(conn,addr))
t.start()
if __name__=="__main__":
run()
É claro que, na verdade, o cenário de aplicação mais comum de servidores multithread está em conexões longas.
Pois bem, temos outro problema. O cliente pode se desconectar repentinamente da conexão, mas o servidor não a encontra, quão triste o servidor deve estar, e não sabe [escape] depois de ser enganado. O servidor multithread enfrenta a conexão de tantos clientes, então como ele sabe se o cliente ainda está conectado ao servidor? Então lançamos outro artefato: o mecanismo do pacote de pulsação
O pacote de pulsação pode enviar periodicamente um pacote de dados para a outra parte e a conexão TCP pode ser julgada pela situação de envio.
Existem muitos métodos de design para o mecanismo de pacote de pulsação, como:
- O cliente envia pacotes de pulsação para o servidor regularmente e o servidor envia um pacote para o cliente após receber o pacote de pulsação, para que ambas as partes possam saber se a outra parte está online.
- O servidor envia periodicamente um pacote de heartbeat para o cliente. Se o envio falhar, significa que o cliente foi desconectado. Caso contrário, significa que o cliente ainda está conectado. Isso pertence ao monitoramento unilateral do servidor.
Existem mais de dois esquemas de design acima, e a situação específica é analisada em detalhes! ! ! Lembre-se, lembre-se! !
Então vamos dar uma olhada em como é um servidor multithread com um pacote heartbeat~
#-*- coding:utf-8 -*-
# 多线程服务器(心跳包版)
# hreatBeat_Server.py
import socket
import select
import threading
import time
# 心跳包线程
def hreatBeat(conn):
sum = 0 # 无回应次数
while True:
time.sleep(10)
if sum<3:
try:
conn.sendall("hreatBeat")
sum=0
except socket.error:
sum = sum + 1
continue
else:
conn.close()
break
def process(tcpCliSock,addr):
print "connect from "+str(addr)
pattern_data = tcpCliSock.recv(1024)
print data
tcpCliSock.sendall("here is server")
# 创建心跳包线程
# 须记住,创建新线程时 args参数如果只有一个的话一定要加个逗号!!
thr = threading.Thread(target=hreatBeat, args=(tcpCliSock,))
thr.start()
# thr.is_alive() 用于监测进程thr是否还存在(即client是否还在连接中)
while thr.is_alive():
print "do everything you like here"
def run():
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("127.0.0.1",33333))
server.listen(5)
while True:
r,w,e = select.select([server,],[],[],1)
# enumerate()分别列举出list r中的序号和内容
for i,server in enumerate(r):
conn,addr = server.accept()
t = threading.Thread(target=process,args=(conn,addr))
t.start()
if __name__=="__main__":
run()
4. Resumo
Começar com a comunicação Socket é fácil, mas ir fundo não é fácil. Se você quer melhorar, aprenda mais e pratique mais! Acredite que o conhecimento acima é suficiente para você construir um protótipo de um servidor de sucesso. Como programador, os requisitos são os primeiros, e tudo deve ser analisado em combinação com os requisitos específicos.
Amigos que estão aprendendo programação e Python, é difícil aprender por uma pessoa, e os blogueiros também estão aqui. Aqui, um novo grupo de dedução foi criado: 1020465983, que preparou recursos de aprendizado e projetos divertidos para todos. Bem-vindos a todos para participar e comunicar.