Python之路--Python基础9--Socket编程

1、socket介绍

  Socket是应用层与TCP/IP协议族通讯的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来讲,一组简单的接口就是所有,让Socket去组织数据,以符合指定的协议。因此,咱们无需深刻理解tcp/udp协议,socket已经为咱们封装好了,咱们只须要遵循socket的规定去编程,写出的程序天然就是遵循tcp/udp标准的。linux

 

2、套接字工做流程

   一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就创建起了链接,就能够讲话了。等交流结束,挂断电话结束这次交谈。 生活中的场景就解释了这工做原理。算法

 

  先从服务器端提及。服务器端先初始化Socket,而后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端链接。在这时若是有个客户端初始化一个Socket,而后链接服务器(connect),若是链接成功,这时客户端与服务器端的链接就创建了。客户端发送数据请求,服务器端接收请求并处理请求,而后把回应数据发送给客户端,客户端读取数据,最后关闭链接,一次交互结束。shell

 

3、Socket模块函数的用法

服务端套接字函数:编程

  s.bind()绑定(主机,端口号)到套接字
  s.listen()开始TCP监听
  s.accept()被动接受TCP客户的链接,(阻塞式)等待链接的到来

客户端套接字函数
  s.connect()主动初始化TCP服务器链接
  s.connect_ex()connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数
  s.recv() 接收TCP数据
  s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
  s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
  s.recvfrom() 接收UDP数据
  s.sendto() 发送UDP数据
  s.getpeername() 链接到当前套接字的远端的地址
  s.getsockname() 当前套接字的地址
  s.getsockopt() 返回指定套接字的参数
  s.setsockopt() 设置指定套接字的参数
  s.close() 关闭套接字

面向锁的套接字方法
  s.setblocking() 设置套接字的阻塞与非阻塞模式
  s.settimeout() 设置阻塞套接字操做的超时时间
  s.gettimeout() 获得阻塞套接字操做的超时时间

面向文件的套接字的函数
  s.fileno() 套接字的文件描述符
  s.makefile() 建立一个与该套接字相关的文件json

 

4、基于TCP的套接字

tcp是基于连接的,必须先启动服务端,而后再启动客户端去连接服务端。
windows

#TCP服务端
 ss = socket()  #建立服务器套接字
ss.bind()      #把地址绑定到套接字
ss.listen()    #监听连接
inf_loop:      #服务器无限循环
    cs = ss.accept()   #接受客户端连接
    comm_loop:         #通信循环
        cs.recv()/cs.send() #对话(接收与发送)
    cs.close()    #关闭客户端套接字
ss.close()        #关闭服务器套接字(可选)
#TCP客户端
 cs = socket()     # 建立客户套接字
cs.connect()      # 尝试链接服务器
comm_loop:        # 通信循环
    cs.send()/cs.recv()    # 对话(发送/接收)
cs.close()                 # 关闭客户套接字

 

栗子:socket通讯流程与打电话流程相似,咱们就以打电话为例来实现一个low版的套接字通讯:设计模式

#服务端

#_*_coding:utf-8_*_
__author__ = 'YL'

import socket
ip_port
=('127.0.0.1',9000) #电话卡 BUFSIZE=1024 #收发消息的尺寸
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 s.bind(ip_port) #手机插卡 s.listen(5) #手机待机 conn,addr=s.accept() #手机接电话 # print(conn) # print(addr) print('接到来自%s的电话' %addr[0]) msg=conn.recv(BUFSIZE) #听消息,听话 print(msg,type(msg)) conn.send(msg.upper()) #发消息,说话 conn.close() #挂电话 s.close() #手机关机
#客户端

#_*_coding:utf-8_*_
__author__ = 'YL'

import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect_ex(ip_port) #拨电话
 s.send('LHF nb'.encode('utf-8'))                #发消息,说话(只能发送字节类型)
 feedback=s.recv(BUFSIZE)                        #收消息,听话
print(feedback.decode('utf-8')) s.close() #挂电话

 

加上连接循环与通讯循环(持续通话)缓存

#服务端
import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   #买手机
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #就是它,在bind前加
phone.bind(("127.0.0.1", 8080))                             #插入手机卡
 phone.listen(5)                                             #开机

while True:      #连接循环
    conn, addr = phone.accept()                             #接电话
    print("client :", addr) while True:  #通信循环
        try: data = conn.recv(1024)                          #收消息
            if not data: break             #针对linux,客户端断开连接的异常处理
            print("from client msg: %s" % data) conn.send(data.upper()) #发消息
        except Exception: break conn.close() #挂电话
phone.close()                                               #关机
#客户端

