socket简介
1.本地的进程间通信(IPC)有很多种方式,例如
队列
同步(互斥锁、条件变量等)
以上通信方式都是在一台机器上不同进程之间的通信方式,那么问题来了网络中进程之间如何通信?
2. 网络中进程之间如何通信
首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。
这样利用ip地址,协议,端口就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互
3. 什么是socket
socket(简称 套接字) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的例如我们每天浏览网页、QQ 聊天、收发 email 等等
4. 创建socket
在 Python 中 使用socket 模块的函数 socket 就可以完成:
socket.socket(AddressFamily, Type)
说明:
函数 socket.socket 创建一个 socket,返回该 socket 的描述符,该函数带有两个参数:
Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
创建一个tcp socket(tcp套接字)
import socket
#创建套接字对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket Created'
创建一个udp socket(udp套接字)
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print 'Socket Created'
tcp服务器
如同上面的电话机过程一样,在程序中,如果想要完成一个tcp服务器的功能,需要的流程如下:
socket创建一个套接字
bind绑定ip和port
listen使套接字变为可以被动链接
accept等待客户端的链接
recv/send接收发送数据
一个很简单的tcp服务器如下:
#coding=utf-8
from socket import *
# 创建socket
tcpSerSocket = socket(AF_INET, SOCK_STREAM)
# 绑定本地信息
address = ('', 7788)
tcpSerSocket.bind(address)
# 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的链接了
tcpSerSocket.listen(5)
# 如果有新的客户端来链接服务器,那么就产生一个新的套接字专门为这个客户端服务器
# newSocket用来为这个客户端服务
# tcpSerSocket就可以省下来专门等待其他新客户端的链接
newSocket, clientAddr = tcpSerSocket.accept()
# 接收对方发送过来的数据,最大接收1024个字节
recvData = newSocket.recv(1024)
print '接收到的数据为:',recvData
# 发送一些数据到客户端
newSocket.send("thank you !")
# 关闭为这个客户端服务的套接字,只要关闭了,就意味着为不能再为这个客户端服务了,如果还需要服务,只能再次重新连接
newSocket.close()
# 关闭监听套接字,只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接
tcpSerSocket.close()
tcp客户端
tcp客户端,并不是像之前一个段子:一个顾客去饭馆吃饭,这个顾客要点菜,就问服务员咱们饭店有客户端么,然后这个服务员非常客气的说道:先生 我们饭店不用客户端,我们直接送到您的餐桌上如果,不学习网络的知识是不是 说不定也会发生那样的笑话 ,哈哈。所谓的服务器端:就是提供服务的一方,而客户端,就是需要被服务的一方
tcp客户端构建流程
tcp的客户端要比服务器端简单很多,如果说服务器端是需要自己买手机、查手机卡、设置铃声、等待别人打电话流程的话,那么客户端就只需要找一个电话亭,拿起电话拨打即可,流程要少很多
示例代码:
#coding=utf-8
from socket import *
# 创建socket
tcpClientSocket = socket(AF_INET, SOCK_STREAM)
# 链接服务器
serAddr = ('192.168.1.102', 7788)
tcpClientSocket.connect(serAddr)
# 提示用户输入数据
sendData = raw_input("请输入要发送的数据:")
tcpClientSocket.send(sendData)
# 接收对方发送过来的数据,最大接收1024个字节
recvData = tcpClientSocket.recv(1024)
print '接收到的数据为:',recvData
# 关闭套接字
tcpClientSocket.close()
应用:模拟QQ聊天
客户端参考代码
#coding=utf-8
from socket import *
# 创建socket
tcpClientSocket = socket(AF_INET, SOCK_STREAM)
# 链接服务器
serAddr = ('192.168.1.102', 7788)
tcpClientSocket.connect(serAddr)
while True:
# 提示用户输入数据
sendData = raw_input("send:")
if len(sendData)>0:
tcpClientSocket.send(sendData)
else:
break
# 接收对方发送过来的数据,最大接收1024个字节
recvData = tcpClientSocket.recv(1024)
print 'recv:',recvData
# 关闭套接字
tcpClientSocket.close()
服务器端参考代码
#coding=utf-8
from socket import *
# 创建socket
tcpSerSocket = socket(AF_INET, SOCK_STREAM)
# 绑定本地信息
address = ('', 7788)
tcpSerSocket.bind(address)
# 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的链接了
tcpSerSocket.listen(5)
while True:
# 如果有新的客户端来链接服务器,那么就产生一个新的的套接字专门为这个客户端服务器
# newSocket用来为这个客户端服务,clientAddr是客户端的ip地址。
# 等待其他新客户端的链接是通过死循环实现。如果这里没有新的客户端连接则会阻塞线程。
newSocket, clientAddr = tcpSerSocket.accept()
while True:
# 接收对方发送过来的数据,最大接收1024个字节。
recvData = newSocket.recv(1024)
# 如果接收的数据的长度为0,则意味着客户端关闭了链接
if len(recvData)>0:
print 'recv:',recvData
else:
break
# 发送一些数据到客户端
sendData = raw_input("send:")
newSocket.send(sendData)
# 关闭为这个客户端服务的套接字,只要关闭了,就意味着为不能再为这个客户端服务了,如果还需要服务,只能再次重新连接
newSocket.close()
# 关闭监听套接字,只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接
tcpSerSocket.close()
案例:人脸识别服务端与客户端
服务器端参考代码
# coding=utf-8
"""输入一张图片经过检测、特征提取环节然后匹配输出标签和距离"""
import argparse
import sys,os
import time
import cv2
import face
from scipy import misc
from socket import *
import threading
from Queue import Queue
mess_dic = {}
def get_message(q1,q2,tcpSerSocket):
#HOST ='192.168.1.189'
#PORT = 8080
while True:
#为每个新连接的客户创建新的socket,如果没有新的客户端连接就会阻塞线程,通过死循环实现。
print("~~~~~~~~~~~~~等待客户端链接~~~~~~~~~~~~~~")
newSocket, clientAddr = tcpSerSocket.accept()
while True:
recvData = newSocket.recv(1024)
print("~~~~~~~~~~~~~客户端链接成功~~~~~~~~~~~~~~")
if recvData > 0:
print("~~~~~~~~~~~~~已收到客户端数据~~~~~~~~~~")
#收到错误数据的异常处理
try :
image = misc.imread(recvData)
except :
print "提示:数据发生异常!"
break
else:
q1.put(image)
print("~~~~~~~~~~~~~数据已经加入队列q1~~~~~~~~~~")
else:
print("~~~~~~~~~~~~~未收到客户端数据~~~~~~~~~~")
if q2.empty():
time.sleep(1)
else:
value = q2.get()
print("~~~~~~~~~~~~~得到输出检测结果~~~~~~~~~~~~~~")
newSocket.send(str(value))
#由于异常退出while循环后需要关闭该套接字,才能重新建立显得连接
newSocket.close()
#同理,被动接听的套接字最后(关闭服务器时)也需要关闭
tcpSerSocket.close()
def main():
#创建两个队列
q1 = Queue()
q2 = Queue()
#创建socket(被动接听套接字)
tcpSerSocket = socket(AF_INET, SOCK_STREAM)
address = ('', 8080)
tcpSerSocket.bind(address)
tcpSerSocket.listen(5)
#把人脸识别模型预先载入内存
face_recognition = face.Recognition()
#接受信息线程
get_thread = threading.Thread(target=get_message,args=(q1,q2,tcpSerSocket,))
#启动线程
get_thread.start()
#主线程进行人脸识别
while True:
if q1.empty():
print "队列q1中没有数据"
time.sleep(1)
else:
value = q1.get()
print "从队列q1中取出数据"
faces = face_recognition.identify(value)
if faces:
for one_face in faces:
face_bb = one_face.bounding_box.astype(int)
cv2.rectangle(value, (int(face_bb[0]),int(face_bb[1])),(int(face_bb[2]),int(face_bb[3])),(255,0,0))
if one_face.dist < 0.5:
#cv2.putText(value, str(one_face.dist),(face_bb[0], face_bb[3]),cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255,0),thickness=1, lineType=1)
#misc.imshow(value)
mess_dic = {}
mess_dic[one_face.label] = one_face.embedding
q2.put(mess_dic)
print "检测数据已经放入队列q2"
else:
print "警告:陌生人!"
else:
print "提示:未检测到人脸!"
get_thread.join()
if __name__ == '__main__':
main()
客户端参考代码
#coding=utf-8
from socket import *
import os,time
import threading
from Queue import Queue
# 创建socket
tcpClientSocket = socket(AF_INET, SOCK_STREAM)
# 链接服务器
serAddr = ('192.168.1.162', 8080)
tcpClientSocket.connect(serAddr)
f = open("/home/ubuntu/facenet/contributed/cbir/image.txt")
#辅助线程接收来自服务器的数据
def get_message(queue):
while True:
print "等待接收服务器返回的数据"
recv_data = tcpClientSocket.recv(1024)
if len(recv_data)>0:
print "收到服务器返回的数据"
print 'recv_data:',recv_data
time.sleep(1)
tcpClientSocket.close()
##主线程发送数据
def main():
queue = Queue()
get_thread = threading.Thread(target=get_message,args=(queue,))
get_thread.start()
while True:
print ("~~~~~~~~~~~向服务器发送数据~~~~~~~~~~~~~")
image = f.readline()
image = image.strip('\n')
if len(image)>0:
tcpClientSocket.send(image)
print ("~~~~~~~~~~~~~发送数据成功~~~~~~~~~~~~~~~")
time.sleep(1)
else:
time.sleep(1)
if __name__ == '__main__':
main()