关于Mac应用连接sql server的问题

概述:MacOS10.14,使用freetds库的C语言代码连接sql server远程数据库,插入中文数据,编码不匹配。

freetds库官网http://www.freetds.org,下载freetds库=freetds-0.91.tar.gz

进入目录usr/local下新建文件夹用于存放freetds。
解压安装包,在解压后的目录里改配置

root# ./configure --prefix=/usr/local/freetds --enable-msdblib --with-tdsver=7.1

在解压后的目录里安装

make

然后
make install,查看配置文件如下

sudo vi /usr/local/freetds/etc/freetds.conf

正文:

[global]
        # TDS protocol version
;	tds version = 4.2

	# Whether to write a TDSDUMP file for diagnostic purposes
	# (setting this to /tmp is insecure on a multi-user system)
;	dump file = /tmp/freetds.log
;	debug flags = 0xffff

	# Command and connection timeouts
;	timeout = 10
;	connect timeout = 10
	
	# If you get out-of-memory errors, it may mean that your client
	# is trying to allocate a huge buffer for a TEXT field.  
	# Try setting 'text size' to a more reasonable limit 
	text size = 64512
#client charest = GBK

# A typical Sybase server
[egServer50]
	host = symachine.domain.com
	port = 5000
	tds version = 5.0

# A typical Microsoft server
[egServer70]
	host = ntmachine.domain.com
	port = 1433
	tds version = 7.0
#client charest = GREEK
[v70]
	host = cd.vtede.com
	port = 1433
	tds version = 7.0
# charset = ISO-8859-1

———————————————————————————

数据库表:

create table Temp(
	id bigint not null,
	flag bit,
	ChatMsg ntext,
	ChatTime datetime,
	IpAddress varchar(50),
	UserName nVarchar(50),
);

数据库排序规则:
使用sql语句查询

SELECT
    name,
    collation_name
FROM sys.databases
WHERE name = N’interview0710’;

查询结果:

interview0710,SQL_Latin1_General_CP1_CI_AS

使用sql语句

