esp32之Socket通信,get/post至mysql数据库

1、socket通信创建过程(以客户端为例)

      a:创建Socket,涉及到的主要参数有:domain、type、protocal。

      domain是协议域,其中AF_INET->IPv4;AF_INET6->IPv6 。

      type对应socket类型,SOCK_STREAM->TCP;SOCK_DGRAM->UDP。

      protocol是IPPROTO_TCP,若传入0,则会根据第二个参数type,自动选择合适的参数。

      b,连接到服务器,主要参数:

      客户端socket

      指向数据结构socketaddr的指针,其中包括目的端口和IP地址

      结构体数据长度

      c,发送数据到服务器,主要参数:

      客户端socket

      发送内容地址

      发送内容长度

      发送方式标志,一般为0

      d,从服务器接受数据,主要参数:

      客户端socket

      接受内容缓冲区地址

      接受内容缓冲区长度

      接受方式,0表示阻塞,必须等待服务器返回数据返回值,若成功,则返回读入的字节数,失败则 返回SOCKET_ERROR。

      e,关闭socket。

2、python 实现socket通信代码实例

     服务器端:

"""
file: service.py
socket service
"""
 
 
import socket
import threading
import time
import sys
 
 
def socket_service():
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 实例化
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #防止socket server重启后端口被占用(socket.error: [Errno 98] Address already in use)
        s.bind(('0.0.0.0', 6666)) #bind端口
        s.listen(100) #允许连接数
    except socket.error as msg:
        print(msg)
        sys.exit(1)
    print('Waiting connection...')
 
    while 1:
        conn, addr = s.accept() #阻塞连接
        t = threading.Thread(target=deal_data, args=(conn, addr)) #多线程
        t.start()
 
def deal_data(conn, addr):
    print('Accept new connection from {0}'.format(addr))
    conn.send(('Hi, Welcome to the server!').encode()) #发送响应
    while 1:
        data = conn.recv(1024)
        print('{0} client send data is {1}'.format(addr, data.decode()))
        time.sleep(1)
        if data == 'exit' or not data: #防止客户断线或异常中断
            print('{0} connection close'.format(addr))
            conn.send(bytes('Connection closed!'),'UTF-8')
            break
        """
        reavFull += data #如果客户发送100k数据的处理
        if len(reavFull)!=1024*100:
            continue
        """
        conn.send(bytes('Hello, {0}'.format(data),"UTF-8"))
    conn.close()
 
 
if __name__ == '__main__':
    socket_service()

 客户端:

import socket
import sys
 
 
def socket_client():
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(('192.168.1.101', 6666))
    except socket.error as msg:
        print(msg)
        sys.exit(1)
    print(s.recv(1024))#目的在于接受:Accept new connection from (...
    while 1:
        data = input('please input work: ').encode()
        s.send(data)
        print('aa',s.recv(1024))
        if data == 'exit':
            break
    s.close()
 
 
if __name__ == '__main__':
    socket_client()

3、python 实现socket通信代码实例二

进行文件的传输,如,.txt,.jpg等等

服务器端:

###服务器端server.py
import socket
import os
import sys
import struct
 
 
def socket_service_image():
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # s.bind(('127.0.0.1', 6666))
        s.bind(('127.0.0.1', 6666))
        s.listen(10)
    except socket.error as msg:
        print(msg)
        sys.exit(1)
 
    print("Wait for Connection.....................")
 
    while True:
        sock, addr = s.accept()  # addr是一个元组(ip,port)
        deal_image(sock, addr)
 
 
def deal_image(sock, addr):
    print("Accept connection from {0}".format(addr))  # 查看发送端的ip和端口
 
    while True:
        fileinfo_size = struct.calcsize('128sq')
        print('fileinfo_size is',fileinfo_size)
        buf = sock.recv(fileinfo_size)  # 接收图片名
        print('buf is ',buf)
        if buf:
            filename, filesize = struct.unpack('128sq', buf)
            print('filename ,filesize is',filename.decode(),filesize )
            fn = filename.decode().strip('\x00')
            print('fn is ',fn)
            new_filename = os.path.join('./',
                                        'new_' + fn)  # 在服务器端新建图片名(可以不用新建的,直接用原来的也行,只要客户端和服务器不是同一个系统或接收到的图片和原图片不在一个文件夹下)
 
            recvd_size = 0
            fp = open(new_filename, 'wb')
 
            while not recvd_size == filesize:
                if filesize - recvd_size > 1024:
                    data = sock.recv(1024)
                    recvd_size += len(data)
                else:
                    data = sock.recv(1024)
                    recvd_size = filesize
                print('data is',data)
                fp.write(data)  # 写入图片数据
            fp.close()
        sock.close()
        break
 
 
