Python で実装されたシンプルな UDP サービスを CentOS 上にデプロイします (MySQL データベースの書き込みを含む)

1 Python環境構築

Anaconda は、複数の Python 環境をセットアップできる、一般的に使用される Python パッケージ管理プログラムです。

  • Anaconda インストール スクリプトをダウンロードする
wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-2021.05-Linux-x86_64.sh --no-check-certificate
  • ダウンロードが完了したら、インストールを実行します。認証を読むように求められますので、Enter キーを押し続けてください。
sh Anaconda3-2021.05-Linux-x86_64.sh

次に、同意するかどうかを尋ねられます。「はい」と入力します。
インストール プロセス中にインストール場所について尋ねられます。通常は変更する必要はありません。Enter キーを押すだけで自動的に解凍されます。最後に、Anaconda の一部の構成を初期化するかどうかを尋ねられます。「はい」と入力することを忘れないでください

  • インストールが完了したら、Anaconda を環境変数に構成します (初期構成時に [はい] が選択されている場合、この手順は構成されているはずなのでスキップできます)。ショートカットの conda コマンドを使用できるようになります。
vim ~/.bashrc #编辑环境配置文件
export PATH="~/anaconda3/bin:$PATH" # 在第一行加入这个

vim で「i」と入力して編集し、編集後に Esc キーを押して、「wq」と入力して変更を保存します。

  • 保存後、環境変数を更新し、コマンド ラインに次のように入力します。
source ~/.bash_profile

最後に、設定が成功したかどうかを確認し、保存されていなければ、設定は成功しています。

  • インストール後、python3.8環境を作成し、コマンドを実行すると確認が表示されるのでyを入力してEnterを押します
conda create --name ais_env python=3.8 --channel https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
  • paddle_env 環境をアクティブ化する
conda activate ais_env
  • データベースサポートパッケージpymysqlをインストールします。
pip install pymysql

2 コードのアップロード

サービス コードをサーバーの ais_server ディレクトリにアップロードします。ファイルは次のとおりです。
ここに画像の説明を挿入

ais_udp_server.py: UDP サービス コード
database_mysql.py: データベース インターフェイス
test_client.py: テスト UDP クライアント

2.1 ais_udp_server.py

# 这是 UDP 服务端
import socket
import datetime
import logging
from database_mysql import DataBaseMySQL


class MySqlDal:
    def __init__(self):
        self.db_mysql = DataBaseMySQL()

    def __del__(self):
        self.db_mysql.close()

    # 插入AIS原始数据
    def insert_ais_raw_data(self, type, data, parse_state, ct, mt):
        db = self.db_mysql
        str_sql = "INSERT INTO ais_raw_data (TYPE,DATA,parse_state,ct,mt) VALUES (%s,%s,%s,%s,%s)"
        return db.insert(str_sql, [type, data, parse_state, ct, mt])

    # 插入AIS动态数据
    def insert_ais_dynamic_data(self, raw_id, mmsi, lon, lat, speed, head_dir, track_dir, state, recv_time, ct):
        db = self.db_mysql
        str_sql = "INSERT INTO ais_dynamic_data (raw_id, mmsi, lon, lat, speed, head_dir, track_dir, state, " \
                  "recv_time, ct) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) "
        return db.insert(str_sql, [raw_id, mmsi, lon, lat, speed, head_dir, track_dir, state, recv_time, ct])

    # 插入AIS静态数据
    def insert_ais_static_data(self, raw_id, mmsi, ship_name, imo, call_no, ship_form, ship_draught, ship_length,
                               ship_width, destination, eta, recv_time, ct):
        db = self.db_mysql
        str_sql = "INSERT INTO ais_static_data (raw_id, mmsi, ship_name, imo, call_no, ship_form, ship_draught, " \
                  "ship_length, ship_width, destination, eta, recv_time, ct) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s," \
                  "%s,%s,%s) "
        return db.insert(str_sql,
                         [raw_id, mmsi, ship_name, imo, call_no, ship_form, ship_draught, ship_length, ship_width,
                          destination, eta, recv_time, ct])

    # 更新解析结果
    def update_ais_parse_state(self, id, state, mt):
        db = self.db_mysql
        str_sql = "UPDATE ais_raw_data SET parse_state = %s, mt = %s WHERE id = %s"
        return db.update(str_sql, [state, mt, id])


dal = MySqlDal()

