一些坎坷
过程中遇到了一个奇怪的问题:广播地址为“255.255.255.255”时会出现“[WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试”的错误。将广播地址换成“192.168.1.255”就能运行。能运行之后,便将这个问题搁置了。
在将代码移植到Linux系统时,又出现了错误,其中最大的PermissionError: [Errno 13] Permission denied(还有一个不太大的是:OSError: [Errno 22] Invalid argument,这个好像与上一个有点关系,最后一起都解决了)。老一会儿也没解决,最后从网上找了一个简单的广播代码测试,并进行比较,发现少了一行:
udpServer.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
这一行的是设置socket属性,第二个参数是广播服务。这一行的作用之一是开启广播属性,问题解决。后来回想到,搜索Permission denied的错误产生原因,其中有一个是没有访问权限,即使sudo也不行,得加上开启广播属性的代码才可以。之后完成了两台主机之间正常的广播服务,请求服务和提供服务的目标。(但是广播地址从“255.255.255.255”改到“192.168.1.255”这个现象,在Windows的pycharm上可以,在Linux上不行,也许是一个小漏洞?)
一开始在Windows上测试的时候,socket(IP, port)中的IP可以使用socket.gethostname()来获取。当服务器和客户端在一台主机上时没有问题,在两台主机上的时候,发现客户端不能接收的广播报文。输出这个地址之后,发现socket中的IP事主机的名称,而不是IP地址。将套接字中的IP改为“0.0.0.0”(表示本网段上的本主机),或者ipconfig查到的的IP地址(192.168.1.161),两台主机即可通信。
payLoad.py
import someip.sd from scapy.contrib.automotive.someip import * import binascii from scapy.layers.inet import IP from scapy.all import ls import struct class load: def __init__(self): self.opt = 0 # 0: offer , 1: request, 2: response self.svce = [] # 存储服务ID def encod(self): msg: str = "" msg += str(self.len_format(self.opt, 4)) for i in self.svce: msg += str(self.len_format(i, 4)) return msg def len_format(self, num, len): """对数据做格式化, 去掉16进制数前面的0x""" length_hex = hex(num) if length_hex.startswith('0x'): str_length = length_hex[2:] # 去掉16进制数前面的0x total_length = str_length.zfill(len) return total_length def decod(self, str: str): self.opt = int(str[0: 4], 16) i = 4 while i < str.__len__(): self.svce.append(int(str[i: i + 4], 16)) i += 4 def show_payload(self): print("opt = ", self.opt) for i in self.svce: print("service : ", i) ''' pkt = SOMEIP(b"\x07\xff\x80\x01\x00\x00\x00S\x00\x00\x00\x06\x01\x01\x02\x00\x0c\\xf1\xdds\x84\x00\x00\x00>[\x19\xa2\xd1aV\xce'\xc1)\xa9x02Eg\x00\x00\x00D") # ls(pkt) # 编报头 data = SOMEIP() data.srv_id = 0xffff data.method_id = 0x8100 data.len = 8 data.client_id = 0 data.session_id = 0 data.proto_ver = 1 data.iface_ver = 1 data.msg_type = 2 data.retcode = 0 # 编辑有效载荷 msg = load() msg.opt = 0 msg.svce.append(1234) msg.svce.append(5678) msg.svce.append(2345) msg.svce.append(6789) # 组合报文 data.add_payload(binascii.a2b_hex(msg.encod().encode())) print("data: ", data.build()) # 接收报文,并解码 data1 = data msg1 = binascii.b2a_hex(data1.payload.build()) msg1 = msg1.decode() # 从 bytes 到 str rcv = load() rcv.decod(msg1) rcv.show_payload() '''
服务器端
import time from scapy.contrib.automotive.someip import * import socket from payLoad import load import binascii from threading import Thread def recveData(soc): while True: clientData, clientAddr = soc.recvfrom(1024) # 接收来自客户端的数据 # print(clientData) # 打印普通格式报文 someipData = SOMEIP(_pkt=clientData) # someipData.show() # 打印someip格式报文 # 输出payload字段 pay_load = load() # 16进制 -> 2进制的表现形式 -> str rmsg = binascii.b2a_hex(someipData.payload.build()).decode() pay_load.decod(rmsg) if pay_load.opt == 1: print("request service from client ", clientAddr) print("opt = ", pay_load.opt) for i in pay_load.svce: print("service : ", i) pay_load.opt = 2 serviceData = someipData serviceData.remove_payload() serviceData.add_payload(binascii.a2b_hex(pay_load.encod().encode())) soc.sendto(serviceData.build(), clientAddr) # 发送数据给客户端,提供服务 def broadCast(soc): data = SOMEIP() data.srv_id = 0xffff data.method_id = 0x8100 # data.len = 8 data.client_id = 0 data.session_id = 0 data.proto_ver = 1 data.iface_ver = 1 data.msg_type = 0x80 data.retcode = 0 # 编辑有效载荷 msg = load() msg.opt = 0 msg.svce.append(1234) msg.svce.append(5678) msg.svce.append(6789) msg.svce.append(7891) data.len = 8 + 2 + 2 * msg.svce.__len__() # 组合报文 data.add_payload(binascii.a2b_hex(msg.encod().encode())) while True: soc.sendto(data.build(), ("255.255.255.255", 8080)) print("broadcast") time.sleep(1) udpServer = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 创建socket对象,走udp通道 # 开启socket的广播属性,否则进行广播时会报错! udpServer.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) host = "0.0.0.0" # 获取本地主机名 port = 1000 serverAddr = (host, port) udpServer.bind(serverAddr) # 绑定服务端地址 broadcastAddr = ("255.255.255.255", port) # 广播地址 t1 = Thread(target=recveData, args=(udpServer,)) t2 = Thread(target=broadCast, args=(udpServer,)) t1.start() t2.start() t1.join() t2.join() udpServer.close()
客户端
import binascii import socket import time import random from payLoad import load from scapy.contrib.automotive.someip import * from threading import Thread udpClient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 创建socket对象,走udp通道 tcpClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建socket对象,走tcp通道 # 开启socket的广播属性socket.SO_BROADCAST,否则进行广播时会报错! udpClient.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) host = "0.0.0.0" # 获取本地主机名 port = 8080 serverAddr = (host, port) clientAddr = (host, port) print(serverAddr) udpClient.bind(clientAddr) serviceList = [] find_server = 0 def recveData(soc): global find_server, serverAddr while True: rcvData, rcvAddr = soc.recvfrom(1024) # 接收来自服务端的数据 # print(rcvData) # 打印普通格式报文 someipData = SOMEIP(_pkt=rcvData) # someipData.show() # 打印someip格式报文 # 输出payload字段 pay_load = load() # 16进制 -> 2进制的表现形式 -> str rmsg = binascii.b2a_hex(someipData.payload.build()).decode() pay_load.decod(rmsg) if pay_load.opt == 0 and find_server == 0: # 存储下服务器的地址和端口,以及提供的服务 serverAddr = rcvAddr for i in pay_load.svce: serviceList.append(i) find_server = 1 print("add service") else: if pay_load.opt == 2 and rcvAddr == serverAddr: print("response from server ", serverAddr) print("opt = ", pay_load.opt) for i in pay_load.svce: print("service : ", i) def sendData(udpClient): global serverAddr while True: if serviceList.__len__() == 0: continue # someip报文 data = SOMEIP() data.srv_id = 0x2568 data.method_id = 0x8001 # data.len = 27 data.client_id = 0 data.session_id = 0 data.proto_ver = 1 data.iface_ver = 1 data.msg_type = 0 data.retcode = 0 # 编辑有效载荷 msg = load() msg.opt = 1 req_svce = serviceList[random.randint(0, serviceList.__len__() - 1)] msg.svce.append(req_svce) data.len = 8 + 2 + 2 * msg.svce.__len__() # 组合报文 data.add_payload(binascii.a2b_hex(msg.encod().encode())) i = 0 while i < 1: udpClient.sendto(data.build(), serverAddr) # 发送报文给服务端 serverData, serverAddr = udpClient.recvfrom(1024) # 接收来自服务端的数据 # print(serverData) # 打印普通格式报文 someipData = SOMEIP(_pkt=serverData) # someipData.show() # 打印someip格式报文 # 输出payload字段 pay_load = load() # 16进制 -> 2进制的表现形式 -> str rmsg = binascii.b2a_hex(someipData.payload.build()).decode() pay_load.decod(rmsg) # pay_load.show_payload() time.sleep(1) i += 1 t1 = Thread(target=recveData, args=(udpClient,)) t2 = Thread(target=sendData, args=(udpClient,)) t1.start() t2.start() t1.join() t2.join() udpClient.close()