if __name__ == '__main__':
    socket_service_image()

客户端:

'''
Fuction:客户端发送图片和数据
Date:
Author:mxh
'''
###客户端client.py
import socket
import os
import sys
import struct
 
 
def sock_client_image():
    while True:
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect(('127.0.0.1', 6666))  # 服务器和客户端在不同的系统或不同的主机下时使用的ip和端口,首先要查看服务器所在的系统网卡的ip
            # s.connect(('127.0.0.1', 6666))  #服务器和客户端都在一个系统下时使用的ip和端口
        except socket.error as msg:
            print(msg)
            print(sys.exit(1))
        filepath = input('input the file: ')  # 输入当前目录下的图片名 xxx.jpg
        fhead = struct.pack(b'128sq', bytes(os.path.basename(filepath), encoding='utf-8'),
                            os.stat(filepath).st_size)  # 将xxx.jpg以128sq的格式打包
        s.send(fhead)
 
        fp = open(filepath, 'rb')  # 打开要传输的图片
        while True:
            data = fp.read(1024)  # 读入图片数据
            if not data:
                print('{0} send over...'.format(filepath))
                break
            s.send(data)  # 以二进制格式发送图片数据
        s.close()
        # break    #循环发送
 
 
if __name__ == '__main__':
    sock_client_image()

IO多路复用 

I/O(input/output),即输入/输出端口。每个设备都会有一个专用的I/O地址,用来处理自己的输入输出信息首先什么是I/O:

I/O分为磁盘io和网络io,这里说的是网络io

IO多路复用:

I/O多路复用指:通过一种机制,可以监视多个描述符(socket),一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

Linux

Linux中的 select,poll,epoll 都是IO多路复用的机制。

Linux下网络I/O使用socket套接字来通信,普通I/O模型只能监听一个socket,而I/O多路复用可同时监听多个socket.

I/O多路复用避免阻塞在io上,原本为多进程或多线程来接收多个连接的消息变为单进程或单线程保存多个socket的状态后轮询处理.

Python  

Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。

对于select模块操作的方法:

句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)

参数: 可接受四个参数(前三个必须)

返回值:三个列表

select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。

1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中

2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中

3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中

4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化

5、当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行

例子1:

服务端:
sk1 = socket.socket()
sk1.bind(("127.0.0.1",8001))
sk1.listen()
 
inpu = [sk1,]
 
while True:
    r_list,w_list,e_list = select.select(inpu,[],[],1)
    for sk in r_list:
        if sk == sk1:
            conn,address = sk.accept()
            inpu.append(conn)
        else:
            try:
                ret = str(sk.recv(1024),encoding="utf-8")
                sk.sendall(bytes(ret+"hao",encoding="utf-8"))
            except Exception as ex:
                inpu.remove(sk)
 
客户端
import socket
 
obj = socket.socket()
 
obj.connect(('127.0.0.1',8001))
 
while True:
    inp = input("Please(q\退出):\n>>>")
    obj.sendall(bytes(inp,encoding="utf-8"))
    if inp == "q":
        break
    ret = str(obj.recv(1024),encoding="utf-8")
    print(ret)

例子2:

服务端:
import socket
sk1 = socket.socket()
sk1.bind(("127.0.0.1",8001))
sk1.listen()
inputs = [sk1]
import select
message_dic = {}
outputs = []
while True:
 
    r_list, w_list, e_list = select.select(inputs,[],inputs,1)
    print("正在监听的socket对象%d" % len(inputs))
    print(r_list)
    for sk1_or_conn in r_list:
        if sk1_or_conn == sk1:
            conn,address = sk1_or_conn.accept()
            inputs.append(conn)
            message_dic[conn] = []
        else:
            try:
                data_bytes = sk1_or_conn.recv(1024)
                data_str = str(data_bytes,encoding="utf-8")
                sk1_or_conn.sendall(bytes(data_str+"好",encoding="utf-8"))
            except Exception as ex:
                inputs.remove(sk1_or_conn)
            else:
                data_str = str(data_bytes,encoding="utf-8")
                message_dic[sk1_or_conn].append(data_str)
                outputs.append(sk1_or_conn)
        for conn in w_list:
            recv_str = message_dic[conn][0]
            del message_dic[conn][0]
            conn.sendall(bytes(recv_str+"好",encoding="utf-8"))
        for sk in e_list:
            inputs.remove(sk)
 
客户端:
import socket
 
obj = socket.socket()
 
obj.connect(('127.0.0.1',8001))
 
