一、Socket通信简介
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
这里推荐一篇大神的博客,这里讲socket通信讲的明明白白Socket通信原理,因为主要讲解底层原理,因此没有学过C或对底层了解不够多的看着有点吃力,我会根据大神的介绍对Python中封装的socket模块中的socket类的一些bind(),listen(),connent(),accept(),close()函数做介绍,这几个函数是我们通过python学习socket()通信的关键。
二、代码实现
服务端实现
import socket
#创建服务端的socket对象
server_socket = socket.socket()
#绑定一个ip和端口
server_socket.bind(('192.168.0.9',9091))
#服务器端一直监听是否有客户端进行连接
print('server_socket is listening 9091')
server_socket.listen()
#如果有客户端进行连接、则接受客户端的连接
clientSockt,addr = server_socket.accept() #返回客户端socket通信对象和客户端的ip
#客户端与服务端进行通信
data = clientSockt.recv(1024).decode('utf-8')
print('服务端收到客户端发来的消息:%s'%(data))
#服务端给客户端回消息
clientSockt.send(b'I am a server')
#关闭socket对象
clientSockt.close() #客户端对象
server_socket.close() #服务端对象
客户端实现
import socket
#1、创建socket通信对象
clientSocket = socket.socket()
#2、使用正确的ip和端口去链接服务器
clientSocket.connect(('192.168.0.9',9091))
#3、客户端与服务器端进行通信
# 给socket服务器发送信息
clientSocket.send(b'I am a client')
# 接收服务器的响应(服务器回复的消息)
recvData = clientSocket.recv(1024).decode('utf-8')
print('客户端收到服务器回复的消息:%s'%(recvData))
#4、关闭socket对象
clientSocket.close()
代码分析:
socket.cocket()函数用于创建socket对象其源代码如下
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,type,proto;
family:即协议域,又称协议族,常用的有AF_INET、AF_INET6、AF_LOCAL(又称AF_UNIX)、AF_ROUTE等等。协议族决定了socket()的地址类型,在通信中必须采用对应的地址,AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
type:指socket类型。常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
proto:是指定协议常用的协议有IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
注意:并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。
通常我们使用下面几种:
family:
socket.AF_INET:指定通过IPV4进行连接
socket.AF_INET6:指定通过IPV6进行通信
type :
socket.SOCK_STREAM:通过TCP进行通信
socket.SOCK_DGRAM: 通过udp进行通信
bind()
socket.bind()函数把一个地址族中的特定地址赋给socket,例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket对象
源码:
def bind(self, address): # real signature unknown; restored from __doc__
"""
bind(address)
Bind the socket to a local address. For IP sockets, the address is a
pair (host, port); the host must refer to the local host. For raw packet
sockets the address is a tuple (ifname, proto [,pkttype [,hatype]])
""
pass
listen()
listen()函数,当服务器在调用socket()、bind()之后就会调用listen()函数来监听其绑定ip和端口,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。
源码
def listen(self, backlog=None): # real signature unknown; restored from __doc__
"""
listen([backlog])
Enable a server to accept connections. If backlog is specified, it must be
at least 0 (if it is lower, it is set to 0); it specifies the number of
unaccepted connections that the system will allow before refusing new
connections. If not specified, a default reasonable value is chosen.
"""
pass
accept()
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就想TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。
三、使用UDP协议进行socket通信
代码:
服务端
import socket
serverSocket = socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM)
# socket.AF_INET:指定通过IPV4进行连接
# socket.SOCK_DGRAM: 通过udp进行通信
serverSocket.bind(('192.168.0.9',9997))
# 使用UDP协议进行socket通信,服务器不需要进行监听
while True:
data , addr = serverSocket.recvfrom(1024)
print('接收到客户端的信息:',data)
print('客户端的地址为:',addr)
recvdata = input('server >>').encode('utf-8')
serverSocket.sendto(recvdata,addr)
客户端
import socket
clientSocket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
msg = input('client >>:')
msg = msg.encode()
clientSocket.sendto(msg,('192.168.0.9',9997))
print('接收到客户端的消息为:',clientSocket.recv(1024))
四、练习:
我们在linux中使用ssh服务的时候就是用了socket通信,在这个我们简单实现这个功能(只有部分命令功能可以实现)
服务端
import socket
import os
import time
#1.如何创建socket:
# family:
# socket.AF_INET:指定通过IPV4进行连接
# socket.AF_INET6:指定通过IPV6进行通信
# type :
# socket.SOCK_STREAM:通过TCP进行通信
# socket.SOCK_DGRAM: 通过udp进行通信
with socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM) as serverScoket:
serverScoket.bind(('192.168.0.9',9999))
serverScoket.listen()
clientSocket , addr = serverScoket.accept()
with clientSocket:
while True:
time.sleep(1)
cmd = clientSocket.recv(1024).decode('utf-8')
print(cmd)
res = os.popen(cmd).read().encode('utf-8')
if res:
clientSocket.send(res)
客户端
import socket
#1、创建socket通信对象
clientSocket = socket.socket()
#2、使用正确的ip和端口去链接服务器
clientSocket.connect(('192.168.0.9',9091))
#3、客户端与服务器端进行通信
# 给socket服务器发送信息
clientSocket.send(b'I am a client')
# 接收服务器的响应(服务器回复的消息)
recvData = clientSocket.recv(1024).decode('utf-8')
print('客户端收到服务器回复的消息:%s'%(recvData))
#4、关闭socket对象
clientSocket.close()