SELECT SERVERPROPERTY(N'Collation’)

查询结果:

1

———————————————————————————
终端使用tsql连接远程sql server 数据库,已知该数据库地址,端口号,一个可登陆的用户名和对应密码

正常插入:

在终端输入命令tsql -H xxx.com -p 1433 -U username -P password
(1433是该远程数据库默认端口号)
连接到数据库

locale is "C/UTF-8/C/C/C/C"
locale charset is "UTF-8"
using default charset "UTF-8"
1> 

结果说明freetds正常连接到sql server,然后使用sql语句插入数据

1> insert into [interview0710].[dbo].[Temp](id,flag,ChatMsg,ChatTime,IpAddress,UserName) values(139,NULL,N'中文','2018-12-03 08:47:52','192.168.1.212',N'中文')
2> go

在数据库中查看结果

139,NULL,中文,2018-12-03 08:47:52:000,192.168.1.212,中文

结果正确。

———————————————————————————

在Xcode中新建工程,
TARGETS-BuildSettings添加头文件路径

HEADER_SEARCH_PATHS = /Users/wc.zeng/Downloads/freetds-0.91\(jb51.net\)/freetds-0.91/include 

添加库文件路径

LIBRARY_SEARCH_PATHS = /usr/local/lib /Users/wc.zeng/Downloads/freetds-0.91\(jb51.net\)/freetds-0.91/src/dblib

添加静态库连接

OTHER_LDFLAGS = -l sybdb

freetds库的API文档https://sites.ualberta.ca/dept/aict/uts/software/openbsd/ports/4.6/i386/obj/freetds-0.82.20110223/freetds-0.82/doc/reference/index.html

代码参考网友博客
https://www.cnblogs.com/fnlingnzb-learner/p/5970556.html

在AppDelegate.m文件中
需要包含头文件和定义一些宏

#include <stdio.h>
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <unistd.h>
#import <sqlfront.h> /* sqlfront.h always comes first */
#import "sybdb.h" /* sybdb.h is the only other file you need */
#define SQLDBIP "xxx.com" //SQL数据库服务器IP
#define SQLDBPORT "1433" //SQL数据库服务器端口
#define SQLDBNAME "username" //SQL数据库服务器数据库名
#define SQLDBUSER "username" //SQL数据库服务器数据库用户名
#define SQLDBPASSWD "password" //SQL数据库服务器用户密码
#define SQLDBSERVER SQLDBIP":"SQLDBPORT

为一个按钮添加动作,使它可以连接sql server数据库并写入数据
在-(IBAction)Send:(id)sender里添加以下C代码

-(IBAction)Send:(id)sender
{
    //写入远程数据库
    int i, ch;
    LOGINREC *login;//描述客户端的结构体,在连接时被传递到服务器.
    DBPROCESS *dbproc;
    RETCODE erc; //库函数中最普遍的返回类型.
    
NSString *sendchatmsg=[sendmessage stringValue];//从插座变量里获取字符串
NSString *myip=[ip_address stringValue];

/*************************************************************/
//在开始调用本库函数前常常要先调用dbinit()函数
if (dbinit() == FAIL) {
    exit(1);
}
//dblogin()函数申请 LOGINREC 结构体,此结构体被传递给dbopen()函数,用来创建一个连接。
//虽然基本上不会调用失败,但是检查它!
if ((login = dblogin()) == NULL) {
    exit(1);
}
//LOGINREC结构体不能被直接访问,要通过以下宏设置,下面设置两个必不可少的域
DBSETLUSER(login, SQLDBUSER);
DBSETLPWD(login, SQLDBPASSWD);
/*************************************************************/

//dbopen()与服务器建立一个连接. 传递 LOGINREC 指针和服务器名字
if ((dbproc = dbopen(login, SQLDBSERVER)) == NULL) {
    exit(1);
}
// 可以调用dbuser()函数选择我们使用的数据库名,可以省略,省略后使用用户默认数据库.
if (SQLDBNAME && (erc = dbuse(dbproc, SQLDBNAME)) == FAIL) {
    exit(1);
}

//获取当前时间
NSDate *datenow = [NSDate date];//现在时间
NSString *timeSp = [NSString stringWithFormat:@"%d", (long)[datenow timeIntervalSince1970]];
//NSLog(@"timeSp:%@",timeSp); //时间戳的值时间戳转时间的方法
NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSString*strDate = [dateFormatter stringFromDate:[NSDate date]];
//NSLog(@"%@",strDate);

NSStringEncoding enc=CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatin1);
//NSString转换成ISOLatin1编码
//sendchatmsg=[NSString stringWithCString:[sendchatmsg UTF8String] encoding:enc];//能把NSString转换成IOSLatin1编码格式
NSLog(@"sendchatmsg:%@",sendchatmsg);

//拼接sql插入语句
char str1[256];
strcpy(str1,"insert into [interview0710].[dbo].[Temp](id,flag,ChatMsg,ChatTime,IpAddress,UserName) values(3,NULL,N'");
//strcat(str1, [sendchatmsg cStringUsingEncoding:NSUTF8StringEncoding]);
strcat(str1, [sendchatmsg cStringUsingEncoding:NSUTF8StringEncoding]);
printf("now str1=%s\n",str1);
strcat(str1,"','");
strcat(str1, [strDate cStringUsingEncoding:NSUTF8StringEncoding]);
strcat(str1,"','");
strcat(str1, [myip cStringUsingEncoding:NSUTF8StringEncoding]);
strcat(str1,"',N'");
strcat(str1,[sendchatmsg cStringUsingEncoding:NSUTF8StringEncoding]);
strcat(str1,"')");
printf("final str1=%s\n",str1);

const char *resultCString = strcat(str1,"");

/*
if ([stringOBJ canBeConvertedToEncoding:NSUTF8StringEncoding]) {
    resultCString = [stringOBJ cStringUsingEncoding:NSUTF8StringEncoding];
}*/

printf("c string is:%s",resultCString);

//[testrecv setString:stringOBJ];//textfield cell显示聊天记录失败

dbcmd(dbproc,resultCString);//装载sql语句

printf("\n");
if ((dbsqlexec(dbproc)) == FAIL) {
    exit(1); //等待服务器执行SQL语句,等待时间取决于查询的复杂度。
}

//在调用dbsqlexec()、dbsqlok()、dbrpcsend()返回成功之后调用dbresults()函数
printf("then fetch results:\n");
int count = 0;
while ((erc = dbresults(dbproc)) != NO_MORE_RESULTS) {
struct col { //保存列的所有信息
    char *name; //列名字
    char *buffer; //存放列数据指针
    int type, size, status;
} *columns, *pcol;
int ncols;
int row_code;
if (erc == FAIL) {
    exit(1);
}
ncols = dbnumcols(dbproc);//返回执行结果的列数目
if ((columns = calloc(ncols, sizeof(struct col))) == NULL) {
    perror(NULL);
    exit(1);
}
/* read metadata and bind. */
for (pcol = columns;pcol - columns < ncols;pcol++) {
    int c = (int)(pcol - columns + 1);
    pcol->name = dbcolname(dbproc, c); //返回指定列的列名
    pcol->type = dbcoltype(dbproc, c);
    pcol->size = dbcollen(dbproc, c);
    printf("%*s(%d)", 20, pcol->name, pcol->size);
    if ((pcol->buffer = calloc(1, 20)) == NULL) {
        perror(NULL);
        exit(1);
    }
    erc = dbbind(dbproc, c, NTBSTRINGBIND, 20, (BYTE*)pcol->buffer);
    if (erc == FAIL) {
        exit(1);
    }
    erc = dbnullbind(dbproc, c, &pcol->status); //(5)
    if (erc == FAIL) {
        exit(1);
    }
}
printf("\n");
/* 打印数据 */
while ((row_code = dbnextrow(dbproc)) != NO_MORE_ROWS) {//读取行数据
    switch (row_code) {
        case REG_ROW:
            for (pcol = columns; pcol - columns < ncols; pcol++) {
                char *buffer = pcol->status == -1 ?
                "null" : pcol->buffer;
                printf("%*s ", 20, buffer);
            }
            printf("\n");
            break;
        case BUF_FULL:
            break;
        case FAIL:
            exit(1);
            break;
        default:
            printf("data for computeid %d ignored\n", row_code);
    }
}
/* free metadata and data buffers */
for (pcol = columns; pcol - columns < ncols; pcol++) {
    free(pcol->buffer);
}
free(columns);
if (DBCOUNT(dbproc) > -1) /* 得到SQL语句影响的行数 */
    fprintf(stderr, "%d rows affected\n", DBCOUNT(dbproc));
}
dbclose(dbproc);
dbexit();
//\\
NSLog(@"%@",[sendmessage stringValue]);
NSData *data=[[sendmessage stringValue] dataUsingEncoding:NSUTF8StringEncoding];
[Socket writeData:data withTimeout:-1 tag:0];
}

