python与plc用socket通信_Python中网络编程Socket详解 如何实现客户端和服务端通信?...

Python网络编程Socket的介绍

socket 的原意是“插座”,在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。网络套接字是计算机网络连接的端点。今天,大多数计算机之间的通信是基于Internet协议的;因此,大多数网络套接字都是Internet套接字。更准确地说,套接字是一个句柄(抽象引用),本地程序可以将其传递给网络应用程序编程接口(API)来使用连接,例如“在此套接字上发送此数据”。

例如,要通过TCP将“Hello, world!”发送到地址为1.2.3.4的主机的80端口,可以获得一个套接字,将其连接到远程主机,发送字符串,然后关闭套接字。

Python中网络编程Socket的使用

如何更好的理解socket网络编程,通过一些例子,你可能会知道更多。首先跟着鳄鱼君创建一个服务器端(server.py)和一个创建客户端(client.py),我们先来写一个简单的发送,接收试试。

server.py服务器端

import socket

#服务器端

server=socket.socket() #这里不写的话,默认 family = AF_INET,type = SOCK_STREAM

server.bind(('localhost',8899)) #绑定要监听的端口

server.listen() #监听

print('我要开始等电话了')

conn,addr=server.accept() #等电话打进来,这里会返回两个值,第一个连接的标记,第二个是对方的地址

print(conn,addr) #conn就是客户端连接过来,然后在服务器端为其生成的一个连接实例

data=conn.recv(1024) #接收客户端发过来的消息

print('recv:',data)

conn.send(data.upper()) #转换为大写返回给客户端

server.close()

通过上面服务器端的代码,我们可以大致归结出TCP通信的基本步骤:服务器首先创建一个socket对象—>服务器端socket将自己绑定到指定端口和IP地址—>服务器端socket调用listen()监听网络—>程序可以采用循环,不断调用socket的accept()方法来接受客户端的连接。

client.py客户端

import socket

#客户端

client=socket.socket() #声明socket类型,同时生成socket连接对象

client.connect(('localhost',8899))

client.send(b'Hello World') #Python3里面必须发送字节类型

data=client.recv(1024) #接收服务器端返回的消息

print('recv:',data)

client.close()

那么TCP通信客户端编程按照上面的代码,可以归纳出:客户端先创建一个socket对象—->客户端socket()调用connect()方法远程连接服务器。

上面的代码就实现客户端发送,服务器端接收的功能,很显然我们只是发送了一个字节类型的Hello World,上面也提到循环发送,我一个客户端可以发送多次

server.py

import socket

server=socket.socket()

server.bind(('localhost',9999))

server.listen()

print('我要开始等电话了')

while True: #最外层的循环就是可以接受多个电话,一个关掉就换另一个客户端

conn,addr=server.accept() #在Windows上不起作用

print('电话来了',conn)

while True:

data=conn.recv(1024)

print('recv:',data)

if len(data)==0: #在Windows中没有用,断开客户端和服务器端就会报错

print('客户端已断开连接')

break

conn.send(data)

server.close()

client.py

import socket

client=socket.socket()

client.connect(('localhost',9999))

while True:

input_content=input('请输入需要发送的消息:').strip()

if len(input_content)==0:continue #为空就接着输入

client.send(input_content.encode())

data=client.recv(1024)

print('recv:',data.decode())

client.close()

上面的代码在Linux中完全没有问题,我们可以打开多个客户端连接服务器端,同时跟服务器端通信的只有一个,其他的都是在阻塞着,只要当前客户端已断开,那么服务器端就会接受下一个客户端,但是在pycharm(Windows)中使用是有问题的,在server.py中,我们问什么要判断data的长度的呢?因为如果客户端如果断开的话,服务器端收到的就是空,那么在Linux上就会出现循环接受空值,在Windows上,只要客户端已断开就会报错,这里自己去尝试,看百遍不如自己敲一遍。

上面的服务器端,我们在外层套一个while循环,就是为了接受多个客户端,就是指代多个用户打电话,经过测试,不管你打开多少个客户端连接服务器,只要一个断开,就会报ConnectionResetError的错误,呢我们可以捕获一下异常,这样就可以实现多个客户端通信了:

