TCP/IP Winsock编程要点

原创 2007年09月28日 09:03:00

       利用Winsock编程由同步和异步方式,同步方式逻辑清晰,编程专注于应用,在抢先式的多任务操作系统中(WinNt、Win2K)采用多线程方式效率基本达到异步方式的水平,应此以下为同步方式编程要点。

     1、快速通信

         Winsock的Nagle算法将降低小数据报的发送速度,而系统默认是使用Nagle算法,使用

       int setsockopt(

                       SOCKET s,                

                      int level,                

                     int optname,             

                     const char FAR *optval, 

                     int optlen               

                   );函数关闭它

       例子:

                   SOCKET sConnect;

                   sConnect=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

                   int bNodelay = 1;

                   int err;

                 err = setsockopt(

                   sConnect,

                   IPPROTO_TCP,

                   TCP_NODELAY,

                   (char *)&bNodelay,

                   sizoeof(bNodelay));//不采用延时算法

           if (err != NO_ERROR)

              TRACE ("setsockopt failed for some reason/n");; 

      2、SOCKET的SegMentSize和收发缓冲

         TCPSegMentSize是发送接受时单个数据报的最大长度,系统默认为1460,收发缓冲大小为8192。

         在SOCK_STREAM方式下,如果单次发送数据超过1460,系统将分成多个数据报传送,在对方接受到的将是一个数据流,应用程序需要增加断帧的判断。当然可以采用修改注册表的方式改变1460的大小,但MicrcoSoft认为1460是最佳效率的参数,不建议修改。

       在工控系统中,建议关闭Nagle算法,每次发送数据小于1460个字节(推荐1400),这样每次发送的是一个完整的数据报,减少对方对数据流的断帧处理。  

     3、同步方式中减少断网时connect函数的阻塞时间

        同步方式中的断网时connect的阻塞时间为20秒左右,可采用gethostbyaddr事先判断到服务主机的路径是否是通的,或者先ping一下对方主机的IP地址。

A、采用gethostbyaddr阻塞时间不管成功与否为4秒左右。

例子:

LONG lPort=3024;

struct sockaddr_in ServerHostAddr;//服务主机地址

ServerHostAddr.sin_family=AF_INET;

ServerHostAddr.sin_port=::htons(u_short(lPort));

ServerHostAddr.sin_addr.s_addr=::inet_addr("192.168.1.3");

HOSTENT* pResult=gethostbyaddr((const char *) &

(ServerHostAddr.sin_addr.s_addr),4,AF_INET); 

if(NULL==pResult)

{

int nErrorCode=WSAGetLastError();

TRACE("gethostbyaddr errorcode=%d",nErrorCode);

}

else

{

TRACE("gethostbyaddr %s/n",pResult->h_name);;

}

B、采用PING方式时间约2秒左右

暂略

 

4、同步方式中解决recv,send阻塞问题

采用select函数解决,在收发前先检查读写可用状态。

A、读

例子:

TIMEVAL tv01 = {0, 1};//1ms钟延迟,实际为0-10毫秒

int nSelectRet;

int nErrorCode;

FD_SET fdr = {1, sConnect};

nSelectRet=::select(0, &fdr, NULL, NULL, &tv01);//检查可读状态

if(SOCKET_ERROR==nSelectRet)

{

nErrorCode=WSAGetLastError();

TRACE("select read status errorcode=%d",nErrorCode);

::closesocket(sConnect);

goto 重新连接(客户方),或服务线程退出(服务方);

}

if(nSelectRet==0)//超时发生,无可读数据

{

继续查读状态或向对方主动发送

}

else

{

读数据

}        

B、写

TIMEVAL tv01 = {0, 1};//1ms钟延迟,实际为9-10毫秒

int nSelectRet;

int nErrorCode;

FD_SET fdw = {1, sConnect};

nSelectRet=::select(0,  NULL, NULL,&fdw, &tv01);//检查可写状态

if(SOCKET_ERROR==nSelectRet)

{

nErrorCode=WSAGetLastError();

TRACE("select write status errorcode=%d",nErrorCode);

::closesocket(sConnect);

//goto 重新连接(客户方),或服务线程退出(服务方);

}

if(nSelectRet==0)//超时发生,缓冲满或网络忙

{

//继续查写状态或查读状态

}

else

{

//发送

}

5、改变TCP收发缓冲区大小

系统默认为8192,利用如下方式可改变。

SOCKET sConnect;

sConnect=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

int nrcvbuf=1024*20;

int err=setsockopt(

sConnect,        

SOL_SOCKET,       

SO_SNDBUF,//写缓冲,读缓冲为SO_RCVBUF

(char *)&nrcvbuf,

sizeof(nrcvbuf));