为工程添加库路径
运行,点击send按钮,输出结果

2018-12-03 10:18:31.342076+0800 WindowSkip[2471:244406] sendchatmsg:中文
now str1=insert into [interview0710].[dbo].[Temp](id,flag,ChatMsg,ChatTime,IpAddress,UserName) values(3,NULL,N'中文
final str1=insert into [interview0710].[dbo].[Temp](id,flag,ChatMsg,ChatTime,IpAddress,UserName) values(3,NULL,N'中文','2018-12-03 10:18:31','192.168.1.212',N'中文')
c string is:insert into [interview0710].[dbo].[Temp](id,flag,ChatMsg,ChatTime,IpAddress,UserName) values(3,NULL,N'中文','2018-12-03 10:18:31','192.168.1.212',N'中文')
then fetch results:

1 rows affected

然后在数据库中查询

3,NULL,中文,2018-12-03 10:18:31:000,192.168.1.212,中文

无法显示出”中文“,登录http://www.mytju.com/classcode/tools/messyCodeRecover.asp查询乱码

这个时候,查Google,百度。
查dbgetcharset()获取字符集设置;
跟踪sql server语句执行情况。

最后,在.conf文件设置一下clientcharset=utf8解决了问题。

补充:数据库插入语句中有特殊符号,使用带参数的dbfcmd填充命令

dbfcmd(dbproc,"insert into [interview0710].[dbo].[Temp](ChatMsg,ChatTime,IpAddress,UserName) values(N'%s','%s',N'%s',N'%s')",[sendchatmsg cStringUsingEncoding:NSUTF8StringEncoding],[strDate cStringUsingEncoding:NSUTF8StringEncoding],[myip cStringUsingEncoding:NSUTF8StringEncoding],[sendchatmsg cStringUsingEncoding:NSUTF8StringEncoding]);

更多函数请看
https://www.cnblogs.com/fnlingnzb-learner/p/5835477.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值