如何进行tcp数据交互
这是我网络中找的参考,希望对你有帮助。 在多线程任务中,TCP任务通过三次握手能建立可靠的连接,但是经常会发生在数据传输或通信时发生网络突然断开或者长时间连接空循环监听而未进行操作,需要在软件设计时考虑程序运行中检测到服务器对客户端的这一“虚连接”现象。如果主机崩溃,write是否阻塞取决于内核的tcp缓冲区,但read将一直阻塞,直到超时ETIMEOUT,或由于某些中间路由器的原因返回EHOSTUNREACH/ENETUNREACH。select不能检测到该情况。如果主机崩溃并重起,客户的write到达主机时主机响应RST,客户的read将返ECONNRESET。此处的”非正常断开”指TCP连接不是以优雅的方式断开,如网线故障等物理链路的原因,还有突然主机断电等原因。心跳机制有两种方法可以检测:1.TCP连接双方定时发握手消息2.利用TCP协议栈中的KeepAlive探测第二种方法简单可靠,只需对TCP连接两个Socket设定KeepAlive探测,所以本文只讲第二种方法在Linux,Window2000下的实现(在其它的平台上没有作进一步的测试)1)Windows平台C代码//定义结构及宏struct TCP_KEEPALIVE {u_longonoff;u_longkeepalivetime;u_longkeepaliveinterval;} ;#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)//KeepAlive实现TCP_KEEPALIVE inKeepAlive = {0}; //输入参数unsigned long ulInLen = sizeof(TCP_KEEPALIVE);TCP_KEEPALIVE outKeepAlive = {0}; //输出参数unsigned long ulOutLen = sizeof(TCP_KEEPALIVE);unsigned long ulBytesReturn = 0;//设置socket的keep alive为5秒,并且发送次数为3次inKeepAlive.onoff = 1;inKeepAlive.keepaliveinterval = 5000; //两次KeepAlive探测间的时间间隔inKeepAlive.keepalivetime = 5000; //开始首次KeepAlive探测前的TCP空闭时间if (WSAIoctl((unsigned int)s, SIO_KEEPALIVE_VALS,(LPVOID)&inKeepAlive, ulInLen,(LPVOID)&outKeepAlive, ulOutLen,&ulBytesReturn, NULL, NULL) == SOCKET_ERROR){ACE_DEBUG ((LM_INFO,ACE_TEXT ("(%P|%t) WSAIoctl failed. error code(%d)!n"), WSAGetLastError()));}//定义结构及宏 struct TCP_KEEPALIVE { u_longonoff; u_longkeepalivetime; u_longkeepaliveinterval; } ; #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) //KeepAlive实现 TCP_KEEPALIVE inKeepAlive = {0}; //输入参数 unsigned long ulInLen = sizeof(TCP_KEEPALIVE); TCP_KEEPALIVE outKeepAlive = {0}; //输出参数 unsigned long ulOutLen = sizeof(TCP_KEEPALIVE); unsigned long ulBytesReturn = 0; //设置socket的keep alive为5秒,并且发送次数为3次 inKeepAlive.onoff = 1; inKeepAlive.keepaliveinterval = 5000; //两次KeepAlive探测间的时间间隔 inKeepAlive.keepalivetime = 5000; //开始首次KeepAlive探测前的TCP空闭时间 if (WSAIoctl((unsigned int)s, SIO_KEEPALIVE_VALS, (LPVOID)&inKeepAlive, ulInLen, (LPVOID)&outKeepAlive, ulOutLen, &ulBytesReturn, NULL, NULL) == SOCKET_ERROR) { ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%P|%t) WSAIoctl failed. error code(%d)!n"), WSAGetLastError())); }2)Linux平台C代码#include……////KeepAlive实现//下面代码要求有ACE,如果没有包含ACE,则请把用到的ACE函数改成linux相应的接口int keepAlive = 1;//设定KeepAliveint keepIdle = 5;//开始首次KeepAlive探测前的TCP空闭时间int keepInterval = 5;//两次KeepAlive探测间的时间间隔int keepCount = 3;//判定断开前的KeepAlive探测次数if(setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)) == -1){ACE_DEBUG ((LM_INFO,ACE_TEXT ("(%P|%t) setsockopt SO_KEEPALIVE error!n")));}if(setsockopt(s,SOL_TCP,TCP_KEEPIDLE,(void *)&keepIdle,sizeof(keepIdle)) == -1){ACE_DEBUG ((LM_INFO,ACE_TEXT ("(%P|%t) setsockopt TCP_KEEPIDLE error!n")));}if(setsockopt(s,SOL_TCP,TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval)) == -1){ACE_DEBUG ((LM_INFO,ACE_TEXT ("(%P|%t) setsockopt TCP_KEEPINTVL error!n")));}if(setsockopt(s,SOL_TCP,TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount)) == -1){ACE_DEBUG ((LM_INFO,ACE_TEXT ("(%P|%t)setsockopt TCP_KEEPCNT error!n")));}心跳机制:定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性。网络中的接收和发送数据都是使用WINDOWS中的SOCKET进行实现。但是如果此套接字已经断开,那发送数据和接收数据的时候就一定会有问题。可是如何判断这个套接字是否还可以使用呢?这个就需要在系统中创建心跳机制。其实TCP中已经为我们实现了一个叫做心跳的机制。如果你设置了心跳,那TCP就会在一定的时间(比如你设置的是3秒钟)内发送你设置的次数的心跳(比如说2次),并且此信息不会影响你自己定义的协议。所谓“心跳”就是定时发送一个自定义的结构体(心跳包或心跳帧),让对方知道自己“在线”。以确保链接的有效性。所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。代码就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息如果服务端几分钟内没有收到客户端信息则视客户端断开。比如有些通信软件长时间不使用,要想知道它的状态是在线还是离线就需要心跳包,定时发包收包。发包方:可以是客户也可以是服务端,看哪边实现方便合理。一般是客户端。服务器也可以定时轮询发心跳下去。心跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项。系统默认是设置的是2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。而且逻辑层处理断线可能也不是那么好处理。一般,如果只是用于保活还是可以的。心跳包一般来说都是在逻辑层发送空的包来实现的。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。只需要send或者recv一下,如果结果为零,则为掉线。但是,在长连接下,有可能很长一段时间都没有数据往来。理论上说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉。在这个时候,就需要我们的心跳包了,用于维持长连接,保活。在获知了断线之后,服务器逻辑可能需要做一些事情,比如断线后的数据清理呀,重新连接呀当然,这个自然是要由逻辑层根据需求去做了。总的来说,心跳包主要也就是用于长连接的保活和断线处理。一般的应用下,判定时间在30-40秒比较不错。如果实在要求高,那就在6-9秒。TCP连接异常断开后操作系统会告诉你,你查询套接字的状态会得到异常,或者当发现函数失败WSAGetLastError的时候也会得到内核的通知。// 发送回应消息int nSend = Send4IntMsg(sock, (char*)(LPCTSTR)strSendBuf,strSendBuf.GetLength(), errMsg);if (nSend < 0) //发送消息失败closesocket(sock);//重新连接 在B/S编程和UDP编程时才用到心跳。比如定期向web服务器发一个request证明自己在线。http协议是请求一下就断开了,每次都要重新连接,重新请求,这种情况下才有必要用心跳机制。一般的TCP通信都是长连接,不可能频繁连接和断开。对于长期保持连接的情况,一旦断开,操作系统底层都会通知你,你需要解决的是如何获取到系统的通知。