if (err != NO_ERROR)

{

TRACE("setsockopt Error!/n");

}

在设置缓冲时,检查是否真正设置成功用

int getsockopt(

SOCKET s,        

int level,       

int optname,     

char FAR *optval,

int FAR *optlen 

);

6、服务方同一端口多IP地址的bind和listen

在可靠性要求高的应用中,要求使用双网和多网络通道,再服务方很容易实现,用如下方式可建立客户对本机所有IP地址在端口3024下的请求服务。

SOCKET hServerSocket_DS=INVALID_SOCKET;

struct sockaddr_in HostAddr_DS;//服务器主机地址

LONG lPort=3024;

HostAddr_DS.sin_family=AF_INET;

HostAddr_DS.sin_port=::htons(u_short(lPort));

HostAddr_DS.sin_addr.s_addr=htonl(INADDR_ANY);

 

hServerSocket_DS=::socket( AF_INET, SOCK_STREAM,IPPROTO_TCP);

 

if(hServerSocket_DS==INVALID_SOCKET)

{

AfxMessageBox("建立数据服务器SOCKET 失败!");

return FALSE;

}

 

if(SOCKET_ERROR==::bind(hServerSocket_DS,(struct

sockaddr *)(&(HostAddr_DS)),sizeof(SOCKADDR)))

{

int  nErrorCode=WSAGetLastError ();

TRACE("bind error=%d/n",nErrorCode);               

AfxMessageBox("Socket Bind 错误!");

return FALSE;

}

if(SOCKET_ERROR==::listen(hServerSocket_DS,10))//10个客户

{

AfxMessageBox("Socket listen 错误!");

return FALSE;

}

AfxBeginThread(ServerThreadProc,NULL,THREAD_PRIORITY_NORMAL);

在客户方要复杂一些,连接断后,重联不成功则应换下一个IP地址连接。也可采用同时连接好后备用的方式。

 

7、用TCP/IP Winsock实现变种Client/Server

传统的Client/Server为客户问、服务答,收发是成对出现的。而变种的Client/Server是指在连接时有客户和服务之分,建立好通信连接后,不再有严格的客户和服务之分,任何方都可主动发送,需要或不需要回答看应用而言,这种方式在工控行业很有用,比如RTDB作为I/O Server的客户,但I/O Server也可主动向RTDB发送开关状态变位、随即事件等信息。在很大程度上减少了网络通信负荷、提高了效率。

采用1-6的TCP/IP编程要点,在Client和Server方均已接收优先,适当控制时序就能实现。 

【winsock2】windows网络编程 之 TCP/IP Client

还是按照以下几步来完成客户端与服务器间的通信: 1、加载和初始化套接字 #include #pragma comment(lib,"ws2_32.lib") WSADATA wsa_data; i...
  • ieczw
  • ieczw
  • 2014年02月19日 19:31
  • 1615

Winsock;TCP/IP编程;模板代码;VC++;单线程;

#include #include #include #pragma comment(lib,"ws2_32.lib") int main() { //加载套接字库 W...

TCP/IP网络通信 WinSock编程

网络字节顺序: 网络中传输的数据使用的均是大尾方式, 大尾方式是 高位存放低字节,地位存放高字节。 windows提供了一些函数来处理本地机器的字节顺序和网络字节顺序的转换: u_shor...

WinSock API网络编程——TCP/IP协议详解

 WinSock API网络编程——TCP/IP协议(http://www.impcas.ac.cn/usr/lujun/browse.asp?id=winsock_tcp)     ...

【C++】Winsock套接字编程(TCP/IP协议体系)常用API

鄙人水平浅薄,如有错误,欢迎大神指正 系统环境:Windows10 64位 开发环境:VisualStudio2015 PS:关于本文提供的winsock套接字API,是针对wi...

【winsock2】windows网络编程 之 TCP/IP Server

Socket网络编程无论是什么平台,都有相似流程;在windows中我们一般使用WinSock2来进行网络通信,当然还有其他网络通信库。 今天具体说下TCP/IP Server的编程流程,具体分为6大...
  • ieczw
  • ieczw
  • 2014年02月19日 18:27
  • 5313

winsock window套接字 TCP/IP

  • 2011年04月14日 16:50
  • 9KB
  • 下载

VB的winsock(TCP/IP)连续发送数

字体大小: 大 | 中 | 小     已经差不多过完年了,又要开始忙了,新的一年新的开始,希望今年的目标能实现。 很久没有写技术文章了,说点老技术,也就是标题中所说的,用...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:TCP/IP Winsock编程要点
举报原因:
原因补充:

(最多只允许输入30个字)