在CentOS上部署一个简单的Python实现的UDP服务(包含MySQL数据库写入)

1 Python环境搭建

Anaconda是1个常用的python包管理程序,里面可以设置好多个python环境。

  • 下载 Anaconda 安装脚本
wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-2021.05-Linux-x86_64.sh --no-check-certificate
  • 下载完成后,执行安装,会提示你阅读授权,一直按回车就可以了
sh Anaconda3-2021.05-Linux-x86_64.sh

然后提示你是否同意,输入yes
安装过程中会询问你安装的位置,一般不用更改,输入回车就行,它会自动解压缩
最后会提示是否初始化Anaconda的一些配置,记得输入yes

  • 安装好了之后,把Anaconda配置到环境变量(如果初始化配置时选择了yes,这步应该已经配置好了,可以跳过),就可以用快捷 conda 命令了
vim ~/.bashrc #编辑环境配置文件
export PATH="~/anaconda3/bin:$PATH" # 在第一行加入这个

vim 输入 i 可以编辑,编辑好了之后按 Esc, 然后输入 :wq 保存修改

  • 保存好了之后更新环境变量,在命令行输入:
source ~/.bash_profile

最后验证一下是否配置成功,没有保存的话就是配置成功了!

  • 安装好了之后,创建一个 python3.8的环境,执行一下命令,会有一个确认,输入 y ,然后回车就可以了
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 目录下,文件如下图:
在CentOS上部署一个简单的Python实现的UDP服务(包含MySQL数据库写入)_第1张图片

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 database_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

在CentOS上部署一个简单的Python实现的UDP服务(包含MySQL数据库写入)_第2张图片

3.2 ais_dynamic_data

在CentOS上部署一个简单的Python实现的UDP服务(包含MySQL数据库写入)_第3张图片

3.3 ais_static_data

在CentOS上部署一个简单的Python实现的UDP服务(包含MySQL数据库写入)_第4张图片

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 工具进行验证,如下图:
在CentOS上部署一个简单的Python实现的UDP服务(包含MySQL数据库写入)_第5张图片

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

你可能感兴趣的:(使用教程,python,数据库,centos,udp)