import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("127.0.0.1", 8080)) #拨通电话

while True: msg = input(">>>: ") client.send(msg.encode("utf-8")) data = client.recv(1024) print(data) client.close()

问题:有的同窗在重启服务端时可能会遇到如下错误服务器

解决:多线程

phone=socket(AF_INET,SOCK_STREAM) phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
phone.bind(('127.0.0.1',8080))

 

5、基于UDP的套接字

udp是无连接的,先启动哪一端都不会报错。

#UDP服务端
 ss = socket()   #建立一个服务器的套接字
ss.bind()       #绑定服务器套接字
inf_loop:       #服务器无限循环
    cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
ss.close()                         # 关闭服务器套接字
#UDP客户端
 cs = socket()   # 建立客户套接字
comm_loop:      # 通信循环
    cs.sendto()/cs.recvfrom()   # 对话(发送/接收)
cs.close()                      # 关闭客户套接字

 

栗子:udp套接字简单示例

#UDP服务端
#
_*_coding:utf-8_*_
import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) udp_server_client.bind(ip_port) while True: msg,addr=udp_server_client.recvfrom(BUFSIZE) print(msg,addr) udp_server_client.sendto(msg.upper(),addr)
#UDP客户端

#_*_coding:utf-8_*_
__author__ = 'YL'

import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) while True: msg=input('>>: ').strip() if not msg:continue udp_server_client.sendto(msg.encode('utf-8'),ip_port) back_msg,addr=udp_server_client.recvfrom(BUFSIZE) print(back_msg.decode('utf-8'),addr)

 

栗子2:qq聊天(因为udp无链接,因此能够同时多个客户端去跟服务端通讯)

bug:消息都是服务端接收到,而且服务端进行回复的,至关于服务端一我的与众客户端在聊天

#QQ服务端

#_*_coding:utf-8_*_
__author__ = 'YL'
import socket ip_port=('127.0.0.1',8081) udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #买手机
udp_server_sock.bind(ip_port) while True: qq_msg,addr=udp_server_sock.recvfrom(1024) print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8'))) back_msg=input('回复消息: ').strip() udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
#QQ客户端1,2,3

#_*_coding:utf-8_*_
__author__ = 'YL'
import socket BUFSIZE=1024 udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) qq_name_dic={ 'alex':('127.0.0.1',8081), '二':('127.0.0.1',8081), '一棵树':('127.0.0.1',8081), '武大郎':('127.0.0.1',8081), } while True: qq_name=input('请选择聊天对象: ').strip() while True: msg=input('请输入消息,回车发送: ').strip() if msg == 'quit':break
        if not msg or not qq_name or qq_name not in qq_name_dic:continue udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name]) back_msg,addr=udp_client_socket.recvfrom(BUFSIZE) print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8'))) udp_client_socket.close()

 

栗子3:时间服务器

#ntp服务端

#_*_coding:utf-8_*_
__author__ = 'YL'

from socket import *
from time import strftime ip_port=('127.0.0.1',9000) bufsize=1024 tcp_server=socket(AF_INET,SOCK_DGRAM) tcp_server.bind(ip_port) while True: msg,addr=tcp_server.recvfrom(bufsize) print('===>',msg) if not msg: time_fmt='%Y-%m-%d %X'
    else: time_fmt=msg.decode('utf-8') back_msg=strftime(time_fmt) tcp_server.sendto(back_msg.encode('utf-8'),addr) tcp_server.close()
#ntp客户端

#_*_coding:utf-8_*_
__author__ = 'YL'
from socket import * ip_port=('127.0.0.1',9000) bufsize=1024 tcp_client=socket(AF_INET,SOCK_DGRAM) while True: msg=input('请输入时间格式(例%Y %m %d)>>: ').strip() tcp_client.sendto(msg.encode('utf-8'),ip_port) data=tcp_client.recv(bufsize) print(data.decode('utf-8')) tcp_client.close()

 输出:

 

 

6、粘包