# HOST_NAME = socket.gethostname()
# HOST = socket.gethostbyname(HOST_NAME)  # 主机号可为空白 HOST = ""
HOST = '0.0.0.0'    # 这里有个坑,服务ip需要写0.0.0.0,不能使用127.0.0.1,不然内网和外网其他机器都访问不了,只能本机访问该服务
PORT = 9504
ADDR = (HOST, PORT)  # 地址与端口
BUF_SIZE = 10240  # 接收数据缓冲大小
UDPSerSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 创建udp服务器套接字
UDPSerSock.bind(ADDR)  # 套接字与地址绑定-服务端特有
print(ADDR)
encodeing = "gb18030"
# encodeing = "utf-8"

while True:
    # 接收客户端发来的字节数组-此处监听
    data, addr = UDPSerSock.recvfrom(BUF_SIZE)  # 接收客户端发来的字节数组,data.decode()='char',data.upper()='bytes'
    data_str = data.decode(encodeing)
    print("{} Data Receive from Client {} --> {}".format(datetime.datetime.now(), addr, data_str))
    if data_str is None:
        response_str = "Error: Data is None!"
    elif not data_str.startswith("@"):
        response_str = "Error: Format error 1!"
    else:
        data_array = data_str.replace("@", "").strip(' ').split(",")
        if len(data_array) == 8:
            # 动态数据
            ret = dal.insert_ais_raw_data(1, data_str, 0, datetime.datetime.now(), datetime.datetime.now())
            if ret is not None:
                try:
                    raw_id = ret
                    mmsi = data_array[0]
                    lon = 0 if len(data_array[1].strip(" ")) == 0 else float(data_array[1].strip(" "))
                    lat = 0 if len(data_array[2].strip(" ")) == 0 else float(data_array[2].strip(" "))
                    speed = 0 if len(data_array[3].strip(" ")) == 0 else float(data_array[3].strip(" "))
                    head_dir = 0 if len(data_array[4].strip(" ")) == 0 else float(data_array[4].strip(" "))
                    track_dir = 0 if len(data_array[5].strip(" ")) == 0 else float(data_array[5].strip(" "))
                    state = 0 if len(data_array[6].strip(" ")) == 0 else int(data_array[6].strip(" "))
                    recv_time = 0 if len(data_array[7].strip(" ")) == 0 else int(data_array[7].strip(" "))
                    ret1 = dal.insert_ais_dynamic_data(raw_id, mmsi, lon, lat, speed, head_dir, track_dir, state, recv_time,
                                                datetime.datetime.now())
                    if ret1 is not None:
                        response_str = "Success!"
                        dal.update_ais_parse_state(raw_id, 1, datetime.datetime.now())
                    else:
                        response_str = "Error: Insert parse data error!"
                        dal.update_ais_parse_state(raw_id, -2, datetime.datetime.now())
                except Exception as e:
                    logging.error('数据解析失败:%s' % e)
                    response_str = "Error: Parse error!"
                    dal.update_ais_parse_state(raw_id, -1, datetime.datetime.now())
            else:
                response_str = "Error: Insert error!"
        elif len(data_array) == 11:
            # 静态数据
            ret = dal.insert_ais_raw_data(2, data_str, 0, datetime.datetime.now(), datetime.datetime.now())
            if ret is not None:
                try:
                    raw_id = ret
                    mmsi = data_array[0]
                    ship_name = data_array[1]
                    imo = data_array[2]
                    call_no = data_array[3]
                    ship_form = data_array[4]
                    ship_draught = 0 if len(data_array[5].strip(" ")) == 0 else float(data_array[5].strip(" "))
                    ship_length = 0 if len(data_array[6].strip(" ")) == 0 else float(data_array[6].strip(" "))
                    ship_width = 0 if len(data_array[7].strip(" ")) == 0 else float(data_array[7].strip(" "))
                    destination = data_array[8]
                    eta = 0 if len(data_array[9].strip(" ")) == 0 else int(data_array[9].strip(" "))
                    recv_time = 0 if len(data_array[10].strip(" ")) == 0 else int(data_array[10].strip(" "))
                    ret2 = dal.insert_ais_static_data(raw_id, mmsi, ship_name, imo, call_no, ship_form, ship_draught,
                                               ship_length, ship_width, destination, eta, recv_time,
                                               datetime.datetime.now())
                    if ret2 is not None:
                        response_str = "Success!"
                        dal.update_ais_parse_state(raw_id, 1, datetime.datetime.now())
                    else:
                        response_str = "Error: Insert parse data error!"
                        dal.update_ais_parse_state(raw_id, -2, datetime.datetime.now())
                except Exception as e:
                    logging.error('数据解析失败:%s' % e)
                    response_str = "Error: Parse error!!"
                    dal.update_ais_parse_state(raw_id, -1, datetime.datetime.now())
            else:
                response_str = "Error: Insert error!"
        else:
            response_str = "Error: Format error 2!"
    # 向客户端发送字节数组
    UDPSerSock.sendto(bytes(response_str, encodeing), addr)  # 向客户端发送字节数组, bytes("char", "utf-8")
    print("{} Response Sentend --> {}".format(datetime.datetime.now(), response_str))
    pass
