Cheng Tang | 05eec9b | 2013-07-09 09:33:37 +0800 | [diff] [blame] | 1 | # -*- coding: utf-8 |
| 2 | """ |
| 3 | agentlink 模块,提供与 swagent 通讯协议的封装 |
| 4 | """ |
| 5 | import codecs |
| 6 | import socket |
| 7 | import struct |
| 8 | import random |
| 9 | import time |
| 10 | import os |
| 11 | import traceback |
| 12 | |
| 13 | __all__ = ['VERSION', 'SWAgentLink'] |
| 14 | |
| 15 | VERSION = "4.0.0" |
| 16 | |
| 17 | |
| 18 | class SWAgentLink(object): |
| 19 | """ |
| 20 | agentlink 协议封装 |
| 21 | """ |
| 22 | MSG_LEN_FMT = '!H' |
| 23 | MSG_HEADER_FMT = '!IIIBB' |
| 24 | MSG_HEADER_DESC = ('major_version', 'minor_version', 'unique_no', 'msg_type', 'retcode') |
| 25 | # 协议版本号定义 |
Cheng Tang | 2d6d085 | 2013-07-09 17:06:53 +0800 | [diff] [blame^] | 26 | MAJOR_VERSION = 2 |
Cheng Tang | 05eec9b | 2013-07-09 09:33:37 +0800 | [diff] [blame] | 27 | MINOR_VERSION = 1 |
| 28 | |
| 29 | def __init__(self): |
| 30 | self.sock = None |
| 31 | self.ip = None |
| 32 | self.port = None |
| 33 | self.timeout = 10.0 |
| 34 | self._is_login = False |
| 35 | self.unique_no = None |
| 36 | self.connection_id = None |
| 37 | random.seed(time.time()) |
| 38 | |
| 39 | def connect(self, ip, port): |
| 40 | self.close() |
| 41 | try: |
| 42 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 43 | sock.settimeout(self.timeout) |
| 44 | sock.connect((ip, port)) |
| 45 | self.sock = sock |
| 46 | self.ip = ip |
| 47 | self.port = port |
| 48 | return True |
| 49 | except socket.error: |
| 50 | return False |
| 51 | |
| 52 | def login(self): |
| 53 | msg_type = 0x30 |
| 54 | process_id = struct.pack('!I', os.getpid()) |
| 55 | message_header = self._new_message(msg_type) |
| 56 | login_data = self._pack_request(message_header, process_id) |
| 57 | data = self._send_and_recv(login_data, 5.0) |
| 58 | if not data: |
| 59 | return False |
| 60 | login_resp = self._unpack_request(data) |
| 61 | if not self._check_response(message_header, login_resp): |
| 62 | print u"Login response error" |
| 63 | return False |
| 64 | if login_resp['retcode'] != 0: |
| 65 | print u"Login error retcode={0}".format(login_resp['retcode']) |
| 66 | return False |
| 67 | self.connection_id = login_resp['data'] |
| 68 | self._is_login = True |
| 69 | return True |
| 70 | |
| 71 | def _send_and_recv(self, request, timeout): |
| 72 | header_len = struct.calcsize(self.MSG_LEN_FMT) |
| 73 | try: |
| 74 | self.sock.settimeout(timeout) |
| 75 | self.sock.send(request) |
| 76 | # print u"Send[{0}]".format(codecs.encode(request, 'hex')) |
| 77 | header = self.sock.recv(header_len) |
| 78 | if not header: |
| 79 | return None |
| 80 | body_len = struct.unpack(self.MSG_LEN_FMT, header)[0] |
| 81 | body = self.sock.recv(body_len) |
| 82 | # print u"Recv[{0}]".format(codecs.encode(body, 'hex')) |
| 83 | return body |
| 84 | except socket.error as e: |
| 85 | print u"socket send and recv error, error: <{0}>".format(e) |
| 86 | traceback.print_exc() |
| 87 | self.sock.close() |
| 88 | self.sock = None |
| 89 | self._is_login = False |
| 90 | return None |
| 91 | |
| 92 | def get_data(self): |
| 93 | msg_type = 0x32 |
| 94 | message_header = self._new_message(msg_type) |
| 95 | req_data = self._pack_request(message_header, self.connection_id) |
| 96 | data = self._send_and_recv(req_data, None) |
| 97 | if not data: |
| 98 | return False |
| 99 | getdata_resp = self._unpack_request(data) |
| 100 | if not self._check_response(message_header, getdata_resp): |
| 101 | print u"get data error" |
| 102 | return None |
| 103 | if getdata_resp['retcode'] != 0: |
| 104 | print u"GetData error retcode={0}".format(getdata_resp['retcode']) |
| 105 | return None |
| 106 | data = getdata_resp['data'] |
| 107 | # print codecs.encode(data, 'hex') |
| 108 | data_head_len = struct.calcsize('!I') |
| 109 | data_len = struct.unpack('!I', data[:data_head_len])[0] |
| 110 | if data_len != len(data) - data_head_len: |
| 111 | return None |
| 112 | return data[data_head_len: data_head_len + data_len] |
| 113 | |
| 114 | def answer_data(self, response): |
| 115 | msg_type = 0x34 |
| 116 | message_header = self._new_message(msg_type) |
| 117 | req_data = self._pack_request(message_header, self.connection_id + response) |
| 118 | data = self._send_and_recv(req_data, None) |
| 119 | answer_resp = self._unpack_request(data) |
| 120 | if not self._check_response(message_header, answer_resp): |
| 121 | print u"answer data error" |
| 122 | return False |
| 123 | if answer_resp['retcode'] != 0: |
| 124 | print u"Answer error retcode={0}".format(answer_resp['retcode']) |
| 125 | return False |
| 126 | return True |
| 127 | |
Cheng Tang | 2d6d085 | 2013-07-09 17:06:53 +0800 | [diff] [blame^] | 128 | def answer_invalidate(self): |
| 129 | msg_type = 0x36 |
| 130 | message_header = self._new_message(msg_type) |
| 131 | req_data = self._pack_request(message_header, self.connection_id) |
| 132 | data = self._send_and_recv(req_data, None) |
| 133 | answer_resp = self._unpack_request(data) |
| 134 | if not self._check_response(message_header, answer_resp): |
| 135 | print u"answer data error" |
| 136 | return False |
| 137 | if answer_resp['retcode'] != 0: |
| 138 | print u"Answer error retcode={0}".format(answer_resp['retcode']) |
| 139 | return False |
| 140 | return True |
| 141 | |
Cheng Tang | 05eec9b | 2013-07-09 09:33:37 +0800 | [diff] [blame] | 142 | def _new_message(self, msg_type): |
| 143 | unique_no = random.randint(0x10000000, 0xFFFFFFFF) |
| 144 | return dict(major_version=self.MAJOR_VERSION, minor_version=self.MINOR_VERSION, |
| 145 | unique_no=unique_no, msg_type=msg_type, retcode=0) |
| 146 | |
| 147 | def _pack_request(self, message_header, data): |
| 148 | header = struct.pack(self.MSG_HEADER_FMT, message_header['major_version'], |
| 149 | message_header['minor_version'], message_header['unique_no'], |
| 150 | message_header['msg_type'], message_header['retcode']) |
| 151 | r = [] |
| 152 | if not data: |
| 153 | r.append(struct.pack(self.MSG_LEN_FMT, len(header))) |
| 154 | r.append(data) |
| 155 | else: |
| 156 | r.append(struct.pack(self.MSG_LEN_FMT, (len(header) + len(data)))) |
| 157 | r.append(header) |
| 158 | r.append(data) |
| 159 | return ''.join(r) |
| 160 | |
| 161 | def close(self): |
| 162 | if self.sock: |
| 163 | self.sock.close() |
| 164 | self.sock = None |
| 165 | self._is_login = False |
| 166 | |
| 167 | def is_ok(self): |
| 168 | if not self.sock: |
| 169 | return False |
| 170 | if not self._is_login: |
| 171 | return False |
| 172 | return True |
| 173 | |
| 174 | def _unpack_request(self, data): |
| 175 | if not data: |
| 176 | return None |
| 177 | header_len = struct.calcsize(self.MSG_HEADER_FMT) |
| 178 | if header_len > len(data): |
| 179 | return None |
| 180 | resp = struct.unpack(self.MSG_HEADER_FMT, data[:header_len]) |
| 181 | resp_dict = dict(zip(self.MSG_HEADER_DESC, resp)) |
| 182 | resp_dict['data'] = data[header_len:] |
| 183 | return resp_dict |
| 184 | |
| 185 | def _check_response(self, message_header, response): |
| 186 | if not response: |
| 187 | return False |
| 188 | if (message_header['major_version'] != response['major_version'] |
| 189 | or message_header['minor_version'] != response['minor_version'] |
| 190 | or message_header['unique_no'] != response['unique_no'] |
| 191 | or message_header['msg_type'] != response['msg_type']): |
| 192 | return False |
| 193 | return True |