一、粘包是啥

  只有TCP有粘包现象,UDP永远不会粘包

  发送端能够是一K一K地发送数据,而接收端的应用程序能够两K两K地提走数据,固然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个总体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,所以TCP协议是面向流的协议,这也是容易出现粘包问题的缘由。

  而UDP是面向消息的协议,每一个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不一样的。怎样定义消息呢?能够认为对方一次性write/send的数据为一个消息,须要明白的是当对方send一条信息的时候,不管底层怎样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈如今内核缓冲区。

  例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束。

  所谓粘包问题主要仍是由于接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所形成的。(玩过单片机串口通讯的都深有感触)

  此外,发送方引发的粘包是由TCP协议自己形成的,TCP为提升传输效率,发送方每每要收集到足够多的数据后才发送一个TCP段。若连续几回须要send的数据都不多,一般TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

  

  TCP(transport control protocol,传输控制协议)是面向链接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,所以,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将屡次间隔较小且数据量小的数据,合并成一个大的数据块,而后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通讯是无消息保护边界的。

  UDP(user datagram protocol,用户数据报协议)是无链接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 因为UDP支持的是一对多的模式,因此接收端的skbuff(套接字缓冲区)采用了链式结构来记录每个到达的UDP包,在每一个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来讲,就容易进行区分处理了。 即面向消息的通讯是有消息保护边界的。

  tcp是基于数据流的,因而收发的消息不能为空,这就须要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即使是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头。

 

  udp的recvfrom是阻塞的,一个recvfrom(x)必须对惟一一个sendinto(y),收完了x个字节的数据就算完成,如果y>x数据就丢失,这意味着udp根本不会粘包,可是会丢数据,不可靠。

  tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端老是在收到ack时才会清除缓冲区内容。数据是可靠的,可是会粘包。

 

二、两种状况下会发生粘包

a、发送端须要等缓冲区满才发送出去,形成粘包(发送数据时间间隔很短,数据块很小,会合到一块儿,产生粘包)

#服务端

#_*_coding:utf-8_*_
__author__ = 'YL'
from socket import * ip_port=('127.0.0.1',8080) tcp_socket_server=socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5) conn,addr=tcp_socket_server.accept() data1=conn.recv(10) data2=conn.recv(10) print('----->',data1.decode('utf-8')) print('----->',data2.decode('utf-8')) conn.close()
#客户端

#_*_coding:utf-8_*_
__author__ = 'YL'
import socket BUFSIZE=1024 ip_port=('127.0.0.1',8080) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(ip_port) s.send('hello'.encode('utf-8')) s.send('feng'.encode('utf-8'))

输出:

 

 

b、接收方不及时接收缓冲区的包,形成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候仍是从缓冲区拿上次遗留的数据,产生粘包) 

#_*_coding:utf-8_*_
__author__ = 'YL'
from socket import * ip_port=('127.0.0.1',8080) tcp_socket_server=socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5) conn,addr=tcp_socket_server.accept() data1=conn.recv(2) #一次没有收完整
data2=conn.recv(10)#下次收的时候,会先取旧的数据,而后取新的

print('----->',data1.decode('utf-8')) print('----->',data2.decode('utf-8')) conn.close()
#_*_coding:utf-8_*_
__author__ = 'YL'
import socket BUFSIZE=1024 ip_port=('127.0.0.1',8080) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(ip_port) s.send('hello feng'.encode('utf-8'))

输出:

 

三、解决办法

  为字节流加上自定义固定长度报头,报头中包含字节流长度,而后一次send到对端,对端在接收时,先从缓存中取出定长的报头,而后再取真实数据

  struct模块:该模块能够把一个类型,如数字,转成固定长度的bytes

  咱们能够把报头作成字典,字典里包含将要发送的真实数据的详细信息,而后json序列化,而后用struck将序列化后的数据长度打包成4个字节(4个本身足够用了)

  发送时:

  先发报头长度

  再编码报头内容而后发送

  最后发真实内容

 

  接收时:

  先收报头长度,用struct取出来

  根据取出的长度收取报头内容,而后解码,反序列化

  从反序列化的结果中取出待取数据的详细信息,而后去取真实的数据内容

#服务端

import socket import subprocess import struct import json phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    # 买手机
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 就是它,在bind前加
phone.bind(("127.0.0.1", 8080))                              # 插入手机卡
 phone.listen(5)  # 开机

while True:      # 连接循环
    conn, addr = phone.accept()    # 接电话
    print("client :", addr) while True:                    # 通信循环
        try: cmd = conn.recv(1024)  # 收消息
            if not cmd: break              # 针对linux,客户端断开连接的异常处理
            print("from client msg: %s" % cmd) res = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err = res.stderr.read() if err: back_msg = err else: back_msg = res.stdout.read() # 第一阶段:制做报头
            head_dic = { "data_size": len(back_msg) } head_json = json.dumps(head_dic) head_bytes = head_json.encode("utf-8") # 第二阶段:发送包头长度
            conn.send(struct.pack("i", len(head_bytes))) # 第三阶段:发报头
 conn.send(head_bytes) # 第四阶段:发送真实数据
 conn.send(back_msg) except Exception: break conn.close() # 挂电话
phone.close()     # 关机
#客户端

