indy 10tcp通信
- 格式:doc
- 大小:49.00 KB
- 文档页数:13
tcp ip 通讯步骤
1. 数据封装:应用程序将要发送的数据转换为TCP/IP 协议栈可以理解的格式。
数据被封装成TCP 或UDP 数据包。
2. 寻址:TCP 或UDP 数据包包含源和目标IP 地址,以及源和目标端口号。
这些地址用于将数据包路由到正确的目的地。
3. 路由选择:根据数据包的目标IP 地址,网络设备(如路由器)通过查找路由表来确定将数据包发送到目的地的最佳路径。
4. 传输:数据包通过网络传输到目的地。
在传输过程中,数据包可能经过多个网络设备,如路由器和交换机。
5. 解封装:在目标设备上,数据包被解封装,TCP 或UDP 头被移除,数据被传递给相应的应用程序。
6. 确认与重传:TCP 协议通过发送确认(ACK)消息来确认数据包的成功接收。
如果数据包未被成功接收,发送方会重传数据包。
7. 流量控制:TCP 协议通过滑动窗口机制来控制数据发送的速率,以避免缓冲区溢出和网络拥塞。
8. 连接关闭:通信完成后,TCP 连接可以通过发送关闭(FIN)消息来关闭。
tcp协议通讯机制
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP通讯机制主要包括以下三个阶段:1.连接建立阶段:TCP协议在两个端点之间建立连接的过程。
在连接建立阶
段,客户端向服务器发送一个SYN报文,表示请求连接。
服务器收到SYN 报文后,回复一个SYN+ACK报文,表示接受连接请求。
客户端再回复一个ACK报文,表示连接建立成功。
此时,双方之间的连接已经建立完成。
2.数据传输阶段:在连接建立后,TCP协议进行数据传输。
在数据传输过程
中,TCP协议将数据分成若干个数据包进行传输。
每个数据包包含一个序号和一个确认号,用于保证数据传输的顺序和可靠性。
接收方收到数据包后,需要发送一个ACK报文进行确认。
3.连接释放阶段:数据传输完成后,进入连接释放阶段。
发送方发送一个FIN
报文,表示请求释放连接。
接收方收到FIN报文后,回复一个ACK报文,确认连接释放。
然后接收方再发送一个FIN报文请求释放连接,发送方回复一个ACK报文进行确认。
最后,接收方发送一个FIN报文请求释放连接,发送方回复一个ACK报文进行确认。
完成以上步骤后,连接释放阶段结束。
TCP协议通过这种方式保证了数据传输的可靠性和顺序性,以及控制了网络拥塞等问题。
indy10 开发通用tcpserver例子Indy是一个流行的Delphi/C++Builder 网络编程库,它提供了一组组件和类来简化各种网络应用的开发,包括TCP/IP、UDP、HTTP、FTP、SMTP、POP3 等协议。
以下是一个使用Indy 的TIdTCPServer组件来开发一个通用TCP 服务器的简单例子。
这个例子使用Delphi 语言,但Indy 也支持C++Builder。
首先,确保你已经安装了Indy 库。
在Delphi 中,你可以通过包管理器来安装Indy。
然后,你可以使用以下代码来创建一个基本的TCP 服务器:delphiunit Unit1;interfaceusesWindows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, IdTCPServer, IdContext, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPServerThreads;typeTForm1 = class(TForm)IdTCPServer1: TIdTCPServer;procedure IdTCPServer1Connect(AContext: TIdContext);procedure IdTCPServer1Disconnect(AContext: TIdContext);procedure IdTCPServer1Execute(AContext: TIdContext);private{ Private declarations }public{ Public declarations }end;varForm1: TForm1;implementation{$R *.dfm}procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);begin// 当一个客户端连接到服务器时调用此方法ShowMessage('Client connected.');end;procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);begin// 当一个客户端断开连接时调用此方法ShowMessage('Client disconnected.');end;procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);varData: TBytes;Size: Integer;begin// 当从客户端接收到数据时调用此方法// 这里简单地将接收到的数据回发给客户端trySize := AContext.Connection.IOHandler.InputStream.Read(Data,SizeOf(Data));if Size > 0thenbeginAContext.Connection.IOHandler.Write(Data);end;excepton E: Exception dobegin// 处理异常ShowMessage(E.Message);end;end;end;end.在这个例子中,我们创建了一个TIdTCPServer实例,并设置了三个事件处理程序:•OnConnect:当一个客户端连接到服务器时调用。
tcp校准10点法TCP校准是网络中常用的一项技术,主要用于调整TCP协议的参数,以提高网络传输的性能和稳定性。
本文将介绍TCP 校准的10个关键点,包括窗口大小、拥塞控制、延迟优化等等,并且对每个关键点进行详细的解释和分析。
1. 窗口大小:窗口大小是TCP连接中的一个重要参数,它表示能够发送的数据量。
调整窗口大小能够提高网络吞吐量,减少延迟。
根据当前的网络状况和带宽情况,合理设置窗口大小是很重要的。
2. 拥塞控制:TCP采用了拥塞控制算法来避免网络拥塞的发生。
根据拥塞窗口的大小调整发送数据的速率,避免造成网络拥塞。
可以通过调整拥塞窗口大小、快速重传等方式来优化拥塞控制算法。
3. 延迟优化:TCP协议在传输数据时会引入一定的延迟,而对于一些实时性要求较高的应用来说,这种延迟可能会影响用户体验。
通过优化TCP协议的超时机制、丢包重传等,可以减少延迟,提高应用的实时性。
4. 慢启动:TCP连接的开始阶段会采用慢启动模式,逐渐增加拥塞窗口大小以测试网络的性能。
可以通过调整慢启动的阈值、拥塞窗口的大小来减少慢启动对网络性能的影响。
5. 带宽延迟积(BDP):BDP是指网络中可以存放的最大数据量,由带宽和延迟共同决定。
合理设置BDP可以提高网络的吞吐量和传输性能。
6. 传输速率控制:根据当前的网络状况和带宽情况,调整发送数据的速率,避免网络拥塞。
可以通过动态调整发送窗口大小、调整数据包的重传机制来控制传输速率。
7. 超时重传:超时重传是TCP协议中一种处理丢包的机制,通过在一定时间内没有收到确认信息时进行重传。
可以通过调整超时时间和丢包重传机制来提高重传的效率,减少重传所消耗的网络带宽。
8. 拥塞避免:拥塞避免算法是TCP协议中的一个重要机制,通过控制发送窗口大小和拥塞窗口大小来避免网络拥塞的发生。
可以通过调整拥塞窗口的增加速率、拥塞窗口的减少速率等来优化拥塞避免算法。
9. TCP/IP协议栈优化:除了调整TCP协议本身的参数外,还可以通过对整个TCP/IP协议栈进行优化来提高网络传输性能。
indy10tcp通信在Delphi 2007中使用Indy10的TCP连接首先先说明下为什么要用 INDY10最新的indy10可以基于win32上的纤程(Fiber) API.什么叫Fiber API呢,这里是解释:纤程(Fiber) —可以从 32 位版本的 Windows? 中使用的轻量级线程处理对象—在很多方案中都很有用。
由于线程是宝贵资源,因此您有时不希望将整个OS 线程专门用于执行简单的任务。
通过纤程,可以比线程更严密地控制任务的调度,因为是您而不是OS 负责管理它们。
由于它们具有较少的开销,因此当您切换上下文时,它们还更加快速。
此外,因为是由您控制纤程,所以对于它们而言,通常可以更容易地跟踪同步问题。
不过这个特性,现在只有针对delphi7有用。
端口重叠可以让你的服务器承担更多的用户。
indy10值得一用。
indy10支持完成端口和纤程,性能有了巨大提升!====================================== ==========================================DelPhi2007 中Indy 升级到了10 而其代码的操作方式也改了很多,很多网友包括本人也是在网络上找了很多资料都没有一个很满意的答案!没办法也得自己亲自调试亲自写了!先来侃侃客户端程序!因为这个程序毕竟真的很难找到资料了所以为了大家很好的使用上这个控件我在这里是一边做一边填代码了!我们先打开 DelPhi2007 工具吧!首先我们做好一个简单的客户端先新建一个窗口程序拖入一个TCP客户端控件还有3个按钮一个文本框是连接断开和发生设置一下 IdTCPClient 控件的属性Host :127.0.0.1Post:3000下面我们来对连接按钮做事件procedure TForm6.ConetClick(Sender: TObject);begintryif not (IdTCPClient1.Connected) thenIdTCPClient1.Connect;ShowMessage('连接成功');exceptShowMessage('连接失败');end;end;接着我们来做一下服务端的程序先新建一个窗口程序拖入一个TCP服务端控件两个按钮以及一个TMemo用来显示信息Bindings 0.0.0.0:3000DefaultPort 3000我们在“启动服务” 按钮上的事件procedure TForm6.Button1Click(Sender: TObject);beginIdTCPServer1.Active:= true;end;启动时只要将其Active设置为 true 既启动了服务而关闭则同样设置为False接下来我们要对 IdTCPServer1 的 OnExecute 事件做处理!选择控件 EVENTS 栏双击OnExecute在这里代码我们暂时这样写procedure TForm6.IdTCPServer1Execute(AContext: TIdContext);exit;end;TIdContext 需要uses IdContext好到这里运行下服务器和客户端然后启动服务器和连接服务器好已经可以连接得上了吧!但是因为我们在服务器监听的部分退出了所以并没有保持着连接现在我们修改一下代码吧我们把OnExecute 代码修改如下procedure TForm6.IdTCPServer1Execute(AContext: TIdContext);varSwp:String;begintryAContext.Connection.IOHandler.CheckForDisconnect(True, True);Swp:=AContext.Connection.IOHandler.ReadLn();Memo1.Lines.Add(Swp) ;finallyend;end;我们对客户端也修改一下procedure TForm6.ConetClick(Sender: TObject);begintryif not (IdTCPClient1.Connected) thenbeginIdTCPClient1.Connect;IdTCPClient1.IOHandler.writeln('lianjie');ShowMessage('连接成功');exceptShowMessage('连接失败');end;end;在运行测试一下当按下连接按钮后服务器上的文本框里加入了一行'lianjie' 字符串而其再次点击连接已经无效而刚刚每次点击一次都会提示一次连接成功仔细看代码就发现在连接的时候判断了是否已经连接了如果已经保持连接了哪么就不会在做下面的代码!从而可知现在的连接已经是保持着的了!那好我们来发个信息看下是否真的可以连接了在发送按钮上的事件procedure TForm6.SendClick(Sender: TObject);varStr:String;beginStr:=Edit1.Text;if(IdTCPClient1.Connected) thenIdTCPClient1.IOHandler.writeln(Str);end;好我们来测试一下是不是连接以后真的可以向服务器发送数据了呢?看到了吧!是不是可以发送数据了!我们已经做到了客户端能正常的连接服务器并且向服务器发送内容了!大家可以多开几个客户端然后连接服务器!并发送内容!在这里可能你会问到!服务器怎么样区别数据到底是哪一个发送过来的呢,或者服务器如何对其回复数据呢!~ 那么!我们现在就先针对回复对应的客户端发送过来的数据!已经客户端接受并显示服务器反馈回来的数据!我们修改服务器上的OnExecute代码如下!procedure TForm6.IdTCPServer1Execute(AContext: TIdContext);varSwp:String;begintryAContext.Connection.IOHandler.CheckForDisconnect(True, True);Swp:=AContext.Connection.IOHandler.ReadLn();if(Swp<>'')thenAContext.Connection.IOHandler.WriteLn('服务器已经收到您发来的信息:'+Swp);Memo1.Lines.Add(Swp) ;finallyend;end;在客户端里我们加入一个TMemo用来接受服务器发来的数据信息!然后我们在来看客户端的代码!在连接和发送按钮上的事件修改为procedure TForm6.ConetClick(Sender: TObject);begintryif not (IdTCPClient1.Connected) thenbeginIdTCPClient1.Connect;IdTCPClient1.IOHandler.writeln('lianjie');Str:=IdTCPClient1.IOHandler.ReadLn();Memo1.Lines.Add(Str);ShowMessage('连接成功');end;exceptShowMessage('连接失败');end;end;procedure TForm6.SendClick(Sender: TObject);varStr:String;beginStr:=Edit1.Text;if(IdTCPClient1.Connected) thenIdTCPClient1.IOHandler.writeln(Str);tryStr:=IdTCPClient1.IOHandler.ReadLn();Memo1.Lines.Add(Str);finallyend;end;我们编译后打开多个客户端进行测试就会发现对不同客户端服务器会分别的响应并对其回复内容互不干扰!做到这里大家也知道客户端如果要发送一条数据才能相应的去读取一条数据!可能有些人会想到利用定时器对数据进行定时读取!~ 这样也是一个办法!但是在程序操作中由于数据太快而没有及时读取就会出现数据丢失掉了!那我们要用什么方法才能很好的对数据进行准确读取呢!在这里我使用了线程!启用一个线程利用一个死循环对数据进行读取!一旦有数据就读取出来并放在一个StringList 里供我们使用!好我们一步步的来实现!我们先来做一全局变量的定义新建一全局变量页面 MainUnit.pas 我们先声明两个全局变量代码如下unit MainUnit;interfaceuses Classes,SyncObjs;varM_Lock : TCriticalSection;M_MsgList:TStringList;implementationend.然后我们在主程序的窗口创建事件里创建这两个对象procedure TForm6.FormCreate(Sender: TObject);beginM_MsgList:=TStringList.Create;M_Lock :=TCriticalSection.Create;end;接下来我们把这个页面引用到程序中以及线程代码中线程页面MyThread.pas 代码如下unit MyThread;interfaceusesClasses,SysUtils,Forms,Windows,Variants,idIOHandler,MainUnit; typeTMainThread = class(TThread)privateprotectedprocedure Foo;procedure Execute;Override;publicConstructor Create(Suspended:Boolean);end;implementationuses Client;Constructor TMainThread.Create(Suspended:Boolean);//创建线程Begininherited Create(Suspended);FreeOnTerminate:=True;End;procedure TMainThread.Foo;varMsg:string;bool: boolean;beginbool:=true;while bool do begintryMsg:= Form6.IdTCPClient1.IOHandler.ReadLn;if(Msg='') thenbool:=falseelsebeginM_Lock.Enter;M_MsgList.Add(Msg);M_Lock.Leave;end;exceptbool:=false;end;end;end;Procedure TMainThread.Execute;//线程启动beginFoo;End;End.线程做好了哪么我们在程序里进行使用线程吧!首先当然是要在程序中引用MyThread 启动的代码如下连接按钮事件在连接的时候启动线程procedure TForm6.ConetClick(Sender: TObject);begintryif not (IdTCPClient1.Connected) thenbeginIdTCPClient1.Connect;TMainThread.Create(false);IdTCPClient1.IOHandler.writeln('lianjie');ShowMessage('连接成功');end;exceptShowMessage('连接失败');end;end;相应的我们把发送的读取部分也去掉所有读取全部交给线程去处理!procedure TForm6.SendClick(Sender: TObject);varStr:String;beginStr:=Edit1.Text;if(IdTCPClient1.Connected) thenIdTCPClient1.IOHandler.writeln(Str);end;这里线程读取的内容我们全部都放入了StringList 是因为在我们操作界面时可能会出现访问不安全的现象!因为在服务器发送过来的消息里可能有一些是自己定义的执行的命令这些命令可能会直接操作主窗口的一些事件!而在线程里直接操作某些控件是不安全的!所以我们还是先把所有数据放到StringList 里!如果是其他的2进制你可以放入LIST 或者ObjectList里!好下一步就是要把StringList 里的数据读取出来并显示在 Memo1 里了!在这里我是用一个定时器对StringList 进行检查的!加入一个记时器设置时间为1毫秒!我们设置它活动的状态就放在TCP客户端控件的OnConnected事件里!Enabled FalseInterval 1procedure TForm6.IdTCPClient1Connected(Sender: TObject);beginTimer1.Enabled:=true;end;停止活动哦事件放在TCP客户端控件的OnDisconnected事件里!procedure TForm6.IdTCPClient1Disconnected(Sender: TObject);beginTimer1.Enabled:=false;end;然后我们在事件响应函数里这样做procedure TForm6.Timer1Timer(Sender: TObject);varMsg:String;beginM_Lock.Enter;while M_MsgList.Count > 0 dobeginMsg:='';Msg := M_MsgList[0];M_MsgList.Delete(0);if(Msg<>'')thenMemo1.Lines.Add(Msg);end;M_Lock.Leave;end;我们再来运行下看一下效果吧!效果和刚刚的基本一样!但是唯一不同的一点就在于!客户端可以在任何一个时候接受来自服务器的数据!而非主动发送数据而只能单次获取!而且使用了StringList 你完全可以在这里安全的执行相应的事件或函数!不会对线程接受数据的操作有任何影响!好到这里客户端既然能主动发送数据到服务器并且也能接受到服务器的反馈了!但是大家注意到没有!如果服务器想对客户端主动发送数据好像是不可以的!因为在服务端里都是只有响应与其对话的那个客户端的IdTCPServer1Execute 事件里才能有反应!也才能对这个用户发送数据!下面我们来做一下服务端如何对所有用户发送广播信息!在服务器上添加一按钮为广播以及一个文本输入框!在按钮时间里我们的代码如下procedure TForm6.Button3Click(Sender: TObject);varcList : TList;Count : Integer;Str:String;beginStr:=Edit1.Text;trycList := IdTCPServer1.Contexts.LockList;for Count := 0 to cList.Count -1 dobeginTIdContext(cList[Count]).Connection.IOHandler.WriteLn(Str);end;finallyIdTCPServer1.Contexts.UnlockList; //一定要解锁否则将会造成死锁end;end;好了我们编译好客户端多开几个来测试结果吧!怎么样服务器可以主动给所有连接的用户发送数据了吧!如果是按照我们之前的客户端没有使用随时准备着接收那么就不会接受到服务器的广播数据了或者接收到的数据不够准确!前面章节已经介绍了 Indy10 的基本通讯!而且也实现了客户端的发送和接收数据!~ 以及服务端的广播信息!现在我们就接着来做服务端如何针对一个客户进行主动发送信息!首先服务端要针对某一个用户进行发送信息那么就意味着没一个客户端必须拥有唯一标识身份的标志!如用户名用户ID 等等!在这里我们就使用用户名吧!我们在客户端连接的时候加上一用户名以便区别用户!我们在客户端上加入一个文本输入框命名为 UserName 在连接按钮的代码如下procedure TForm6.ConetClick(Sender: TObject);varStr:String;beginStr:=UserName.Text;if Str='' thenbeginShowMessage('请输入用户名');exit;end;tryif not (IdTCPClient1.Connected) thenbeginIdTCPClient1.Connect;TMainThread.Create(false);IdTCPClient1.IOHandler.writeln('@User:'+Str);ShowMessage('连接成功');end;exceptShowMessage('连接失败');end;end;在这里我们在用户名的前面加上“@User:”是为了区别与其他客户端发送到服务端的信息!您可以自己定义!好那我们接着来看服务端的代码吧!为了对用户数据的管理方便我们先来定义一个用户类代码我就直接贴出来了!UserObj.pas-----------------------unit UserObj;interfaceusesClasses,SyncObjs,SysUtils,IdContext;typeTUserClass=class(TObject)FUserName:String; //您还可以定义更多的数据以及方法FContext: TIdContext; //这里之所有要定义是可以在对象内发送信息publicconstructor create;destructor Destroy; override;procedure CheckMsg(AContext: TIdContext); //这里是用于对象类处理信息publishedproperty UserName:string read FUserName write FUserName;end;implementationuses Server;constructor TUserClass.create;begininherited;FUserName:='';end;destructor TUserClass.Destroy;begininherited;end;procedure TUserClass.CheckMsg(AContext: TIdContext);varMsg,Key,Value : String;Len:Longint;begintryFContext := AContext;AContext.Connection.IOHandler.CheckForDisconnect(True, True); Msg:=AContext.Connection.IOHandler.ReadLn();if(Msg<>'') thenbeginif(Msg[1]='@') then //@表示命令beginLen:=Length(Msg);if(Len>6) thenbeginKey:=Copy(Msg, 2, 4); //命令符号if Key='User' thenbeginValue:=Copy(Msg, 7); //值FUserName:=Value;Form6.Memo1.Lines.Add('用户:'+FUserName+'登陆服务器!') ; end;end;endelseForm6.Memo1.Lines.Add(FUserName+':'+Msg);end;finallyend;end;end.------------------------------------------------------------------ 好我们来看下服务器的程序是怎么样使用这个类来管理用户数据的!我们先引用UserObj 然后在IdTCPServer1控件的连接事件OnConnect上这样做!procedure TForm6.IdTCPServer1Connect(AContext:TIdContext);beginAContext.Data:=TUserClass.create;end;同样我们在断开连接的时候释放掉这个对象procedure TForm6.IdTCPServer1Disconnect(AContext: TIdContext);beginAContext.Data.Free;AContext.Data := nil;end;接着我们就要把服务器的监听事件交给我们的用户对象去处理了!我们把 IdTCPServer1控件的 OnExecute事件代码改写为如下:procedure TForm6.IdTCPServer1Execute(AContext: TIdContext);beginTUserClass(AContext.Data).CheckMsg(AContext);end;做到这里我们来运行看一下效果!~ 客户端先输入用户名然后点击连接多个用户进行连接后我们就发现服务器上可以识别信息到底是谁发过来的了!接着要做服务器针对一个用户发送信息了!我们在服务端上添加一个指定发送信息的用户名文本输入框!名称也为UserName 然后在添加一个单用户发送按钮按钮事件如下procedure TForm6.Button4Click(Sender: TObject);varcList : TList;Count : Integer;Str,User:String;beginStr:=Edit1.Text;User:=UserName.T ext;if(User='')thenbeginshowmessage('请输入要指定发送信息的用户名!');exit;end;trycList := IdTCPServer1.Contexts.LockList;for Count := 0 to cList.Count -1 dobeginif(TUserClass(TIdContext(cList[Count]).Data).UserName=Use r)then //转为对象并判断对象的用户名TIdContext(cList[Count]).Connection.IOHandler.WriteLn(Str);end;finallyIdTCPServer1.Contexts.UnlockList; //一定要解锁否则将会造成死锁end;end;收尾工作就是给服务器加上一个启动灯效果以及做一下简单的握手退出!大概道理就是发送一个EXIT给服务器然后服务器退出后客户端再退出!这样做也是为了安全的退出连接!如果不做这一步好像也没有什么大问题~ 在测试中可能会有一些提示说是连接还没有结束就退出了主程序!这个问题我查阅了一些国外的文档~上面说“这个是DelPhi的正常提示!提示并不一定是报错~~ 可以选择编译的时候忽略掉这个提示信息!~ 只要程序在独立运行下没有报错了就行了!~”OK 大功告成!~ 我们来测试一下我们是否真的可以指定用户发送信息了!。
labview的tcp通信例程LabVIEW是一款强大的通用性编程语言,其实现了可以与许多设备和系统进行通信的功能,甚至能够在不同平台之间进行通信,如Windows、Mac、Linux。
这里我们来探讨一下“LabVIEW的TCP通信例程”。
1. 创建TCP/IP服务器首先,我们需要在LabVIEW环境中创建一个TCP/IP服务器。
在程序底部选取“Other TCP/IP”并拖拽至空白VI,然后选择“TCP Listen”,即可完成创建。
2. 设置端口号接下来,我们需要为该TCP/IP服务器设置一个端口号。
此时需要创建一个常量,选取“Unbundle by Name”的选项卡,输入端口号并保存。
3. 创建TCP/IP客户端我们还需要在程序中创建一个TCP/IP客户端。
该客户端会用来连到我们先前创建的TCP服务器。
在程序底部选择“Other TCP/IP”一栏,并拖拽至空白VI中,我们然后选择“TCP Connect”创建TCP/IP客户端。
4. 设置IP地址与端口号在建立连接前,我们还需要设置IP地址和端口号。
此时,我们创建一个常量,选取“Unbundle by Name”的选项卡,并以相同的端口号和IP地址为输入。
5. 开始连接当‘连接’按钮按下时,我们将会建立连接,并打开了TCP/IP服务器。
此时我们还需要创建一个无限循环,拖拽TCP/IP连接的输入与输出至循环之内。
再创建一个TCP/IP客户端,将其接至循环中,并以端口号为常量。
6. 发送数据的实现使用“Write”模块进行webservice的数据发送,输入套接字以及需要发送的数据。
7. 接收数据的实现使用“Read”模块进行webservice的数据接收,输入套接字和接受缓冲器,注意缓冲器大小的设置。
8. 实现数据解析如果接收到的数据是XML文件格式,我们还需要进行XML的解析。
我们需要选择“Functions”一栏,并选择“XML Parser”选项,接着选择“Parse XML from String”并将接收到的数据传输至解析器中。
LabVIEW 中支持TCP/IP网络通讯研究介绍LabVIEW具有强大的网络通讯功能,这种功能使得LabVIEW的用户可以很容易编写出具有强大网络通讯能力的LabVIEW应用软件,实现远程虚拟仪器。
LabVIEW支持TCP/IP协议、UDP协议等等。
而且NI公司还开发了一种DATASOCKET 技术,很大程度上的简化甚至免除了网络通讯编程,用户很容易地在互联网实现高速数据交换。
TCP/IP协议(传输控制协议/互联网络协议)是INTERNET最基本的协议,由于INTERNET的广泛使用,使得TCP/IP成了事实的标准。
对于该协议的介绍不是本文的重点,相关信息可以参阅其他的资料。
TCP节点介绍在LabVIEW中,可以采用TCP节点来实现基于TCP协议的局域网通讯,该节点在FUNCTION/COMMUNICATION/TCP下,该节点分为三个部分:Connection节点:用于建立客户机和服务器之间通讯的连接Transmission节点:用于在客户机和服务器之间传输数据Conversion节点:用于计算机IP地址和计算机名称间的切换双机TCP通讯流程两种通讯方式多机都采用LabVIEW进行通讯在多机采用LV中TCP实现的通讯,一端我们设定为服务器端,一端为客户端。
这样在运行的时候服务器端不断的侦听,当有客户端连接的时候进行建立连接,然后收取和发送数据形成通讯,客户端运行后不断的与服务器端进行试探连接,如果没有连接上做一定的处理,当连接上后进行收发数据进行处理。
该VI图结构如下通过以上方式,可以方便的实现多机进行通讯。
有一端采用LabVIEW另一端是其他的工具实现进行通讯在实际工作当中,有的时候下位机或者其他的机器使用其他的语言或者组态工具实现,这个时候在实现TCP之间进行交互通讯的时候,我们就不能象上面多机都是LV编写的通讯那样处理了,如何去实现呢。
经过研究发现,LabVIEW提供了运行控制技术,在Semaphonre技术中,该功能可以限制任务数目,这些任务可以同时堆一个共享资源进行操作。
indy tcpclient例子下面是一个关于Indy TCPClient示例的文章:Indy是一个强大的开源网络通信库,它提供了许多组件和类来简化TCP/IP通信的过程。
其中之一就是TCPClient组件,它可以用于建立TCP连接并与服务器进行通信。
首先,我们需要创建一个新的Delphi项目,并在工具箱中找到TCPClient组件。
将其拖放到主窗体上,然后可以开始配置它。
我们需要设置一些基本属性,如Host和Port,以便指定要连接的服务器的地址和端口号。
例如,如果要连接到localhost上的HTTP服务器,则可以将Host设置为"127.0.0.1",Port设置为80。
接下来,我们可以编写代码来控制TCPClient的行为。
我们可以使用Connect方法来建立与服务器的连接。
例如,可以在按钮的OnClick事件处理程序中添加以下代码:```delphiprocedure TForm1.Button1Click(Sender: TObject);begintryTCPClient.Connect;ShowMessage('Connected to server.');excepton E: Exception doShowMessage('Error: ' + E.Message);end;```在这个例子中,我们使用了try-except语句来捕获可能发生的连接错误,并在连接成功后显示一个消息框。
一旦连接成功,我们可以使用TCPClient发送和接收数据。
例如,我们可以使用SendBuf方法来发送一个字节数组,并用ReceiveBuf方法接收服务器的响应。
以下是一个简单的例子:```delphiprocedure TForm1.Button2Click(Sender: TObject);varBuffer: TIdBytes;beginif TCPClient.Connected thenbeginSetLength(Buffer, 1024);TCPClient.SendBuf('Hello from client.');TCPClient.ReceiveBuf(Buffer[0], Length(Buffer));ShowMessage('Received: ' + String(Buffer));endelseShowMessage('Not connected to server.');```在这个例子中,我们首先检查TCPClient是否已经连接。
tcp协议上实现点对点通信的方法在当今的网络时代,点对点通信已成为各种应用场景中不可或缺的技术手段。
TCP(传输控制协议)作为一种可靠的传输协议,为实现点对点通信提供了有力保障。
本文将详细介绍在TCP协议上实现点对点通信的方法。
一、TCP协议简介TCP(传输控制协议)是一种面向连接、可靠的传输层协议。
它通过三次握手建立连接,确保数据传输的可靠性。
在TCP协议中,数据以流的形式传输,通信双方通过端口号区分不同的应用进程。
二、点对点通信的概念点对点通信是指两个网络节点之间直接进行数据交换,不需要经过第三方节点。
在TCP协议上实现点对点通信,可以有效降低通信延迟,提高数据传输效率。
三、实现点对点通信的方法1.基于TCP协议的Socket编程Socket编程是实现点对点通信的基础。
在Socket编程中,通信双方通过创建Socket对象,建立连接,然后进行数据传输。
(1)创建Socket对象在Java、C++等编程语言中,可以使用Socket类创建Socket对象。
例如,在Java中:```javaSocket socket = new Socket("对方IP地址", 对方端口号);```(2)建立连接创建Socket对象后,客户端与服务器端会进行三次握手,建立连接。
(3)数据传输连接建立后,双方可以通过Socket对象的输入输出流进行数据传输。
2.使用NIO(非阻塞IO)传统的Socket编程基于BIO(阻塞IO),在处理大量连接时,效率较低。
NIO(非阻塞IO)是一种更高效的IO模型,可以实现对大量连接的高效处理。
在Java中,可以使用Selector对象实现NIO。
Selector可以监控多个Socket连接,当某个Socket连接有数据可读或可写时,Selector会通知应用程序进行处理。
3.使用第三方库为了简化点对点通信的实现,可以使用第三方库,如Netty、MINA等。
这些库封装了底层的Socket通信细节,提供了更易用的API。
在Delphi 2007中使用Indy10的TCP连接首先先说明下为什么要用 INDY10最新的indy10可以基于win32上的纤程(Fiber) API.什么叫Fiber API呢,这里是解释:纤程(Fiber) —可以从 32 位版本的 Windows? 中使用的轻量级线程处理对象—在很多方案中都很有用。
由于线程是宝贵资源,因此您有时不希望将整个 OS 线程专门用于执行简单的任务。
通过纤程,可以比线程更严密地控制任务的调度,因为是您而不是 OS 负责管理它们。
由于它们具有较少的开销,因此当您切换上下文时,它们还更加快速。
此外,因为是由您控制纤程,所以对于它们而言,通常可以更容易地跟踪同步问题。
不过这个特性,现在只有针对delphi7有用。
端口重叠可以让你的服务器承担更多的用户。
indy10值得一用。
indy10支持完成端口和纤程,性能有了巨大提升!===================================================================== ===========DelPhi2007 中 Indy 升级到了10 而其代码的操作方式也改了很多,很多网友包括本人也是在网络上找了很多资料都没有一个很满意的答案!没办法也得自己亲自调试亲自写了!先来侃侃客户端程序!因为这个程序毕竟真的很难找到资料了所以为了大家很好的使用上这个控件我在这里是一边做一边填代码了!我们先打开 DelPhi2007 工具吧!首先我们做好一个简单的客户端先新建一个窗口程序拖入一个TCP客户端控件还有3个按钮一个文本框是连接断开和发生设置一下 IdTCPClient 控件的属性Host :127.0.0.1Post:3000下面我们来对连接按钮做事件procedure TForm6.ConetClick(Sender: TObject);begintryif not (IdTCPClient1.Connected) thenIdTCPClient1.Connect;ShowMessage('连接成功');exceptShowMessage('连接失败');end;end;接着我们来做一下服务端的程序先新建一个窗口程序拖入一个TCP服务端控件两个按钮以及一个 TMemo用来显示信息Bindings 0.0.0.0:3000DefaultPort 3000我们在“启动服务” 按钮上的事件procedure TForm6.Button1Click(Sender: TObject);beginIdTCPServer1.Active:= true;end;启动时只要将其Active设置为 true 既启动了服务而关闭则同样设置为False接下来我们要对 IdTCPServer1 的 OnExecute 事件做处理!选择控件 EVENTS 栏双击OnExecute在这里代码我们暂时这样写procedure TForm6.IdTCPServer1Execute(AContext: TIdContext);beginexit;end;TIdContext 需要uses IdContext好到这里运行下服务器和客户端然后启动服务器和连接服务器好已经可以连接得上了吧!但是因为我们在服务器监听的部分退出了所以并没有保持着连接现在我们修改一下代码吧我们把OnExecute 代码修改如下procedure TForm6.IdTCPServer1Execute(AContext: TIdContext);varSwp:String;begintryAContext.Connection.IOHandler.CheckForDisconnect(True, True);Swp:=AContext.Connection.IOHandler.ReadLn();Memo1.Lines.Add(Swp) ;finallyend;end;我们对客户端也修改一下procedure TForm6.ConetClick(Sender: TObject);begintryif not (IdTCPClient1.Connected) thenbeginIdTCPClient1.Connect;IdTCPClient1.IOHandler.writeln('lianjie');ShowMessage('连接成功');end;exceptShowMessage('连接失败');end;end;在运行测试一下当按下连接按钮后服务器上的文本框里加入了一行'lianjie' 字符串而其再次点击连接已经无效而刚刚每次点击一次都会提示一次连接成功仔细看代码就发现在连接的时候判断了是否已经连接了如果已经保持连接了哪么就不会在做下面的代码!从而可知现在的连接已经是保持着的了!那好我们来发个信息看下是否真的可以连接了在发送按钮上的事件procedure TForm6.SendClick(Sender: TObject);varStr:String;beginStr:=Edit1.Text;if(IdTCPClient1.Connected) thenIdTCPClient1.IOHandler.writeln(Str);end;好我们来测试一下是不是连接以后真的可以向服务器发送数据了呢?看到了吧!是不是可以发送数据了!我们已经做到了客户端能正常的连接服务器并且向服务器发送内容了!大家可以多开几个客户端然后连接服务器!并发送内容!在这里可能你会问到!服务器怎么样区别数据到底是哪一个发送过来的呢,或者服务器如何对其回复数据呢!~ 那么!我们现在就先针对回复对应的客户端发送过来的数据!已经客户端接受并显示服务器反馈回来的数据!我们修改服务器上的OnExecute代码如下!procedure TForm6.IdTCPServer1Execute(AContext: TIdContext);varSwp:String;begintryAContext.Connection.IOHandler.CheckForDisconnect(True, True);Swp:=AContext.Connection.IOHandler.ReadLn();if(Swp<>'')thenAContext.Connection.IOHandler.WriteLn('服务器已经收到您发来的信息:'+Swp);Memo1.Lines.Add(Swp) ;finallyend;end;在客户端里我们加入一个TMemo用来接受服务器发来的数据信息!然后我们在来看客户端的代码!在连接和发送按钮上的事件修改为procedure TForm6.ConetClick(Sender: TObject);begintryif not (IdTCPClient1.Connected) thenbeginIdTCPClient1.Connect;IdTCPClient1.IOHandler.writeln('lianjie');Str:=IdTCPClient1.IOHandler.ReadLn();Memo1.Lines.Add(Str);ShowMessage('连接成功');end;exceptShowMessage('连接失败');end;end;procedure TForm6.SendClick(Sender: TObject);varStr:String;beginStr:=Edit1.Text;if(IdTCPClient1.Connected) thenIdTCPClient1.IOHandler.writeln(Str);tryStr:=IdTCPClient1.IOHandler.ReadLn();Memo1.Lines.Add(Str);finallyend;end;我们编译后打开多个客户端进行测试就会发现对不同客户端服务器会分别的响应并对其回复内容互不干扰!做到这里大家也知道客户端如果要发送一条数据才能相应的去读取一条数据!可能有些人会想到利用定时器对数据进行定时读取!~ 这样也是一个办法!但是在程序操作中由于数据太快而没有及时读取就会出现数据丢失掉了!那我们要用什么方法才能很好的对数据进行准确读取呢!在这里我使用了线程!启用一个线程利用一个死循环对数据进行读取!一旦有数据就读取出来并放在一个StringList 里供我们使用!好我们一步步的来实现!我们先来做一全局变量的定义新建一全局变量页面 MainUnit.pas我们先声明两个全局变量代码如下unit MainUnit;interfaceuses Classes,SyncObjs;varM_Lock : TCriticalSection;M_MsgList:TStringList;implementationend.然后我们在主程序的窗口创建事件里创建这两个对象procedure TForm6.FormCreate(Sender: TObject);beginM_MsgList:=TStringList.Create;M_Lock :=TCriticalSection.Create;end;接下来我们把这个页面引用到程序中以及线程代码中线程页面MyThread.pas 代码如下unit MyThread;interfaceuses Classes,SysUtils,Forms,Windows,Variants,idIOHandler,MainUnit; typeTMainThread = class(TThread)privateprotectedprocedure Foo;procedure Execute;Override;publicConstructor Create(Suspended:Boolean);end;implementationuses Client;Constructor TMainThread.Create(Suspended:Boolean);//创建线程Begininherited Create(Suspended);FreeOnTerminate:=True;End;procedure TMainThread.Foo;varMsg:string;bool: boolean;beginbool:=true;while bool do begintryMsg:= Form6.IdTCPClient1.IOHandler.ReadLn;if(Msg='') thenbool:=falseelsebeginM_Lock.Enter;M_MsgList.Add(Msg);M_Lock.Leave;end;exceptbool:=false;end;end;end;Procedure TMainThread.Execute;//线程启动beginFoo;End;End.线程做好了哪么我们在程序里进行使用线程吧!首先当然是要在程序中引用MyThread 启动的代码如下连接按钮事件在连接的时候启动线程procedure TForm6.ConetClick(Sender: TObject);begintryif not (IdTCPClient1.Connected) thenbeginIdTCPClient1.Connect;TMainThread.Create(false);IdTCPClient1.IOHandler.writeln('lianjie');ShowMessage('连接成功');end;exceptShowMessage('连接失败');end;end;相应的我们把发送的读取部分也去掉所有读取全部交给线程去处理!procedure TForm6.SendClick(Sender: TObject);varStr:String;beginStr:=Edit1.Text;if(IdTCPClient1.Connected) thenIdTCPClient1.IOHandler.writeln(Str);end;这里线程读取的内容我们全部都放入了StringList 是因为在我们操作界面时可能会出现访问不安全的现象!因为在服务器发送过来的消息里可能有一些是自己定义的执行的命令这些命令可能会直接操作主窗口的一些事件!而在线程里直接操作某些控件是不安全的!所以我们还是先把所有数据放到StringList 里!如果是其他的2进制你可以放入LIST 或者ObjectList里!好下一步就是要把StringList 里的数据读取出来并显示在 Memo1 里了!在这里我是用一个定时器对StringList 进行检查的!加入一个记时器设置时间为1毫秒!我们设置它活动的状态就放在TCP客户端控件的OnConnected事件里!Enabled FalseInterval 1procedure TForm6.IdTCPClient1Connected(Sender: TObject);beginTimer1.Enabled:=true;end;停止活动哦事件放在TCP客户端控件的OnDisconnected事件里!procedure TForm6.IdTCPClient1Disconnected(Sender: TObject);beginTimer1.Enabled:=false;end;然后我们在事件响应函数里这样做procedure TForm6.Timer1Timer(Sender: TObject);varMsg:String;beginM_Lock.Enter;while M_MsgList.Count > 0 dobeginMsg:='';Msg := M_MsgList[0];M_MsgList.Delete(0);if(Msg<>'')thenMemo1.Lines.Add(Msg);end;M_Lock.Leave;end;我们再来运行下看一下效果吧!效果和刚刚的基本一样!但是唯一不同的一点就在于!客户端可以在任何一个时候接受来自服务器的数据!而非主动发送数据而只能单次获取!而且使用了StringList 你完全可以在这里安全的执行相应的事件或函数!不会对线程接受数据的操作有任何影响!好到这里客户端既然能主动发送数据到服务器并且也能接受到服务器的反馈了!但是大家注意到没有!如果服务器想对客户端主动发送数据好像是不可以的!因为在服务端里都是只有响应与其对话的那个客户端的IdTCPServer1Execute 事件里才能有反应!也才能对这个用户发送数据!下面我们来做一下服务端如何对所有用户发送广播信息!在服务器上添加一按钮为广播以及一个文本输入框!在按钮时间里我们的代码如下procedure TForm6.Button3Click(Sender: TObject);varcList : TList;Count : Integer;Str:String;beginStr:=Edit1.Text;trycList := IdTCPServer1.Contexts.LockList;for Count := 0 to cList.Count -1 dobeginTIdContext(cList[Count]).Connection.IOHandler.WriteLn(Str);end;finallyIdTCPServer1.Contexts.UnlockList; //一定要解锁否则将会造成死锁end;end;好了我们编译好客户端多开几个来测试结果吧!怎么样服务器可以主动给所有连接的用户发送数据了吧!如果是按照我们之前的客户端没有使用随时准备着接收那么就不会接受到服务器的广播数据了或者接收到的数据不够准确!前面章节已经介绍了 Indy10 的基本通讯!而且也实现了客户端的发送和接收数据!~ 以及服务端的广播信息!现在我们就接着来做服务端如何针对一个客户进行主动发送信息!首先服务端要针对某一个用户进行发送信息那么就意味着没一个客户端必须拥有唯一标识身份的标志!如用户名用户ID 等等!在这里我们就使用用户名吧!我们在客户端连接的时候加上一用户名以便区别用户!我们在客户端上加入一个文本输入框命名为 UserName 在连接按钮的代码如下procedure TForm6.ConetClick(Sender: TObject);varStr:String;beginStr:=UserName.Text;if Str='' thenbeginShowMessage('请输入用户名');exit;end;tryif not (IdTCPClient1.Connected) thenbeginIdTCPClient1.Connect;TMainThread.Create(false);IdTCPClient1.IOHandler.writeln('@User:'+Str);ShowMessage('连接成功');end;exceptShowMessage('连接失败');end;end;在这里我们在用户名的前面加上“@User:”是为了区别与其他客户端发送到服务端的信息!您可以自己定义!好那我们接着来看服务端的代码吧!为了对用户数据的管理方便我们先来定义一个用户类代码我就直接贴出来了!UserObj.pas-----------------------unit UserObj;interfaceusesClasses,SyncObjs,SysUtils,IdContext;typeTUserClass=class(TObject)FUserName:String; //您还可以定义更多的数据以及方法FContext: TIdContext; //这里之所有要定义是可以在对象内发送信息publicconstructor create;destructor Destroy; override;procedure CheckMsg(AContext: TIdContext); //这里是用于对象类处理信息publishedproperty UserName:string read FUserName write FUserName;end;implementationuses Server;constructor TUserClass.create;begininherited;FUserName:='';end;destructor TUserClass.Destroy;begininherited;end;procedure TUserClass.CheckMsg(AContext: TIdContext);varMsg,Key,Value : String;Len:Longint;begintryFContext := AContext;AContext.Connection.IOHandler.CheckForDisconnect(True, True); Msg:=AContext.Connection.IOHandler.ReadLn();if(Msg<>'') thenbeginif(Msg[1]='@') then //@表示命令beginLen:=Length(Msg);if(Len>6) thenbeginKey:=Copy(Msg, 2, 4); //命令符号if Key='User' thenbeginValue:=Copy(Msg, 7); //值FUserName:=Value;Form6.Memo1.Lines.Add('用户:'+FUserName+'登陆服务器!') ; end;end;endelseForm6.Memo1.Lines.Add(FUserName+':'+Msg);end;finallyend;end;end.------------------------------------------------------------------ 好我们来看下服务器的程序是怎么样使用这个类来管理用户数据的!我们先引用UserObj 然后在IdTCPServer1控件的连接事件OnConnect上这样做!procedure TForm6.IdTCPServer1Connect(AContext: TIdContext);beginAContext.Data:=TUserClass.create;end;同样我们在断开连接的时候释放掉这个对象procedure TForm6.IdTCPServer1Disconnect(AContext: TIdContext);beginAContext.Data.Free;AContext.Data := nil;end;接着我们就要把服务器的监听事件交给我们的用户对象去处理了!我们把 IdTCPServer1控件的 OnExecute事件代码改写为如下:procedure TForm6.IdTCPServer1Execute(AContext: TIdContext);beginTUserClass(AContext.Data).CheckMsg(AContext);end;做到这里我们来运行看一下效果!~ 客户端先输入用户名然后点击连接多个用户进行连接后我们就发现服务器上可以识别信息到底是谁发过来的了!接着要做服务器针对一个用户发送信息了!我们在服务端上添加一个指定发送信息的用户名文本输入框!名称也为UserName 然后在添加一个单用户发送按钮按钮事件如下procedure TForm6.Button4Click(Sender: TObject);varcList : TList;Count : Integer;Str,User:String;beginStr:=Edit1.Text;User:=UserName.Text;if(User='')thenbeginshowmessage('请输入要指定发送信息的用户名!');exit;end;trycList := IdTCPServer1.Contexts.LockList;for Count := 0 to cList.Count -1 dobeginif(TUserClass(TIdContext(cList[Count]).Data).UserName=User)then //转为对象并判断对象的用户名TIdContext(cList[Count]).Connection.IOHandler.WriteLn(Str);end;finallyIdTCPServer1.Contexts.UnlockList; //一定要解锁否则将会造成死锁end;end;收尾工作就是给服务器加上一个启动灯效果以及做一下简单的握手退出!大概道理就是发送一个EXIT给服务器然后服务器退出后客户端再退出!这样做也是为了安全的退出连接!如果不做这一步好像也没有什么大问题~ 在测试中可能会有一些提示说是连接还没有结束就退出了主程序!这个问题我查阅了一些国外的文档~上面说“这个是DelPhi的正常提示!提示并不一定是报错~~ 可以选择编译的时候忽略掉这个提示信息!~ 只要程序在独立运行下没有报错了就行了!~”OK 大功告成!~ 我们来测试一下我们是否真的可以指定用户发送信息了!。