import socket

server = socket.socket()

server.bind(('localhost', 9999))

server.listen()

print('我要开始等电话了')

while True: # 最外层的循环就是可以接受多个电话,一个关掉就换另一个客户端

conn, addr = server.accept()

print('电话来了', conn)

while True:

try:

data = conn.recv(1024)

print('recv:', data)

conn.send(data)

except ConnectionResetError as e:

print('当前客户端已经断开连接,准备接听下一个用户')

break

server.close()

Socket常用函数方法 参数介绍

最后我们来对上面的代码做一个总结,可以看到每次都会创建下面的代码,这通常是创建一个socket对象:

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

我们通常可以这样创建socket对象:socket.socket(),因为默认的参数已经够我们使用了,但是不妨碍我们了解其他的参数:

Socket family(地址簇)

socket.AF_UNIX unix #本机进程间通信

socket.AF_INET #IPV4

socket.AF_INET6 #IPV6

这些常量表示地址(和协议)族,用于socket()的第一个参数。如果没有定义AF_UNIX常量,则不支持此协议。根据系统的不同,可以使用更多的常量

Socket type

socket.SOCK_STREAM #for tcp

socket.SOCK_DGRAM #for udp

socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。

socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。

这些常量表示套接字类型,用于套接字()的第二个参数。根据系统的不同,可以使用更多的常量。(只有SOCK_STREAM和SOCK_DGRAM通常有用。)前面介绍的那么多内容就是便于我们理解socket的参数。

使用给定的地址族、套接字类型和协议号创建一个新的套接字。地址家族应该是AF_INET(默认)、AF_INET6、AF_UNIX、AF_CAN或AF_RDS。套接字类型应该是beSOCK_STREAM(默认)、SOCK_DGRAM、SOCK_RAW或其他SOCK_常量之一。协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应该是CAN_RAW或CAN_BCM之一。如果指定了fileno,则忽略其他参数,从而导致具有指定文件描述符的套接字返回。与socket.fromfd()不同,fileno将返回相同的socket,而不是一个副本。这可能有助于使用socket.close()关闭分离的套接字。

socket.socketpair([family[, type[, proto]]])

使用给定的地址族、套接字类型和协议号构建一对连接的套接字对象。地址族、套接字类型和协议编号与上面的套接字()函数相同。如果在平台上定义,则默认系列是AF_UNIX;否则,默认是AF_INET。

socket.create_connection(address[, timeout[, source_address]])

连接到监听Internet地址的TCP服务(一个2元组(主机、端口)),并返回套接字对象。这是一个比socket.connect()更高级的函数:如果host是一个非数字的主机名,它会尝试为AF_INET和AF_INET6解析它,然后依次尝试连接到所有可能的地址,直到连接成功。这使得编写兼容IPv4和IPv6的客户端变得很容易。

传递可选的超时参数将在尝试连接之前设置套接字实例的超时。如果没有提供超时,则使用getdefaulttimeout()返回的全局缺省超时设置。

如果提供,source_address必须是一个2元组(主机、端口),以便套接字在连接之前绑定到它的源地址。如果主机或端口分别为“或0”,则将使用OS默认行为。

socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0) #获取要连接的对端主机地址

Socket 对象(内建)方法

函数

描述

服务器端套接字

bind()

绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址

listen()

开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。

accept()

被动接受TCP客户端连接,(阻塞式)等待连接的到来

客户端套接字

connect()

主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

connect_ex()

connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数

recv()

接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。

send()

发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。

sendall()

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

recvfrom()

接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

sendto()

发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。

close()

关闭套接字

getpeername()

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

getsockname()

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

setsockopt(level,optname,value)

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

getsockopt(level,optname[.buflen])

返回套接字选项的值。

settimeout(timeout)

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

gettimeout()

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

fileno()

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

setblocking(flag)

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

makefile()

创建一个与该套接字相关连的文件

未经允许不得转载:作者:鳄鱼君,

转载或复制请以 超链接形式 并注明出处 鳄鱼君。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值