怎么使用TCP向远端SERVER传输数据
如何使用TCP向远端SERVER传输数据 (发送与接收) 1. 如何使用TCP向远端SERVER传输数据先要建立一个TCP连接.模块作为CLIENT向远程的SERVER发起一个TCP连接,要成功建立连接需要SERVER端为连接到INTERNET的一台PC,而且该PC的IP地址是公网的IP地址(可以用拨号的方式获得,如在PC局域网内部则无法建立连接),然后该PC运行我们的SERVER软件,这时就可以在模块端用AT命令与SERVER建立TCP连接(AT+CIPSTART="TCP","SERVER的IP地址","SERVER的端口号"),连接成功后会返回CONNECTOK.然后就可以用AT+CIPSEND发送数据到SERVER,若SERVER有数据,模块自动通过串口接收.要关闭TCP连接可用AT+CIPCLOSE命令.2. 如何使用UDP向远端SERVER传输数据先要注册一个UDP端口(AT+CIPSTART="UDP","SERVER的IP地址","SERVER的端口号"),成功后返回CONNECT OK.然后用AT+CIPSEND发送UDP包到SERVER,若SERVER有数据,模块自动接收,从串口送出.3. 如何发送数据要发送数据必须先要建立TCP连接或注册一个UDP连接.有3种方式发送数据.可变长度的数据发送可以用AT+CIPSEND命令,返回"$amp;>amp;$quot;后输入发送的数据,ctrl+z(0x1a)启动发送.固定长度的数据发送用AT+CIPSEND=LENGTH命令,返回"> "后输入发送的数据,当输入的数据长度等于LENGTH的时候自动发送,不需结束符.还有一种自动发送方式,先用AT+CIPATS=,命令设定自动发送的时间,然后用AT+CIPSEND返回"$amp;>amp;$quot;输入要发送的数据,等定时到了之后自动发送输入的数据.发送完成之后返回SEND OK.此时处于命令态,若有数据发送再重复以上的步骤就可以了.注意:每次发送的数据长度应小于1024bytes.4. 如何接收数据接收数据为自动接收,若有远端数据则自动接收.可以用AT+CIPHEAD=1在接收的数据前面自动加上标识.SIMCOM Application Note for SIM100 TCP/IP AT Commands45. 如何使用SIM100TCP的SERVER功能模块可以配置为TCPSERVER,接收来自远端的TCP数据.具体的操作如下:AT+CIPCSGP=1,"cmnet"(GPRS方式)AT+CLPORT="TCP","PORT" (设定侦听的TCP的端口号)AT+CIPSERVER (启动SERVER功能,成功后返回SERVER OK,这时候已经启动了SERVER功能,开始侦听PORT)AT+CIFSR (获得SERVER的IP地址)或者:AT+CIPCSGP=0,"17201","172","172", 2 (CSD方式,置为CSD连接,拨17201,用户名和密码皆为172,连接速率为9600)AT+CLPORT="TCP","PORT"(设定侦听的TCP端口号)AT+CIPSERVER(启动SERVER功能,成功后返回CONNECT 9600 和 SERVER OK,这时候已经启动了SERVER功能,开始侦听PORT)AT+CIFSR (获得SERVER的IP地址)可以用SMS通知远端模块本SERVER的IP地址和端口号,以便远端可以与之连接,收发数据.这时远端的模块就可以与本SERVER建立连接,收发数据了.如有CLIENT连接到SERVER,SERVER端会显示 REMOTE CLIENT的IP地址和端口号.这时候SERVER就可以接收来自REMOTE CLIENT的TCP数据包(不含TCP头)了.注意:目前TCP SERVER不能同时支持多个TCP连接.SIMCOM Application Note for SIM100 TCP/IP AT Commands56. 如何使用UDP,接收来自远端的数据可以使用SIM100TCP的UDP功能,接收来自远端的UDP数据.具体过程如下:AT+CIPCSGP=1,"cmnet"(GPRS方式)AT+CLPORT="UDP","端口号"(设置UDP端口号)AT+CSTT (启动TCP任务,成功后返回OK)AT+CIICR (激活场景,成功后返回OK)AT+CIFSR (获得本地IP地址)AT+CIPSTART="UDP","REMOTE IP ADDR","REMOTE PORT"(注册UDP连接,其中"REMOTE IP ADDR"和"REMOTE PORT"可以随便设置一个,成功后返回CONNECT OK)或者AT+CIPCSGP=0,"17201","172","172", 2 (CSD方式,置为CSD连接,拨17201,用户名和密码皆为172,连接速率为9600)AT+CLPORT="UDP","PORT"(设定UDP端口号)AT+CSTT (启动TCP任务)AT+CIICR (激活场景,成功后返回CONNECT 9600和OK)AT+CIFSR (获得本地IP地址)AT+CIPSTART="UDP","REMOTE IP ADDR","REMOTE PORT"(注册UDP连接,其中"REMOTE IP ADDR"和"REMOTE PORT"可以随便设置一个,成功后返回CONNECT OK)此时就模块就可以接收到远端的UDP包.可以用AT+CIPSRIP=1在收到的UDP包的前面加上标识和发送方的IP地址和端口号.SIMCOM Application Note for SIM100 TCP/IP AT Commands67. 如何使用DNS功能利用SIM100TCP的DNS可直接连接到一个域名,或者可以用DNS解析域名获得IP地址.具体步骤如下:直接连接到一个域名1:先配置DNSAT+CDNSCFG="211.136.18.171" (以上海为例)2:选择域名还是IP地址AT+CDNSORIP=1(选择域名)3:建立连接AT+CIPSTART="TCP","", "80" (连接到SERVER)4:发送数据AT+CIPSEND返回"$amp;>amp;$quot;后开始发送数据,ctrl+z启动发送.解析域名获得IP地址1:先激活一个移动场景AT+CSTTAT+CIICRAT+CIFSR2:配置DNSAT+CDNSCFG="211.136.18.171" (以上海为例)3:解析域名,获得IP地址AT+CDNSGIP=""返回该域名的IP地址8. 如何检测是否在线,断线后有什么提示可以用AT+CIPSTATUS查询TCP所处的状态,具体状态请参考AT命令集,若处于连接状态则返回CONNECTOK.断线后会主动返回CLOSE信息,同时TCP状态为IP CLOSE.9. 如何区分接收的数据是AT命令的响应还是来自远端设定AT+CIPHEAD=1,这样来自SERVER的数据前面就会自动加上一个标志,其格式为:+IPD(datalength),+IPD为标识,datalength为来自SERVER端数据的长度.这样就可以区分是来自SERVER的数据还是AT命令的相应.SIMCOM Application Note for SIM100 TCP/IP AT Commands710. 如何使用SIM100TCP建立点对点的TCP连接,传输数据用GPRS方式的具体步骤为:第一台:AT+CLPORT="TCP","3030"AT+CIPSERVER (启动SERVER功能,开始侦听3030端口)AT+CIFSR(获取本地IP地址)AT+CLPORT="TCP","3000"(设置本地TCP端口)第二台:AT+CLPORT="TCP","2020"AT+CIPSERVER (启动SERVER功能,开始侦听2020端口)AT+CIFSR(获取本地IP地址)AT+CLPORT="TCP","2000"(设置本地TCP端口)第一台:AT+CIPSTART="TCP","REMOTE IP ADDR","2020" (REMOTEIP ADDR可由对方发SMS得到)已经建立连接第二台:AT+CIPSTART="TCP","REMOTE IP ADDR","3030"已经建立连接之后任何一方就可以向另外一方发数据(用AT+CIPSEND发送)也可以用CSD方式建立,具体操作如下:第一台:AT+CIPCSGP=0,"17201","172","172", 2 (CSD方式,置为CSD连接,拨17201,用户名和密码皆为172,连接速率为9600)AT+CLPORT="TCP","3030"AT+CIPSERVER (启动SERVER功能,开始侦听3030端口)AT+CIFSR(获取本地IP地址)AT+CLPORT="TCP","3000"第二台:AT+CIPCSGP=0,"17201","172","172", 2 (CSD方式,置为CSD连接,拨17201,用户名和密码皆为172,连接速率为9600)AT+CLPORT="TCP","2020"AT+CIPSERVER (启动SERVER功能,开始侦听2020端口)AT+CIFSR(获取本地IP地址)AT+CLPORT="TCP","2000"SIMCOM Application Note for SIM100 TCP/IP AT Commands8第一台:AT+CIPSTART="TCP","REMOTE IP ADDR","2020" (REMOTEIP ADDR可由对方发SMS得到)已经建立连接第二台:AT+CIPSTART="TCP","REMOTE IP ADDR","3030"已经建立连接之后任何一方就可以向另外一方发数据(用AT+CIPSEND发送)SIMCOM Application Note for SIM100 TCP/IP AT Commands911. 如何使用SIM100TCP实现多个模块之间的互相通信目前SIM100TCP不支持多个TCP同时连接,但是可以用UDP方式来实现.可以用模块启动UDP功能,这样模快就可以接收来自远端的UDP包,可以通过收到的UDP包获取发送方的IP地址和端口号,然后本模块就可以作出应答,回应一个UDP包,实现相互的通讯.下面是一个简单的例子:假设A,B,C三个模块要相互通信,可以用以下的方式实现(以GPRS方式为例)1:A,B,C分别初始化如下:AT+CIPCSGP=1,"cmnet"(GPRS方式)AT+CLPORT="UDP","端口号"(设置UDP端口号)AT+CSTT (启动TCP任务,成功后返回OK)AT+CIICR (激活场景,成功后返回OK)AT+CIFSR (获得本地IP地址)AT+CIPSTART="UDP","REMOTE IP ADDR","REMOTE PORT"(注册UDP连接,其中"REMOTE IP ADDR"和"REMOTE PORT"可以随便设置一个,成功后返回CONNECT OK)2:若A要向B发送数据,只要这样就可以了:AT+CIPCLOSE (注销当前UDP连接)AT+CIPSTART="UDP","B的IP地址","B的端口号"(注册新的UDP连接)(成功后会返回CONNECTOK和OK)AT+CIPSEND (发送数据)若C有数据发给A,则A自动接收到,同时可以获得C的IP地址和端口号,这样A就可以发UDP包给C了,具体过程跟上面类似.SIMCOM Application Note for SIM100 TCP/IP AT Commands1012. TCP连接出错后应该如何处理若在建立TCP连接的过程中出现错误或者SERVER断线,则先用AT+CIPCLOSE关闭TCP连接(本地IP地址不变),然后再重新建立连接.若其他时候可以用AT+CIPSHUT关闭PDP Context(本地IP地址会改变),然后重新建立连接.13. 如何建立一个点对点的CSD连接首先要确保SIM卡已开通CSD业务,直接呼叫对方号码(数据呼叫,非语音呼叫),对方用ATA应答,然后会返回CONNECT 9600,这时候双方进入完全的透明数据传输状态,双方都可以收发数据,发+++后返回命令态(注意+++的前后0.5秒内必须无数据传输才能返回 命令态),ATO可返回数据态.ATH可结束本次CSD连接.
