ch395怎么配置,一个客户端连接多个服务器

ch395怎么配置,一个客户端连接多个服务器,第1张

修改IP地址、设置目标服务器IP和端口和连接目标服务器。
1、将ch395的IP地址修改为与目标服务器所在网络相同的IP地址段。
2、将ch395的IP地址修改为与目标服务器所在网络相同的IP地址段。
3、配置完成后,可以使用AT+TCPCONNECT指令连接到目标服务器。

SQLD-IBDLL 包含InterBase驱动程序和支持文件的动态链接库SQLINT32DLL
INTRBASE驱动类型 加在配置工具的驱动管理程序中以配置基本的Borland InterBase SQL Link驱动程序
INTRBASE别名类型 加入配置文件的别名以使建立联接SQL服务器数据库的别名
SQLD_IBHLP 配置InterBase驱动程序的帮助文件
READLINKTXT Borland SQL Links for Windows自述文件
INTERBASMSG InterBase消息文件,通常安装上C:\INTERBASCONNECTEXE 测试工作站和InterBase服务器连接情况的工具
REMOTEDLL、 InterBase的支持动态链接库
GDSDLL、GDS32DLL
将InterBase服务器描述 安装过程修改工作站的SERVICES文件以增加用于加入SERVICES文件 InterBase 服务器访问所需的协议描述 ,
如:gds- db3050/tcp
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
软件项目中还应包括TCP/IP接口软件
下表列出的文件给InterBase客户端应用提供访问Winsock11的接口
表188 TCP/IP接口软件
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
文 件 名 描 述
————————————————————————————————
MVWASYNCEXE 异步通信模块
VSLINT TCP/1P传输初始化文件
WINSOCKDLLWindows Socket动态链接库
MSOCKLIBDLL 将Windows Socket调用映射到VSL驱动程序
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
如果TCP/IP产品不是Winsock11兼容,InterBase客户端应用将也可采用其它TCP/1P 驱动程序。InterBase服务器还可支持其它通信协议,如SPX/1PX,NetBeIU等。
4 解决一般的联接问题
如果用SQL Links建立与InterBase服务器的连接有问题可采用下列步骤来分离问题原因:
⑴ 通过Windows ISQL工具测试能否与InterBase服务器联接如果成功,状态信息会出现,并继续步骤⑵。如果不成功,询问数据库管理员。
⑵ 检验InterBase SQL Links 驱动程序是否正确安装。
⑶ 重新安装SQL Links。
⑷ 检查SERVICES,文件中应有行:
gds_db 3050/tcp
如果不能正确安装,就请询问数据库管理员,否则继续步骤⑸。
⑸ 测试底层协议
① 输入TELNET命令,确认TCP库是否正确安装。
如果TCP库正确安装,注册提示符会出现。注册入网检查数据库是否存在。
如果消息是“can't resolve hostname” 出现,检查工作站的HOSTS文件是否有你 的主机名和IP地址的人口。如:1281275012 mis_server
如果用TELNET是成功的,但仍然无法正确联接,则没有正确安装InterBase。请寻求数据库管理员的帮助。
② PING到服务器服务器上,测试InterBase服务器是否正常运行并且为桌面应用可见(如果PING是成功的,消息“servername isalive”被显示)。PING成功但TELNET不成功,则inet daemon可能有问题。如果PING到服务器上不成功,则有网络路径问题,将问题报给网络管理员。
如果底层协议不正常,请询问数据库管理员,否则继续帮助⑹。
⑹ 确认是否有InterBase服务器的访问权,如果有请继续步骤⑺。
⑺ 检查BDE应用程序的InterBase别名是否正确安装。
如果能够直接从工作站上联接,但不能从BDE应用程序中,那么很有可能你的
IDAPI32CFG别名设置有问题。运行BDE配置工具检查InterBase别名。
183 Delphi Client/Server编程
本节介绍如何运用Delphi可视化开发工具和ObjectPascal语言开发Client\Server的数据库应用程序,采用的例子是CSDEMOS。这是Delphi20自带的演示Client\Server开发的例子,它安装在C:\Program Files\Borland\Delphi 20\Demos\DB\CSDemos中(缺省安装)。
本节将包含以下内容:
● 使用TDatabase部件连接SQL服务器
● 用DataSet部件(又称数据集部件),如TTable和TQuery,联接TDatabase部件并访问数据库以及各种表之间如何切换
● 使用数据库连接
● 触发器的使用方法
● TStoredProc部件的使用方法
● 客户和服务器之间的事务控制
● TStoredProc部件的使用方法
1831 使用TDatabase部件联接SQL服务器
18311 TDatabase部件概述
TDatabase部件处理应用程序与单个数据库的联接。如果不需要控制数据库联接,可以不用创建TDatabase部件。当应用程序试图打开数据库表(Table)时,会自动创建一个临时的TDatabase部件。但如果你想控制数据库的持续联接、进入数据库服务器的注册和数据库别名的值或事务控制,那么你就必须为每个所需的联接创建一个TDatabase部件。
1 创建TDatabase部件
TDatabase 部件在Component Palette中的Data Access页上,你能将其拖放在数据模块(Data module)或窗体中。在设计时创建TDatabase 部件,用户可以设置初始值和编写OnLogin事件处理过程(Event Handle)。OnLogin事件给用户提供了第一次注册数据库服务器时定制服务器安全参数,如口令,的能力。
2 TDatabase的关键属性
⑴ DatabaseName属性
DatabaseName是所要联接的数据库名,并且用于DataSet软件,它将出现在DataSet部件的DatabaseName属性的下拉式列表框中。设置DataBaseName属性是定义数据库应用的特定别名。DataSet部件能引用该名字以取代直接使用BDE别名。当TDatabase部件的Connected属性为True时不能修改该属性。
⑵ AliaName属性
AliasName是BDE配置工具定义的BDE别名的名字。TDatabase 从中获取其缺省的设置。如果设置DriveName属性,则该属性将被清除,如果当Connected为True时强行设置DriveName属性将引发异常。
⑶ DriveName属性
DriveName是BDE驱动程序,如STANDARD 、ORACLE、SYBASE、INFORMIX或INTERBASE的名字。如果设置AliasName,则该属性值将被清除。
⑷ Params属性
Params属性包含了打开SQL服务器上数据库时所需的参数。在缺省情况下,这些参数由BDE配置工具设置;用户也可以用数据库参数编辑器(Database Parameters Editor)修改这些参数。对于数据库服务器而言,Params将描述一系列的参数,如服务器名、 数据库名、用户名和口令。
⑸ Connected属性
Connected属性指明是否建立数据库的联接,当应用程序打开数据库中的一个表时Connected将被置为True;反之,关闭数据库表,Connected将被置为False,除非KeepConnection为True。而将Connected置为True则可不需打开数据库表即可建立数据库联接。TDatabase的KeepConnection属性描述当数据库中没有表打开时是否维持数据库联连。
⑹ KeepConnection属性
KeepConnection属性描述当数据库中没有打开表时是否要保持与服务器的联连, 如果数据库应用需要打开和关闭单个数据库中的多个表时,将KeepConnection 设置为True是很有用的,那样,即使没有打开任何表,应用仍能保持与数据库的联接,它能够重复地打开和关闭数据库表,而不需要重复执行联接过程。如果KeepConnection置为False,当每次将Connected置为True,数据库都必须执行注册过程。
⑺ LoginPrompt属性
LoginPrompt属性用于控制如何处理SQL数据库的安全性问题。如置为True,当应用程序试图建立数据库联接时屏幕上将出现标准Delphi注册对话框。用户必须输入正确的用户名和口令。如果置为False,则应用程序将寻找TDatabase部件的Params 属性中的注册参数。下面是USERNAME和PASSWORD参数的例子:
USERNAME = SYSDBA
PASSWORD = masterkey
⑻ TransIsolation属性
TransIsolation属性描述SQL服务器所有的事务控制独立级别。 tiDirtyRead使所有修改都被返回,而不管记录是否已被提交。tiReadCommitted将只返回提交的记录,而提交的修改将不会在结果中反映出来。tiRepeatableRead 将只返回事务过程中最初的记录,即使另一个应用程序将所作的修改提交。
各种数据库服务器可能不同程度地支持这些独立级别,或者根本不支持。 如果需要的独立级别不被服务器支持,那么Dephi将使用下一个更高的独立级别,如下表所示:
表1810 各类服务器TransIsolation设置
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
独立级别 Oracle Sybase和 InterBase
Microsoft SQL
——————————————————————————————————————
Dirty Read Read Committed Read Committed Read Committed
Read Committed Read Committed Read Committed Read Committed
Repeatable read Repeatable read Not Supported Repeatable Read
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
各个独立级别的含义请见表1812。
3 TDatabase的关键方法
⑴ StartTransaction方法
StartTransartion方法在由TaransIsolation属性指定的独立级别下开始事务控制。如果在一个事务已被激活的情况下调用该方法,Delphi将引发异常。
调用了该方法后,对数据库所做的修改一直由数据库服务器维持到调用Commmit方法提交数据或调用Rollback方法取消修改为止。只有当联接数据库服务器时,才能调用该方法。
⑵ Rollback方法
Rollback方法返转当前事务控制,并且取消自最近一次调用StartTransaction以来对数据库所做的所有修改。
⑶ Commit方法
Commit方法提交当前事务控制,并且将自最近一次调用StartTransaction以来所有数据修改存入数据库。
4 TDatabase的OnLogin事件的处理
OnLogin事件的触发条件是当联接SQL数据库的TDatabase部件被打开并且LoginPrompt属性为True。使用OnLogin事件处理过程可以在运行时设置注册参数。OnLogin 事件处理过程得到TDatabase的注册参数数组Params,并且使用Values属性改变这些参数。
例如:
LoginParamsVaiues['SERVERNAME'] := 'MYSERVERNAME';
LoginParamsValues['USER NAME'] := 'MYUSERNAME';
LoginParamsValues[PASSWORD'] := 'MYAPSSWORD';
当控制从OnLogin事件处理过程中返回时,应用程序用这些参数来建立联接。
OnLogin事件处理过程的声明是这样的:
TLoginEvent = procedure(Database: TDatabase;LoginParam: TStrings) of Object;
property OnLogin: TLoginEvent;
TLoginEvent类型是处理OnLogin事件的方法头。Database参数是要联接的数据库。LoginParams是TStrings类型的对象,包含用户名和口令,以及打开数据库时所用的其它参数。用户名是形如USER NAME = JohnDoe的字符串,口令是形如PASSWORD = is_Password的字符串。当OnLogin事件处理过程被调用时应当在LoginParams中加入用户名和口令。
18312 定制数据库服务器的注册参数
大多数数据库服务器都包含限制数据库访问的安全特征。通常,在用户能访问数据库之前,服务器都要求注册的用户名和口令。
如果服务器需要注册,在设计阶段,Delphi 会在你试图联接时提示你,诸如在会TTable部件描述数据库表名时。
在缺省情况下,Delphi应用在打开数据库服务器的联接时,显示标准注册对话框。如果联接已建立,则注册对话框不会出现。
可以用下列方法处理服务器注册:
1 将TDatabase部件的LoginPrompt属性置为True。这样,当应用程序试图建立数据库联接时,标准注册对话框会打开。
2 将LoginPrompt属性置为False,在TDatabase部件的Params属性中包含用户名和口令参数。例如:
USERNAME = SYSDBA
PASSWORD = mosterkey
但不推荐使用该方法,因为这会危害数据库安全
3 使用TDatabase部件的OnLogin事件设置注册参数。OnLogin事件得到TDatabase 注册参数数组的拷贝,并利用Values属性改变这些参数。如:
LoginParamsValues['SERVER NAME']:= 'MYSERVERNAME';
LoginParamsValues['USER NAME'] :='MYUERNAME';
LoginParamsValues['PASSWORD'] := 'MYPASSWORD';
当控制从数据库注册事件处理过程中返回时,这些参数被用来建立联接。
18313 建立应用程序特定的别名
TDatabase的Aliases描述了数据库表的位置和数据库服务器的联接参数。通常都是在Delphi之外,运用BDE配置工具(BDECFG32EXE)创建别名,并且别名被存在BDE 配置文件IDAPI32CFG中。
用户也可以用TDatabase创建只在应用程序中可用的别名,用TDatabase创建的别名不会加进BDE配置文件中。任何DataSet部件可通过描述DatabaseName 属性来使用这些别名。为了定制这些局部别名的参数,用鼠标左键双击TDatabase部件或从TDatabase部件中选择Database Editor,Delphi就会打开数据库属性编辑器(Database Properties Editor)。
这只是其中一部分
更多的请点网址 >

有两种类型:(Tcp协议与Udp协议)

流式Socket(STREAM):

是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全,但是效率低;Tcp:是以流的形式来传的。

数据报式Socket(DATAGRAM):

是一种无连接的Socket,对应于无连接的UDP服务应用不安全(丢失,顺序混乱,在接收端要分析重排及要求重发),但效率高Udp:将数据包拆开为若干份编号后来传输。在传输的过程中容易出现数据的丢失。但是传输速度要比TCP的快。

Socket的通信流程

服务器端:

– 申请一个socket (socketWatch)用来监听的

– 绑定到一个IP地址和一个端口上

– 开启侦听,等待接授客户端的连接

– 当有连接时创建一个用于和连接进来的客户端进行通信的socket(socketConnection)

– 即续监听,等侍下一个客户的连接

代码如下:

using System;
using SystemCollectionsGeneric;
using SystemComponentModel;
using SystemData;
using SystemDrawing;
using SystemLinq;
using SystemText;
using SystemWindowsForms;
using SystemNet;//IPAdress,IPEndPoint(ip和端口)类
using SystemNetSockets;
using SystemThreading;
using SystemIO;
namespace MyChatRoomServer
{
public partial class FChatServer : Form
{
public FChatServer()
{
InitializeComponent();
TextBoxCheckForIllegalCrossThreadCalls = false;//关闭 对 文本框  的跨线程 *** 作检查
}
Thread threadWatch = null;//负责监听 客户端 连接请求的 线程
Socket socketWatch = null;//负责监听的 套接字
private void btnBeginListen_Click(object sender, EventArgs e)
{
//创建 服务端 负责监听的 套接字,参数(使用IP4寻址协议,使用流式连接,使用TCP协议传输数据)
socketWatch = new Socket(AddressFamilyInterNetwork, SocketTypeStream, ProtocolTypeTcp);
//获得文本框中的 IP地址对象
IPAddress address = IPAddressParse(txtIPTextTrim());
//创建 包含 ip 和 port 的网络节点对象
IPEndPoint endpoint = new IPEndPoint(address, intParse(txtPortTextTrim()));
//将 负责监听 的套接字 绑定到 唯一的IP和端口上
socketWatchBind(endpoint);
//设置监听队列的长度
socketWatchListen(10);
//创建 负责监听的线程,并传入监听方法
threadWatch = new Thread(WatchConnecting);
threadWatchIsBackground = true;//设置为后台线程
threadWatchStart();//开启线程
ShowMsg("服务器启动监听成功~");
//IPEndPoint
//socketWatchBind(
}
//保存了服务器端 所有负责和客户端通信的套接字
Dictionary<string, Socket> dict = new Dictionary<string, Socket>();
//保存了服务器端 所有负责调用 通信套接字Receive方法 的线程
Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>();
//Socket sokConnection = null;
/// <summary>
/// 监听客户端请求的方法
/// </summary>
void WatchConnecting()
{
while (true)//持续不断的监听新的客户端的连接请求
{
//开始监听 客户端 连接请求,注意:Accept方法,会阻断当前的线程!
Socket sokConnection = socketWatchAccept();//一旦监听到客户端的请求,就返回一个负责和该客户端通信的套接字 sokConnection
//sokConnectionReceive
//向 列表控件中 添加一个 客户端的ip端口字符串,作为客户端的唯一标识
lbOnlineItemsAdd(sokConnectionRemoteEndPointToString());
//将 与客户端通信的 套接字对象 sokConnection 添加到 键值对集合中,并以客户端IP端口作为键
dictAdd(sokConnectionRemoteEndPointToString(), sokConnection);
//创建 通信线程
ParameterizedThreadStart pts = new ParameterizedThreadStart(RecMsg);
Thread thr = new Thread(pts);
thrIsBackground = true;//设置为
thrStart(sokConnection);//启动线程 并为线程要调用的方法RecMsg 传入参数sokConnection
dictThreadAdd(sokConnectionRemoteEndPointToString(), thr);//将线程 保存在 字典里,方便大家以后做“踢人”功能的时候用
ShowMsg("客户端连接成功!" + sokConnectionRemoteEndPointToString());
//sokConnectionRemoteEndPoint 中保存的是 当前连接客户端的 Ip和端口
}
}
/// <summary>
/// 服务端 负责监听 客户端 发送来的数据的 方法
/// </summary>
void RecMsg(object socketClientPara)
{
Socket socketClient = socketClientPara as Socket;
while (true)
{
//定义一个 接收用的 缓存区(2M字节数组)
byte[] arrMsgRec = new byte[1024 1024 2];
//将接收到的数据 存入 arrMsgRec 数组,并返回 真正接收到的数据 的长度
int length=-1;
try
{
length = socketClientReceive(arrMsgRec);
}
catch (SocketException ex)
{
ShowMsg("异常:" + exMessage);
//从 通信套接字 集合中 删除 被中断连接的 通信套接字对象
dictRemove(socketClientRemoteEndPointToString());
//从 通信线程    结合中 删除 被终端连接的 通信线程对象
dictThreadRemove(socketClientRemoteEndPointToString());
//从 列表中 移除 被中断的连接 ip:Port
lbOnlineItemsRemove(socketClientRemoteEndPointToString());
break;
}
catch (Exception ex)
{
ShowMsg("异常:" + exMessage);
break;
}
if (arrMsgRec[0] == 0)//判断 发送过来的数据 的第一个元素是 0,则代表发送来的是 文字数据
{
//此时 是将 数组 所有的元素 都转成字符串,而真正接收到的 只有服务端发来的几个字符
string strMsgRec = SystemTextEncodingUTF8GetString(arrMsgRec,1, length-1);
ShowMsg(strMsgRec);
}
else if (arrMsgRec[0] == 1)//如果是1 ,则代表发送过来的是 文件数据(/视频/文件)
{
SaveFileDialog sfd = new SaveFileDialog();//保存文件选择框对象
if (sfdShowDialog() == SystemWindowsFormsDialogResultOK)//用户选择文件路径后
{
string fileSavePath = sfdFileName;//获得要保存的文件路径
//创建文件流,然后 让文件流来 根据路径 创建一个文件
using (FileStream fs = new FileStream(fileSavePath, FileModeCreate))
{
fsWrite(arrMsgRec, 1, length-1);
ShowMsg("文件保存成功:" + fileSavePath);
}
}
}
}
}
//发送消息到客户端
private void btnSend_Click(object sender, EventArgs e)
{
if (stringIsNullOrEmpty(lbOnlineText))
{
MessageBoxShow("请选择要发送的好友");
}
else
{
string strMsg = txtMsgSendTextTrim();
//将要发送的字符串 转成 utf8对应的字节数组
byte[] arrMsg = SystemTextEncodingUTF8GetBytes(strMsg);
//获得列表中 选中的KEY
string strClientKey = lbOnlineText;
//通过key,找到 字典集合中对应的 与某个客户端通信的 套接字的 send方法,发送数据给对方
try
{
dict[strClientKey]Send(arrMsg);
//sokConnectionSend(arrMsg);
ShowMsg("发送了数据出去:" + strMsg);
}
catch (SocketException ex)
{
ShowMsg("发送时异常:"+exMessage);
}
catch (Exception ex)
{
ShowMsg("发送时异常:" + exMessage);
}
}
}
//服务端群发消息
private void btnSendToAll_Click(object sender, EventArgs e)
{
string strMsg = txtMsgSendTextTrim();
//将要发送的字符串 转成 utf8对应的字节数组
byte[] arrMsg = SystemTextEncodingUTF8GetBytes(strMsg);
foreach (Socket s in dictValues)
{
sSend(arrMsg);
}
ShowMsg("群发完毕!:)");
}
#region 显示消息
/// <summary>
/// 显示消息
/// </summary>
/// <param name="msg"></param>
void ShowMsg(string msg)
{

客户端:

– 申请一个socket(socketClient)

– 连接服务器(指明IP地址和端口号)

代码如下:

using System;
using SystemCollectionsGeneric;
using SystemComponentModel;
using SystemData;
using SystemDrawing;
using SystemLinq;
using SystemText;
using SystemWindowsForms;
using SystemNetSockets;
using SystemNet;
using SystemThreading;
namespace MyChatRoomClient
{
public partial class FChatClient : Form
{
public FChatClient()
{
InitializeComponent();
TextBoxCheckForIllegalCrossThreadCalls = false;
}
Thread threadClient = null; //客户端 负责 接收 服务端发来的数据消息的线程
Socket socketClient = null;//客户端套接字
//客户端发送连接请求到服务器
private void btnConnect_Click(object sender, EventArgs e)
{
IPAddress address = IPAddressParse(txtIPTextTrim());//获得IP
IPEndPoint endpoint = new IPEndPoint(address, intParse(txtPortTextTrim()));//网络节点
//创建客户端套接字
socketClient = new Socket(AddressFamilyInterNetwork, SocketTypeStream, ProtocolTypeTcp);
//向 指定的IP和端口 发送连接请求
socketClientConnect(endpoint);
//客户端 创建线程 监听服务端 发来的消息
threadClient = new Thread(RecMsg);
threadClientIsBackground = true;
threadClientStart();
}
/// <summary>
/// 监听服务端 发来的消息
/// </summary>
void RecMsg()
{
while (true)
{
//定义一个 接收用的 缓存区(2M字节数组)
byte[] arrMsgRec = new byte[1024 1024 2];
//将接收到的数据 存入 arrMsgRec 数组,并返回 真正接收到的数据 的长度
int length=  socketClientReceive(arrMsgRec);
//此时 是将 数组 所有的元素 都转成字符串,而真正接收到的 只有服务端发来的几个字符
string strMsgRec = SystemTextEncodingUTF8GetString(arrMsgRec, 0, length);
ShowMsg(strMsgRec);
}
}
void ShowMsg(string msg)
{
txtMsgAppendText(msg + "\r\n");
}
}
}
通信过程图

客户端与服务器端之间的一个基本通信流程,概括一下Socket 一般应用模式(客户端和服务器端)的作用:

服务器端:最少有两个socket,一个是服务端负责监听客户端发来连接请求,但不负责与请求的客户端通信,另一个是每当服务器端成功接收到客户端时,但在服务器端创建一个用与请求的客户端进行通信的socket

客户端:指定要连接的服务器端地址和端口,通过创建一个socket对象来初始化一个到服务器端的TCP连接。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/zz/13376814.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-07-24
下一篇 2023-07-24

发表评论

登录后才能评论

评论列表(0条)

保存