while True:
    inp = input("Please(q\退出):\n>>>")
    obj.sendall(bytes(inp,encoding="utf-8"))
    if inp == "q":
        break
    ret = str(obj.recv(1024),encoding="utf-8")
    print(ret)

4、esp32之post至mysql数据库

ESP32代码:

import network
import urequests
import json
import time


d1=10
d2=20
d3='h0101'

while True:
    _response = urequests.post("http://www.winvip.top/test/test.php/", headers={"Content-Type":"application/json"}, data=json.dumps({"api_key":"tPmAafaT5Ab3j7F9", "value1":d1, "value2":d2, "value3":d3}))
    print(_response.text)
    time.sleep(3)

PHP接收post,解析json,更新数据库代码:

<?php

$servername = "localhost";  //数据库地址
// 数据库名
$dbname = "XXXXXXXX";
// 数据库用户名
$username = "XXXXXXX";
// 数据库密码
$password = "XXXXXXXX";

//保持此API密钥值与项目页面中提供的ESP32代码兼容。
//如果您更改此值,则ESP32草图需要匹配
$api_key_value = "tPmAT5Ab3j7F9";

$api_key= $sensor = $location = $value1 = $value2 = $value3 = "";

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    
    $read_post = file_get_contents('php://input');
    $datas=json_decode($read_post, true);

    $api_key = $datas['api_key'];

    if($api_key == $api_key_value) {
        $value1 = $datas['value1'];
        $value2 = $datas['value2'];
        $value3 = $datas['value3'];
        // 创建数据库连接
        $conn = new mysqli($servername, $username, $password, $dbname);
        // 检查数据库连接状态
        if ($conn->connect_error) {
            die("Connection failed: " . $conn->connect_error);
        } 
        //插入一个新的数据行
        $sql = "INSERT INTO tempdata( date1, date2, date3)
        VALUES ('" . $value1 . "', '" . $value2 . "', '" . $value3 . "')";

        //更新表中内容
        //$sql = mysqli_query($conn,"UPDATE SensorData SET value1 = '".$value1."',value2 = '".$value2."'
        //    WHERE sensor='Light'");
        //如果数据修改成功
        if ($conn->query($sql) === TRUE) {
            echo "New record created successfully";
        }  
        else {
            echo "Error: " . $sql . "<br>" . $conn->error;
        }
    
        $conn->close();
    }
    else {
        echo "Wrong API Key provided.<br/>";
    }

}
else {
    echo "No data posted with HTTP POST.<br/>";
}


?>

PHP数据库查询,显示代码:(以下参考为网上获得,未经测试!!!)

<!DOCTYPE html>
<html><body>
<?php


$servername = "localhost";

// 数据库名
$dbname = "bak";
// 数据库用户名
$username = "root";
// 数据库密码
$password = "123456";

// 创建数据库连接
$conn = new mysqli($servername, $username, $password, $dbname);
// 检查数据库连接状态
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
} 

//查询数据库bak  SensorData表中的内容
$sql = "SELECT id, sensor, location, value1, value2, value3, reading_time FROM SensorData ORDER BY id DESC";

echo '<table cellspacing="5" cellpadding="5">
      <tr> 
        <td>ID</td> 
        <td>Sensor</td> 
        <td>Location</td> 
        <td>Value 1</td> 
        <td>Value 2</td>
        <td>Value 3</td> 
        <td>Timestamp</td> 
      </tr>';
 
if ($result = $conn->query($sql)) {
    while ($row = $result->fetch_assoc()) {
        $row_id = $row["id"];
        $row_sensor = $row["sensor"];
        $row_location = $row["location"];
        $row_value1 = $row["value1"];
        $row_value2 = $row["value2"]; 
        $row_value3 = $row["value3"]; 
        $row_reading_time = $row["reading_time"];
      
        echo '<tr> 
                <td>' . $row_id . '</td> 
                <td>' . $row_sensor . '</td> 
                <td>' . $row_location . '</td> 
                <td>' . $row_value1 . '</td> 
                <td>' . $row_value2 . '</td>
                <td>' . $row_value3 . '</td> 
                <td>' . $row_reading_time . '</td> 
              </tr>';
    }
    $result->free();
}

$conn->close();

//<!--JS 页面自动刷新 -->
echo ("<script type=\"text/javascript\">");
echo ("function fresh_page()"); 
echo ("{");
echo ("window.location.reload();");
echo ("}"); 
echo ("setTimeout('fresh_page()',3000);"); //3秒刷新一次
echo ("</script>");

?> 
</table>
</body>
</html>

猜你喜欢

转载自blog.csdn.net/wailaizhu/article/details/106604016