UDPSerSock.close()  # 关闭服务端socket

2.2 データベース_mysql.py

import pymysql
import logging


class DataBaseMySQL:
    def __init__(self):
        try:
            self.connection = pymysql.connect(host="这里填写数据库服务器IP",
                                              user="这里填写用户名",
                                              pass删除这个word="这里填写PW",
                                              port=这里填写端口号,
                                              database="这里填写数据库名",
                                              charset='utf8')

            # "vn_vm_center"
            self.cursor = self.connection.cursor()
        except Exception as e:
            logging.error('数据库连接错误:%s' % e)
            raise

    def execute(self, sql):
        conn = self.connection
        try:
            cur = self.cursor
            cur.execute(sql)
            conn.commit()
            return True
        except Exception as e:
            logging.error('数据执行失败:%s' % e)
            conn.rollback()
            return False

    def query(self, sql, data):
        cur = self.cursor
        cur.execute(sql, data)
        res = cur.fetchone()
        return res

    def query_all(self, sql, data):
        cur = self.cursor
        cur.execute(sql, data)
        res = cur.fetchall()
        return res

    def insert(self, sql, data):
        conn = self.connection
        try:
            cur = self.cursor
            cur.execute(sql, data)
            res = conn.insert_id()
            conn.commit()
            return res
        except Exception as e:
            logging.error('数据新增执行失败:%s' % e)
            conn.rollback()
            return None

    def insert_many(self, sql, datas):
        conn = self.connection
        try:
            cur = self.cursor
            # 执行sql语句
            cur.executemany(sql, datas)
            # 提交到数据库执行
            conn.commit()
        except Exception as e:
            # 如果发生错误则回滚
            print(e)
            print(sql)
            print(datas)
            conn.rollback()
            logging.error('数据新增执行失败:%s' % e)

    def update(self, sql, data):
        conn = self.connection
        try:
            cur = self.cursor
            cur.execute(sql, data)
            conn.commit()
            return True
        except Exception as e:
            logging.error('数据更新执行失败:%s' % e)
            conn.rollback()
            return False

    def close(self):
        self.cursor.close()
        self.connection.close()

2.3 test_client.py

# 这是 UDP 客户端
import socket

HOST = '127.0.0.1'  # 本机测试
PORT = 9504  # 端口号
BUFSIZ = 10240  # 接收消息的缓冲大小
ADDR = (HOST, PORT)
print(ADDR)
UDPCliSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 创建客户端套接字
while True:
    ##step.1##
    data = input('输入发送内容> ')
    if not data:
        break
    UDPCliSock.sendto(bytes(data, 'gb18030'), ADDR)  # 由客户端向服务端发送【字节数组】
    ##step.2##
    data, ADDR = UDPCliSock.recvfrom(BUFSIZ)  # 接收服务端回应的【字节数组】
    if not data:  # 如果接收服务器信息失败,或没有消息回应
        break
    print('服务器:', ADDR, str(data, 'gb18030'))  # 打印回应消息
    pass
UDPCliSock.close()  # 关闭客户端socket

3 データシート

3.1 ais_raw_data

ここに画像の説明を挿入

3.2 ais_dynamic_data

ここに画像の説明を挿入

3.3 ais_static_data

ここに画像の説明を挿入

4 つのサービスが実行中

  • コードディレクトリを入力してください
cd /ais_server/
  • 仮想環境に入る
conda activate ais_env
  • サービスを実行する
nohup python -u ais_udp_server.py > log.txt 2>&1 &

プロセスを表示する

ps -ef|grep python

プロセスを閉じる

kill -9 19913

ビュー・ログ

tail -f 1000 /ais_server/log.txt

ポート占有状況の確認方法

$: netstat -anp | grep 9504
udp        0      0 0.0.0.0:9504            0.0.0.0:*                           4793/python

プロセスを強制的に強制終了します: pid によって

$: kill -9 4793
$: netstat -anp | grep 8888

4 テスト検証

ローカル テストは test_client.py クライアントを通じて実行できます。ここでは、次の図に示すように、検証に SocketTool.exe ツールが使用されます。
ここに画像の説明を挿入

参考:https://blog.csdn.net/weixin_41275726/article/details/124529674

おすすめ

転載: blog.csdn.net/loutengyuan/article/details/130152499