//传输文件的服务器端
//这个程序在服务器端要一直开着,否则客户端连接不了服务器
//客户--->左右软件--->巨澜短信网关
unit uServer;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, IdBaseComponent, IdComponent, IdTCPServer,
ComCtrls,uImpTxt, DB,ADODB,IniFiles;
type
//********************************************************************
//自定义类型
THandle = Integer;
JL_HANDLE = Integer;
PJL_HANDLE = ^JL_HANDLE;
//以HTTP方式登陆返回操作句柄,当调用退出登陆时,此句柄自动失效。// 参数说明
T_JL_TCPLogin = function(strHost: Pchar; // strHost: 主机名称或者IP地址: ‘http://www./ 不用改变
nPort: Integer; // nPort: 端口号 : 6688 不用改变
strUserId: Pchar; // strUserId: 用户名
strPassword: Pchar; // strPassword: 用户密码
nType: Integer; // nType连接类型,为 1 表示只发送, 为 2表示即可发送也可接收 默认为 2
pRtnHandle: PJL_HANDLE): Integer stdcall; // pRtnHandle: 返回操作句柄
//退出登陆,此句柄自动失效
T_JL_Logout = function(pHandle: JL_HANDLE): Integer stdcall; // pRtnHandle: 登陆时的操作句柄
//群发短信息
T_JL_SendMsg = function(pHandle: JL_HANDLE; //操作句柄
strFromPhone: Pchar; // strFromPhone 短消息发送者手机号码
strToPhone: Pchar; // strToPhone 目的手机号码,如果有多个号码,用逗号进行分割,如13900000000, 13911111111, ...
strContent: Pchar; // strContent 信息内容,信息长度不超过140字节,否则将返回 JL_ERR_CONTENT
strSendTime: Pchar): Integer stdcall; // strSendTime 定时发送时间。格式:yyyy-mm-dd hh:mm:ss 为空时,表示立即发送
//单发短信息
T_JL_SingleSendMsg = function(pHandle: JL_HANDLE; //操作句柄
strSerNo: PChar; //自定义编号-以对应相应的状态报告
strFromPhone: Pchar; // strFromPhone 短消息发送者手机号码
strToPhone: Pchar; // strToPhone 目的手机号码,如果有多个号码,用逗号进行分割,如13900000000, 13911111111, ...
strContent: Pchar; // strContent 信息内容,信息长度不超过140字节,否则将返回 JL_ERR_CONTENT
strSendTime: Pchar): Integer stdcall; // strSendTime 定时发送时间。格式:yyyy-mm-dd hh:mm:ss 为空时,表示立即发送
//查询帐户余额 -参数:fBalance:返回金额,单位为元
T_JL_GetAccountBalance = function(pHandle: JL_HANDLE; fBalance: PSingle): Integer stdcall;
// 查询单价 -参数:fPrice:返回金额,单位为元
T_JL_GetAccountPrice = function(Handle: JL_HANDLE; fPrice: PSingle): Integer stdcall;
// 修改用户密码
T_JL_ModifyPassword = function(pHandle: JL_HANDLE; // 操作句柄
strNewPassword: Pchar): Integer stdcall; //strNewPassword:新的密码
// 接收短信返回的状态报告 pstrPhone [21] pstrDescribe[255]
T_JL_GetOneReport = function(pHandle: JL_HANDLE; // 操作句柄
pstrSerNo: PChar; //用户自定义序列(单发时用户传过去的参数)
pnReqID: PInteger; //返回MO短信ID
pstrPhone: Pchar; //字符串缓冲区,大小至少为21字节,返回接收手机号码
pnStatus: PInteger; // 返回短信息接收状态 0:发送失败 1:信息已发送到网关 3:信息已发送到手机
pstrDescribe: Pchar): Integer stdcall; //字符串缓冲区,大小至少为256字节,返回状态描述
T_JL_GetOneSM = function(pHandle: JL_HANDLE; // 操作句柄
pnMsgID: PInteger; //信息id
strFrom: Pchar; //返回发送者的手机号码 字符串缓冲区,大小至少为21字节
strCreateTime: Pchar; // 返回发送时间 字符串缓冲区,大小至少为20字节
strContent: Pchar): Integer stdcall; //返回消息内容 字符串缓冲区,大小至少为256字节。
T_JL_GetOneSM_Ex = function(pHandle: JL_HANDLE; // 操作句柄
pnMsgID: PInteger; //信息id
strFrom: Pchar; //返回发送者的手机号码 字符串缓冲区,大小至少为21字节
strTo: Pchar; //字符串缓冲区,大小至少为21字节,返回来源端口号
strCreateTime: Pchar; // 返回发送时间 字符串缓冲区,大小至少为20字节
strContent: Pchar): Integer stdcall; //返回消息内容 字符串缓冲区,大小至少为256字节。
//********************************************************************
type
TFormServer = class(TForm)
IdTCPServer1: TIdTCPServer;
BtnStart: TButton;
StatusBar1: TStatusBar;
BtnStop: TButton;
LabeledEdit1: TLabeledEdit;
ListBox1: TListBox;
ADOQuery1: TADOQuery;
ADOQuery2: TADOQuery;
ADOConnection1: TADOConnection;
ADOQuery3: TADOQuery;
ADOQuery4: TADOQuery;
procedure IdTCPServer1Execute(AThread: TIdPeerThread);
procedure BtnStartClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure BtnStopClick(Sender: TObject);
procedure IdTCPServer1Connect(AThread: TIdPeerThread);
procedure ShowResultMsg(num: Integer);
private
{ Private declarations }
public
{ Public declarations }
OneHandle: THandle; //定义一个句柄变量
pRtnHandle: JL_HANDLE; //定义一个句柄变量
JL_TCPLogin: T_JL_TCPLogin; //登陆
JL_Logout: T_JL_Logout; //退出
JL_GetAccountPrice: T_JL_GetAccountPrice; //单价/条
JL_GetAccountBalance: T_JL_GetAccountBalance; //帐户余额
JL_ModifyPassword: T_JL_ModifyPassword; //修改密码
JL_GetOneSM: T_JL_GetOneSM; //收取回复信息(HTTP)
JL_GetOneSM_Ex: T_JL_GetOneSM_Ex; //收取回复信息(TCP)
JL_GetOneReport: T_JL_GetOneReport; //收取状态报告
JL_SingleSendMsg: T_JL_SingleSendMsg; //单发信息
JL_SendMsg: T_JL_SendMsg; // 群发信息
end;
var
FormServer: TFormServer;
const
FFileName=‘d:\sms.txt‘; //参数有待放进INI文件中
FPort=9925;
FUserName=‘‘; //登录巨澜短信网关用户名和密码
FPWS=‘‘;
implementation
uses uCommFunc;
{$R *.dfm}
//------返回错误列表--------------------------------------------------
procedure tformserver.ShowResultMsg(num: Integer);
begin
case num of
0: MessageBox(Handle, ‘正常!‘, ‘Surge‘, MB_ICONASTERISK);
-1: MessageBox(Handle, ‘EPID错误‘, ‘Surge‘, MB_ICONHAND);
-2: MessageBox(Handle, ‘无该用户‘, ‘Surge‘, MB_ICONHAND);
-3: MessageBox(Handle, ‘注册码错‘, ‘Surge‘, MB_ICONHAND);
-4: MessageBox(Handle, ‘用户被停用‘, ‘Surge‘, MB_ICONHAND);
-5: MessageBox(Handle, ‘未注册成功‘, ‘Surge‘, MB_ICONHAND);
-6: MessageBox(Handle, ‘超出使用日期‘, ‘Surge‘, MB_ICONHAND);
-7: MessageBox(Handle, ‘费用不足‘, ‘Surge‘, MB_ICONHAND);
-8: MessageBox(Handle, ‘源手机错误‘, ‘Surge‘, MB_ICONHAND);
-9: MessageBox(Handle, ‘目的手机错误‘, ‘Surge‘, MB_ICONHAND);
-10: MessageBox(Handle, ‘信息内容错误‘, ‘Surge‘, MB_ICONHAND);
-11: MessageBox(Handle, ‘连接失败‘, ‘Surge‘, MB_ICONHAND);
-12: MessageBox(Handle, ‘系统内部错误或者无效的客户状态‘, ‘Surge‘, MB_ICONHAND);
-13: MessageBox(Handle, ‘客户权限不对‘, ‘Surge‘, MB_ICONHAND);
-14: MessageBox(Handle, ‘不是从指定的IP处登录‘, ‘Surge‘, MB_ICONHAND);
-15: MessageBox(Handle, ‘账号已经登录(TCP)‘, ‘Surge‘, MB_ICONHAND);
-16: MessageBox(Handle, ‘内部通讯错误‘, ‘Surge‘, MB_ICONHAND);
-17: MessageBox(Handle, ‘无可用的MT通道‘, ‘Surge‘, MB_ICONHAND);
-18: MessageBox(Handle, ‘不支持该功能‘, ‘Surge‘, MB_ICONHAND);
-19: MessageBox(Handle, ‘未定义错误‘, ‘Surge‘, MB_ICONHAND); //-------------未定义错误
-20: MessageBox(Handle, ‘未知错误‘, ‘Surge‘, MB_ICONHAND);
-21: MessageBox(Handle, ‘请求过于频繁‘, ‘Surge‘, MB_ICONHAND);
-22: MessageBox(Handle, ‘信息内容有非法关键字‘, ‘Surge‘, MB_ICONHAND);
-23: MessageBox(Handle, ‘错误的产品ID‘, ‘Surge‘, MB_ICONHAND);
-24: MessageBox(Handle, ‘发送失败-Agent 断线‘, ‘Surge‘, MB_ICONHAND);
-25: MessageBox(Handle, ‘发送失败-SMSC 断线‘, ‘Surge‘, MB_ICONHAND);
-26: MessageBox(Handle, ‘发送失败-Agent 与 SMSC 之间断线‘, ‘Surge‘, MB_ICONHAND);
-27: MessageBox(Handle, ‘错误的指令或格式‘, ‘Surge‘, MB_ICONHAND);
else
MessageBox(Handle, ‘未知错误‘, ‘Surge‘, MB_ICONHAND);
end;
end;
procedure TFormServer.IdTCPServer1Execute(AThread: TIdPeerThread);
//const
// c_sql=‘select * from sms_msg_server ‘+ //status:0/1/2 待发/成功/失败
// ‘ where status=0 or status=2 ‘; //待发和曾经发送失败的都要发
var
AFileStream: TFileStream;
cmd:string;
DllPaht: string; // DLL的地址
rtn: Integer; //巨澜的函数是否执行成功
mtel:string; //手机号码
content:string; //短信内容
SendTimes:Integer;//发送次数
i:Integer; //单条发送次数
iSuccess:Integer; //发送成功条数
price:Double; //短信单价
money:Double; //发送短信金额
INIFile:TIniFile;
MyCs:TRTLCriticalSection;//临界区
procedure UpdateStatusDate; //更新状态时间(不论成功或者失败)
begin
with ADOQuery3 do
begin
Close;
sql.Clear;
SQL.Text:=‘update sms_msg_server set status_date=:statusdate ‘+
‘ mtel:=:mtel and ‘+
‘ create_date=:createdate ‘; //create_date是唯一的
Parameters.ParamByName(‘status_date‘).Value:=Now;
Parameters.ParamByName(‘mtel‘).Value:=ADOQuery2.fieldbyname(‘mtel‘).AsString;
Parameters.ParamByName(‘create_date‘).Value:=ADOQuery2.fieldbyname(‘create_date‘).AsDateTime;
ExecSQL;
end;
end;
begin
InitializeCriticalSection(MyCs);//初始化临界区
with AThread.Connection do
begin
cmd := UpperCase(ReadLn);
if cmd=‘BEGIN‘ then
begin
EnterCriticalSection(MyCs); //进入临界区,从流生成文本文件,以及将该文本文件写进数据表这一段要使用临界区保护
try
AFileStream := TFileStream.Create(FFileName, fmCreate);//建立文件流准备接收
try
ReadStream(AFileStream, -1, true); //读入全部直到结束connect
Application.ProcessMessages;
finally
AFileStream.Free; //释放文件流
end;
if not TxtToDataset(ADOQuery1,FFileName,‘,‘) then //将文本文件写入数据库
Application.MessageBox(‘文本写入数据库失败‘, ‘错误‘, MB_OK +
MB_ICONSTOP);
finally
LeaveCriticalSection(MyCs); //离开临界区
end;
//将短信发送给巨澜短信网关
//登录巨澜
if (OneHandle=0) or (pRtnHandle=0) then begin //未登录
DllPaht := ExtractFilePath(Application.ExeName) + ‘\JL_ISP.dll‘; //获得DLL的地址
OneHandle := LoadLibrary(Pchar(DllPaht)); //动态载入DLL,并返回其句柄
try
if OneHandle <> 0 then //如果载入DLL成功则获取DLL函数的地址
begin
@JL_TCPLogin := GetProcAddress(OneHandle, ‘JL_TCPLogin‘);
@JL_Logout := GetProcAddress(OneHandle, ‘JL_Logout‘);
@JL_GetAccountPrice := GetProcAddress(OneHandle, ‘JL_GetAccountPrice‘);
@JL_GetAccountBalance := GetProcAddress(OneHandle, ‘JL_GetAccountBalance‘);
@JL_ModifyPassword := GetProcAddress(OneHandle, ‘JL_ModifyPassword‘);
@JL_GetOneReport := GetProcAddress(OneHandle, ‘JL_GetOneReport‘);
@JL_GetOneSM := GetProcAddress(OneHandle, ‘JL_GetOneSM‘);
@JL_GetOneSM_Ex := GetProcAddress(OneHandle, ‘JL_GetOneSM_Ex‘);
@JL_SingleSendMsg := GetProcAddress(OneHandle, ‘JL_SingleSendMsg‘);
@JL_SendMsg := GetProcAddress(OneHandle, ‘JL_SendMsg‘);
end;
if not (@JL_TCPLogin = nil) then //登录巨澜服务器
begin
rtn := JL_TCPLogin( //函数定义见Type部分。
‘http://www./
6688,
Pchar(fusername),
Pchar(fpws),
2,
@pRtnHandle);
ShowResultMsg(rtn);
if rtn <> 0 then
begin
pRtnHandle := 0;
OneHandle := 0;
end;
end
else
RaiseLastOSError; //报错
except
MessageBox(Handle, ‘系统严重错误,请重新登陆!!‘, ‘Surge‘, MB_ICONASTERISK);
end;
end;
//单条短信循环发送
with ADOQuery2 do
begin
Close;
SQL.Clear;
SQL.Text:=‘select * from sms_msg_server ‘+ //status:0/1/2 待发/成功/失败
‘ where status=0 or status=2 ‘; //发送待发和发送失败的短信
Open;
end;
if ADOQuery2.IsEmpty then exit;
iSuccess:=0; //初始化发送成功短信条数变量
ADOQuery2.First;
while not ADOQuery2.Eof do
begin
//发送前要检查客户的剩余短信条数,等于0则不允许发送,跳转下一条记录
with ADOQuery4 do
begin
Close;
SQL.Clear;
SQL.Text:=‘ select user_leftamounts from sms_user ‘+
‘ where user_name=:username ‘;
Parameters.ParamByName(‘user_name‘).Value:=ADOQuery2.fieldbyname(‘shop_name‘).AsString;
Open;
end;
if ADOQuery4.FieldByName(‘user_leftamounts‘).Value<=0 then //帐户余额不足
begin
Application.MessageBox(PChar(ADOQuery2.fieldbyname(‘shop_name‘).AsString+‘的余额已不足‘),
‘警告‘, MB_OK + MB_ICONWARNING);
exit;
end;
mtel:=ADOQuery2.fieldbyname(‘mtel‘).AsString; //目标手机号
content:=ADOQuery2.fieldbyname(‘content‘).AsString; //短信内容
if (pRtnHandle = 0) or (OneHandle = 0) then //判断是否登陆
begin
MessageBox(Handle, ‘你还没有登陆!‘, ‘Surge‘, MB_ICONASTERISK);
exit;
end;
SendTimes:=ADOQuery2.fieldbyname(‘send_times‘).AsInteger; //短信发送次数
for i:=1 to sendtimes do
try
rtn := JL_SingleSendMsg(pRtnHandle, //函数定义见Type部分。
PChar(IntToStr(i)), //自定义编号-以对应相应的状态报告,每次发送应传入不同的值,用以对应收取到的状态报告
‘‘, //扩展端口号
PChar(Trim(mtel)), //群发时号码用逗号分隔,TCP协议一次群发最多50个号码。HTTP协议一次群发最多100个号码。
PChar(Trim(content)), //内容长度为按 Unicode characters 类型计算 ,总长度不可以超过70个字(包含签名)
‘‘); //发送时间,暂不支持!
if rtn = 0 then //发送成功
begin
Inc(iSuccess); //累加发送成功条数
ADOQuery3.Connection.BeginTrans; //开始事务
//计算发送费用
INIFile:=TIniFile.Create(GetINIFile);
try
price:=INIFile.ReadFloat(‘server‘,‘price‘,0.6); //获取短信的单价
finally
INIFile.Free;
end;
money:=iSuccess*price; //发送条数*单价
with ADOQuery3 do //更新用户余额,剩余条数
begin
Close;
SQL.Clear;
sql.Text:=‘update sms_user ‘+
‘ set user_leftmoney=user_leftmoney-:money, ‘+
‘ user_leftamounts=user_leftamounts-:amounts ‘+
‘ where user_name=:username ‘;
Parameters.ParamByName(‘user_leftmoney‘).Value:=money;
Parameters.ParamByName(‘user_leftamounts‘).Value:=iSuccess;
Parameters.ParamByName(‘user_name‘).Value:=ADOQuery2.fieldbyname(‘shop_name‘).AsString;
ExecSQL;
end;
with ADOQuery3 do //更新状态为发送成功
begin
Close;
sql.Clear;
SQL.Text:=‘update sms_msg_server set status=1 ‘+
‘ where status=0 and ‘+
‘ mtel:=:mtel and ‘+
‘ create_date=:createdata ‘;
Parameters.ParamByName(‘mtel‘).Value:=ADOQuery2.fieldbyname(‘mtel‘).AsString;
Parameters.ParamByName(‘create_date‘).Value:=ADOQuery2.fieldbyname(‘create_date‘).AsDateTime;
ExecSQL;
end;
UpdateStatusDate; //更新状态时间
try
ADOQuery3.Connection.CommitTrans; //提交事务
except
ADOQuery3.Connection.RollbackTrans; //回滚事务
end;
MessageBox(Handle, ‘成功发送!‘, ‘Surge‘, MB_ICONASTERISK);
end
else //发送失败
begin
ADOQuery3.Connection.BeginTrans; //开始事务
with ADOQuery3 do
begin
Close;
sql.Clear;
SQL.Text:=‘update sms_msg_server set status=2 ‘+ //更新状态为发送失败
‘ where status=0 and ‘+
‘ mtel:=:mtel and ‘+
‘ create_date=:createdate ‘;
Parameters.ParamByName(‘mtel‘).Value:=ADOQuery2.fieldbyname(‘mtel‘).AsString;
Parameters.ParamByName(‘create_date‘).Value:=ADOQuery2.fieldbyname(‘create_date‘).AsDateTime;
ExecSQL;
end;
UpdateStatusDate; //更新状态时间
try
ADOQuery3.Connection.CommitTrans; //提交事务
except
ADOQuery3.Connection.RollbackTrans; //回滚事务
end;
ShowResultMsg(rtn); //通过函数的返回值显示相应信息
end;
except
on e:exception do
MessageBox(Handle, PChar(e.Message) , ‘Surge‘, MB_ICONASTERISK);
//‘系统严重错误,请重新登陆!!‘
end;
ADOQuery2.Next; //发送下一条短信
end;
end;
end;
DeleteCriticalSection(MyCs); //删除临界界
end;
procedure TFormServer.BtnStartClick(Sender: TObject); //启动SOCKET服务端
begin
IdTCPServer1.DefaultPort := strtoint(LabeledEdit1.Text); //端口
if not IdTCPServer1.Active then
try
IdTCPServer1.Active := True;
ListBox1.Items.Add(‘已启动‘);
except
ListBox1.Items.Add(‘启动失败‘);
end;
end;
procedure TFormServer.FormClose(Sender: TObject; var Action: TCloseAction);
begin
IdTCPServer1.Active:=False;
end;
procedure TFormServer.BtnStopClick(Sender: TObject);
begin
IdTCPServer1.Active:=False;
ListBox1.Items.Add(‘已停止‘);
end;
procedure TFormServer.IdTCPServer1Connect(AThread: TIdPeerThread);
begin
ListBox1.Items.Add(‘来自 ‘
+ AThread.Connection.Socket.Binding.PeerIP
+ ‘ 的连接请求已被允许‘);
AThread.Connection.WriteLn(‘欢迎连接到左右软件短信服务器‘);
end;
end.