目录
二、在docker容器ubuntu系统内使用C/C++连接宿主机的mysql
一、在ubuntu系统中C/C++连接mysql
1. 更新并升级软件源
apt-get update
apt-get upgrade
2. 安装c/c++解释器
apt-get install g++
安装后,执行 g++ --version 可显示版本信息
3. 安装mysql服务器(若不使用本地数据库,而使用远程数据库,或docker环境下使用宿主机mysql ,可跳过此步)
apt-get install mysql-client mysql-server
安装mysql时,中途会提示你设置用户密码等,请牢记!并在第6步的c++程序中,修改相应的数据库配置变量!顺便新建一个数据库供后面测试:
mysql -u root -p # 进入mysql数据库交互
# 下面在mysql交互下执行
create database mydatabase; # 新建数据库
4. 安装C/C++连接mysql驱动包
apt-get install libmysqlclient-dev
5. 编写C++程序进行测试
vim test.cpp
#include<stdio.h>
#include<mysql/mysql.h>
const char *db_host="localhost";
const char *db_user="root";
const char *db_pass="rootroot";
const char *db_name="mydatabase";
const int db_port=3306;
int main ()
{
MYSQL *mysql=mysql_init(NULL); //初始化数据库连接变量
if(mysql==NULL)
{
printf("Error:%s\n",mysql_error(mysql));
exit(1);
}
mysql = mysql_real_connect(mysql,db_host,db_user,db_pass,db_name, db_port,NULL,0); //连接mysql
if(mysql)
printf("Success!\n"); //连接成功
else printf("Failed!\n");
return 0;
}
6. 编译C++程序并执行
g++ test.cpp -o test -lmysqlclient
注:末尾务必加上 -lmysqlclient 否则编译时找不到mysql库
./test
这是执行程序,如果成功连接mysql,则会显示 Success!
二、在docker容器ubuntu系统内使用C/C++连接宿主机的mysql
最新方法:在版本高于18.03的docker中直接访问 `host.docker.internal` 就可以访问到宿主机!以下可以不看!
注意:请保证宿主机已安装mysql服务器,并开启服务。
1. 编写makefile
FROM ubuntu:16.04
RUN echo "\
deb http://mirrors.163.com/ubuntu/ bionic main restricted universe multiverse\n\
deb http://mirrors.163.com/ubuntu/ bionic-security main restricted universe multiverse\n\
deb http://mirrors.163.com/ubuntu/ bionic-updates main restricted universe multiverse\n\
deb http://mirrors.163.com/ubuntu/ bionic-proposed main restricted universe multiverse\n\
deb http://mirrors.163.com/ubuntu/ bionic-backports main restricted universe multiverse\n\
deb-src http://mirrors.163.com/ubuntu/ bionic main restricted universe multiverse\n\
deb-src http://mirrors.163.com/ubuntu/ bionic-security main restricted universe multiverse\n\
deb-src http://mirrors.163.com/ubuntu/ bionic-updates main restricted universe multiverse\n\
deb-src http://mirrors.163.com/ubuntu/ bionic-proposed main restricted universe multiverse\n\
deb-src http://mirrors.163.com/ubuntu/ bionic-backports main restricted universe multiverse\n" > /etc/apt/sources.list \
&& apt update\
&& apt install -y g++ libmysqlclient-dev
EXPOSE 80
2. 运行makefile,生成镜像(最后有个点。myimage是镜像名字,tag是版本号)
$ docker build -t myimage:tag .
3. 生成容器并进入
docker run -it myimage:tag /bin/bash
4. 使宿主机允许docker容器连接mysql服务
缘由分析: 容器内访问localhost是访问容器本身,访问不到宿主机,故先查宿主机ip。容器内执行 ifconfig 可以查看ip:
图中 172.17.0.2是指容器本身的ip,那么宿主机的ip就是172.17.0.1。所以在docker内访问宿主机的mysql时,就是用这个ip。执行一下 ping 172.17.0.1 或 telnet 172.17.0.1 3306试试能不能访问到宿主机。
若ifconfig和ping命令不识别,请安装插件:
apt install net-tools # ifconfig
apt-get install inetutils-ping # ping
apt-get install telnet # telnet
但是默认情况下,宿主机的mysql只允许本地(localhost)访问,docker容器是拒绝访问的。因此需要修改宿主机mysql用户的权限。
①宿主机是ubuntu系统(宿主机操作)
两步,1.创建允许外网访问的特权用户,2.修改mysql配置文件,使其接受外网连接。
# 第一步,增加特权用户
mysql -u root -p # 进入mysql,回车后需要输入root密码
# 下面在mysql交互里修改root用户的可访问地址
grant all privileges on *.* to 'testuser'@'%' identified by '123456';
flush privileges;
exit;
# testuser是新建的用户名,其密码是123456
# '%' 允许访问数据库的IP地址,%意思是任意IP,也可以指定IP
# flush privileges 刷新权限信息
# 第二步,允许外网连接mysql服务器
vim /etc/mysql/mysql.conf.d/mysqld.cnf
# 找到 bind-address = 127.0.0.1 这一行,注释掉(行首加#)
②宿主机是windows(宿主机操作)
一步:创建允许外网访问的特权用户。
# 请使用管理员权限运行cmd
# 进入到mysql的安装目录里的/bin/目录下,再执行:
.\mysql -uroot -p #进入mysql交互
# 下面在mysql交互里修改root用户的可访问地址
grant all privileges on *.* to 'testuser'@'%' identified by '123456';
flush privileges;
exit;
# testuser是新建的用户名,其密码是123456
# '%' 允许访问数据库的IP地址,%意思是任意IP,也可以指定IP
# flush privileges 刷新权限信息
【未解决的问题】:按说现在可以直接通过ip:172.17.0.1连接宿主机了,但实际测试中总是不行,使用WIFI局域网的ip都连通了,唯独172.17.0.1拒绝访问。恳请知情的大神在评论区指点!
5. 在容器内编写C++程序【关注ip与端口】
#include<stdio.h>
#include<mysql/mysql.h>
const char *db_host="172.17.0.1";
const char *db_user="root";
const char *db_pass="rootroot";
const char *db_name="mydatabase";
const int db_port=3306;
int main ()
{
MYSQL *mysql=mysql_init(NULL); //初始化数据库连接变量
if(mysql==NULL)
{
printf("Error:%s\n",mysql_error(mysql));
exit(1);
}
mysql = mysql_real_connect(mysql,db_host,db_user,db_pass,db_name, db_port,NULL,0); //连接mysql
if(mysql)
printf("Suucess!\n"); //连接成功
else printf("Failed!\n");
return 0;
}
6. 编译C++程序并执行
g++ test.cpp -o test -lmysqlclient
注:末尾务必加上 -lmysqlclient 否则编译时找不到mysql库
./test
这是执行程序,如果成功连接mysql,则会显示 Success!
三、libmysqlclient-dev库常用函数
示例1
#include <iostream>
#include <cstdlib>
#include <string>
#include <vector>
#include <mysql/mysql.h>
using namespace std;
int main(){
const char *host = "localhost";
const char *user = "root";
const char *upwd = "root";
const char *dbs = "test";
unsigned int port = 3306;
MYSQL mydata;
if(0==mysql_library_init(0,NULL,NULL)){
cout<<"mysql_library_init() successed!!"<<endl;
}else{
cout<<"mysql_library_init() failed!!"<<endl;
return -1;
}
//初始化数据结构
if(NULL != mysql_init(&mydata)){
cout<<"初始化数据结构成功!"<<endl;
}else{
cout<<"初始化数据结构失败"<<endl;
return -1;
}
//设置数据库编码类型
if(0==mysql_options(&mydata,MYSQL_SET_CHARSET_NAME,"utf8")){
cout<<"设置数据库编码类型成功!"<<endl;
}else{
cout<<"设置数据库编码类型失败!"<<endl;
return -1;
}
//连接数据库
if(NULL != mysql_real_connect(&mydata,host,user,upwd,dbs,port,NULL,0)){
cout<<"数据库连接成功了!"<<endl;
}else{
cout<<"数据库连接失败了!!"<<endl;
return -1;
}
//插入数据
//string sqlstr;
//sqlstr="INSERT INTO user(`username`, `password`) VALUES ('test', 'test123');";
//if(0==mysql_query(&mydata,sqlstr.c_str())){
// cout<<"插入数据成功"<<endl;
//}else{
// cout<<"插入数据失败了!!"<<endl;
//}
string sqlstr_select = " select * from user;";
//显示查询结果
MYSQL_RES *result=NULL;
if(0==mysql_query(&mydata,sqlstr_select.c_str())){
cout<<"查询成功!"<<endl;
//一次性取得数据集
result =mysql_store_result(&mydata);
//取得并打印行数
my_ulonglong rowcount = mysql_num_rows(result);
cout << "row count: " << rowcount << endl;
//取得并打印各字段的名称
unsigned int fieldcount = mysql_num_fields(result);
MYSQL_FIELD *field = NULL;
for (unsigned int i = 0; i < fieldcount; i++) {
field = mysql_fetch_field_direct(result, i);
cout << field->name << "\t\t";
}
cout << endl;
打印各行
//MYSQL_ROW row = NULL;
//row = mysql_fetch_row(result);
//while (NULL != row) {
// for (int i = 0; i < fieldcount; i++) {
// cout << row[i] << "\t\t";
// }
// cout << endl;
// row = mysql_fetch_row(result);
//}
}else{
cout << "mysql_query() select data failed" << endl;
mysql_close(&mydata);
return -1;
}
mysql_close(&mydata);
mysql_library_end();
return 0;
}
示例2
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string.h>
#include<time.h>
#include<mysql/mysql.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
const char *db_host="192.168.31.16";
const char *db_user="testuser";
const char *db_pass="123456";
const char *db_name="woj";
const int db_port=3306;
static MYSQL *mysql; //数据库连接对象
static MYSQL_RES *mysql_res; //sql查询结果
static MYSQL_ROW mysql_row; //sql查询到的单行数据
char sql[256]; //暂存sql语句
void get_wating_solution(int solution_queue[],int &queueing_cnt) //从solution表读取max_running个待判编号
{
queueing_cnt=0;
sprintf(sql,"SELECT id FROM solution WHERE result<=%d ORDER BY id ASC limit %d",OJ_WT,max_running);
if(mysql_real_query(mysql,sql,strlen(sql))!=0){
printf("select failed!\n");
exit(1);
}
mysql_res=mysql_store_result(mysql); //保存查询结果
char sid_str[max_running*11]="\0";
while(mysql_row=mysql_fetch_row(mysql_res)) //将结果读入判题队列
{
solution_queue[queueing_cnt++]=atoi(mysql_row[0]);
if(sid_str[0]!='\0')strcat(sid_str,",");
strcat(sid_str,mysql_row[0]);
}
if(queueing_cnt>0) //更新已读入的solution的result
{
sprintf(sql,"UPDATE solution SET result=%d WHERE id in (%s)",OJ_QI,sid_str); //更新状态
mysql_real_query(mysql,sql,strlen(sql));
}
}
int main ()
{
mysql = mysql_init(NULL); //初始化数据库连接
mysql = mysql_real_connect(mysql,db_host,db_user,db_pass,db_name, db_port,NULL,0);
if(!mysql){
printf("Error: Can't connect to database!\n\n");
exit(1);
}
static int running_cnt=0,queueing_cnt; //排队数
static int solution_queue[max_running]; //队列
get_wating_solution(solution_queue,queueing_cnt);
mysql_close(mysql);
return 0;
}
3.常用API
mysql_affected_rows() 返回被最新的UPDATE, DELETE或INSERT查询影响的行数。
mysql_close() 关闭一个服务器连接。
mysql_connect() 连接一个MySQL服务器。该函数不推荐;使用mysql_real_connect()代替。
mysql_change_user() 改变在一个打开的连接上的用户和数据库。
mysql_create_db() 创建一个数据库。该函数不推荐;而使用SQL命令CREATE DATABASE。
mysql_data_seek() 在一个查询结果集合中搜寻一任意行。
mysql_debug() 用给定字符串做一个DBUG_PUSH。
mysql_drop_db() 抛弃一个数据库。该函数不推荐;而使用SQL命令DROP DATABASE。
mysql_dump_debug_info() 让服务器将调试信息写入日志文件。
mysql_eof() 确定是否已经读到一个结果集合的最后一行。这功能被反对; mysql_errno()或mysql_error()可以相反被使用。
mysql_errno() 返回最近被调用的MySQL函数的出错编号。
mysql_error() 返回最近被调用的MySQL函数的出错消息。
mysql_escape_string() 用在SQL语句中的字符串的转义特殊字符。
mysql_fetch_field() 返回下一个表字段的类型。
mysql_fetch_field_direct () 返回一个表字段的类型,给出一个字段编号。
mysql_fetch_fields() 返回一个所有字段结构的数组。
mysql_fetch_lengths() 返回当前行中所有列的长度。
mysql_fetch_row() 从结果集合中取得下一行。
mysql_field_seek() 把列光标放在一个指定的列上。
mysql_field_count() 返回最近查询的结果列的数量。
mysql_field_tell() 返回用于最后一个mysql_fetch_field()的字段光标的位置。
mysql_free_result() 释放一个结果集合使用的内存。
mysql_get_client_info() 返回客户版本信息。
mysql_get_host_info() 返回一个描述连接的字符串。
mysql_get_proto_info() 返回连接使用的协议版本。
mysql_get_server_info() 返回服务器版本号。
mysql_info() 返回关于最近执行得查询的信息。
mysql_init() 获得或初始化一个MYSQL结构。
mysql_insert_id() 返回有前一个查询为一个AUTO_INCREMENT列生成的ID。
mysql_kill() 杀死一个给定的线程。
mysql_list_dbs() 返回匹配一个简单的正则表达式的数据库名。
mysql_list_fields() 返回匹配一个简单的正则表达式的列名。
mysql_list_processes() 返回当前服务器线程的一张表。
mysql_list_tables() 返回匹配一个简单的正则表达式的表名。
mysql_num_fields() 返回一个结果集合重的列的数量。
mysql_num_rows() 返回一个结果集合中的行的数量。
mysql_options() 设置对mysql_connect()的连接选项。
mysql_ping() 检查对服务器的连接是否正在工作,必要时重新连接。
mysql_query() 执行指定为一个空结尾的字符串的SQL查询。
mysql_real_connect() 连接一个MySQL服务器。
mysql_real_query() 执行指定为带计数的字符串的SQL查询。
mysql_reload() 告诉服务器重装授权表。
mysql_row_seek() 搜索在结果集合中的行,使用从mysql_row_tell()返回的值。
mysql_row_tell() 返回行光标位置。
mysql_select_db() 连接一个数据库。
mysql_shutdown() 关掉数据库服务器。
mysql_stat() 返回作为字符串的服务器状态。
mysql_store_result() 检索一个完整的结果集合给客户。
mysql_thread_id() 返回当前线程的ID。
mysql_use_result() 初始化一个一行一行地结果集合的检索。