import socket import struct import json client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("127.0.0.1", 8080))  # 拨通电话

while True: cmd = input(">>>: ").strip() if not cmd: continue client.send(cmd.encode("utf-8")) # 收报头的长度
    head = client.recv(4) head_size = struct.unpack("i", head)[0] # 根据报头长度接收报头
    head_bytes = client.recv(head_size) head_json = head_bytes.decode("GBK") head_dic = json.loads(head_json) data_size = head_dic["data_size"]  # 取出真实数据长度

    # 接收真实的数据
    recv_size = 0 recv_bytes = b""
    while recv_size < data_size: res = client.recv(1024) recv_bytes += res recv_size += len(res) print(res.decode("GBK"))

注意注意注意:

res=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)

的结果的编码是以当前所在的系统为准的,若是是windows,那么res.stdout.read()读出的就是GBK编码的,在接收端需要用GBK解码

且只能从管道里读一次结果

 

7、FTP上传下载

#服务端

import socket import struct import json import subprocess import os class MYTCPServer: address_family = socket.AF_INET socket_type = socket.SOCK_STREAM allow_reuse_address = False max_packet_size = 8192 coding='utf-8' request_queue_size = 5 server_dir='file_upload'

    def __init__(self, server_address, bind_and_activate=True): """Constructor. May be extended, do not override.""" self.server_address=server_address self.socket = socket.socket(self.address_family, self.socket_type) if bind_and_activate: try: self.server_bind() self.server_activate() except: self.server_close() raise

    def server_bind(self): """Called by constructor to bind the socket. """
        if self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) self.server_address = self.socket.getsockname() def server_activate(self): """Called by constructor to activate the server. """ self.socket.listen(self.request_queue_size) def server_close(self): """Called to clean-up the server. """ self.socket.close() def get_request(self): """Get the request and client address from the socket. """
        return self.socket.accept() def close_request(self, request): """Called to clean up an individual request.""" request.close() def run(self): while True: self.conn,self.client_addr=self.get_request() print('from client ',self.client_addr) while True: try: head_struct = self.conn.recv(4) if not head_struct:break head_len = struct.unpack('i', head_struct)[0] head_json = self.conn.recv(head_len).decode(self.coding) head_dic = json.loads(head_json) print(head_dic) #head_dic={'cmd':'put','filename':'a.txt','filesize':123123}
                    cmd=head_dic['cmd'] if hasattr(self,cmd): func=getattr(self,cmd) func(head_dic) except Exception: break

    def put(self,args): file_path=os.path.normpath(os.path.join( self.server_dir, args['filename'] )) filesize=args['filesize'] recv_size=0 print('----->',file_path) with open(file_path,'wb') as f: while recv_size < filesize: recv_data=self.conn.recv(self.max_packet_size) f.write(recv_data) recv_size+=len(recv_data) print('recvsize:%s filesize:%s' %(recv_size,filesize)) tcpserver1=MYTCPServer(('127.0.0.1',8080)) tcpserver1.run()
#客户端

import
socket import struct import json import os class MYTCPClient: address_family = socket.AF_INET socket_type = socket.SOCK_STREAM allow_reuse_address = False max_packet_size = 8192 coding='utf-8' request_queue_size = 5 def __init__(self, server_address, connect=True): self.server_address=server_address self.socket = socket.socket(self.address_family, self.socket_type) if connect: try: self.client_connect() except: self.client_close() raise def client_connect(self): self.socket.connect(self.server_address) def client_close(self): self.socket.close() def run(self): while True: inp=input(">>: ").strip() if not inp:continue l=inp.split() cmd=l[0] if hasattr(self,cmd): func=getattr(self,cmd) func(l) def put(self,args): cmd=args[0] filename=args[1] if not os.path.isfile(filename): print('file:%s is not exists' %filename) return else: filesize=os.path.getsize(filename) head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize} print(head_dic) head_json=json.dumps(head_dic) head_json_bytes=bytes(head_json,encoding=self.coding) head_struct=struct.pack('i',len(head_json_bytes)) self.socket.send(head_struct) self.socket.send(head_json_bytes) send_size=0 with open(filename,'rb') as f: for line in f: self.socket.send(line) send_size+=len(line) print(send_size) else: print('upload successful') client=MYTCPClient(('127.0.0.1',8080)) client.run()

 

8、认证客户端的连接的合法性

  若是你想在分布式系统中实现一个简单的客户端连接认证功能,又不像SSL那么复杂,那么利用hmac+加盐的方式来实现

#服务端

#_*_coding:utf-8_*_
__author__ = 'YL'
from socket import *
import hmac,os secret_key=b'linhaifeng bang bang bang'
def conn_auth(conn): ''' 认证客户端连接 :param conn: :return: '''
    print('开始验证新连接的合法性') msg=os.urandom(32) conn.sendall(msg) h=hmac.new(secret_key,msg) digest=h.digest() respone=conn.recv(len(digest)) return hmac.compare_digest(respone,digest) def data_handler(conn,bufsize=1024): if not conn_auth(conn): print('该连接不合法,关闭') conn.close() return
    print('连接合法,开始通讯') while True: data=conn.recv(bufsize) if not data:break conn.sendall(data.upper()) def server_handler(ip_port,bufsize,backlog=5): ''' 只处理连接 :param ip_port: :return: ''' tcp_socket_server=socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(backlog) while True: conn,addr=tcp_socket_server.accept() print('新链接[%s:%s]' %(addr[0],addr[1])) data_handler(conn,bufsize) if __name__ == '__main__': ip_port=('127.0.0.1',9999) bufsize=1024 server_handler(ip_port,bufsize)
#合法客户端

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
from socket import *
import hmac,os secret_key=b'linhaifeng bang bang bang'
def conn_auth(conn): ''' 验证客户端到服务器的连接 :param conn: :return: ''' msg=conn.recv(32) h=hmac.new(secret_key,msg) digest=h.digest() conn.sendall(digest) def client_handler(ip_port,bufsize=1024): tcp_socket_client=socket(AF_INET,SOCK_STREAM) tcp_socket_client.connect(ip_port) conn_auth(tcp_socket_client) while True: data=input('>>: ').strip() if not data:continue
        if data == 'quit':break tcp_socket_client.sendall(data.encode('utf-8')) respone=tcp_socket_client.recv(bufsize) print(respone.decode('utf-8')) tcp_socket_client.close() if __name__ == '__main__': ip_port=('127.0.0.1',9999) bufsize=1024 client_handler(ip_port,bufsize)

服务端输出:

 

#非法客户端,不知道加密方式

#_*_coding:utf-8_*_
__author__ = 'YL'
from socket import *

def client_handler(ip_port,bufsize=1024): tcp_socket_client=socket(AF_INET,SOCK_STREAM) tcp_socket_client.connect(ip_port) while True: data=input('>>: ').strip() if not data:continue
        if data == 'quit':break tcp_socket_client.sendall(data.encode('utf-8')) respone=tcp_socket_client.recv(bufsize) print(respone.decode('utf-8')) tcp_socket_client.close() if __name__ == '__main__': ip_port=('127.0.0.1',9999) bufsize=1024 client_handler(ip_port,bufsize)

服务端输出:

不合法客户端输出:

 

#非法客户端,不知道secret_key

#_*_coding:utf-8_*_
__author__ = 'YL'
from socket import *
import hmac,os secret_key=b'linhaifeng bang bang bang1111'
def conn_auth(conn): ''' 验证客户端到服务器的连接 :param conn: :return: ''' msg=conn.recv(32) h=hmac.new(secret_key,msg) digest=h.digest() conn.sendall(digest) def client_handler(ip_port,bufsize=1024): tcp_socket_client=socket(AF_INET,SOCK_STREAM) tcp_socket_client.connect(ip_port) conn_auth(tcp_socket_client) while True: data=input('>>: ').strip() if not data:continue
        if data == 'quit':break tcp_socket_client.sendall(data.encode('utf-8')) respone=tcp_socket_client.recv(bufsize) print(respone.decode('utf-8')) tcp_socket_client.close() if __name__ == '__main__': ip_port=('127.0.0.1',9999) bufsize=1024 client_handler(ip_port,bufsize)

服务端输出:

不合法客户端输出:

 

9、socketserver实现并发

  SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每一个客户端请求链接到服务器时,Socket服务端都会在服务器是建立一个“线程”或者“进程” 专门负责处理当前客户端的全部请求。

栗子:

#服务端

import socketserver class FtpServer(socketserver.BaseRequestHandler): def handle(self): print(self.request)     #conn
        print(self.client_address) while True: data = self.request.recv(1024) self.request.send(data.upper()) if __name__ == "__main__": s = socketserver.ThreadingTCPServer(("127.0.0.1", 8080), FtpServer) s.serve_forever() #连接循环有了
#客户端1/2/3

from socket import * client = socket(AF_INET, SOCK_STREAM) client.connect(("127.0.0.1", 8080)) while True: msg = input(">>>: ") client.send(msg.encode("utf-8")) data = client.recv(1024) print(data)
相关文章
相关标签/搜索