基于TCP的socket编程
sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW);基于TCP的socket编程是采用的流式套接字。
正在装载数据…
在这个程序中,将两个工程添加到一个工作区。要链接一个ws2_32.lib的库文件。
服务器端编程的步骤:
1:加载套接字库,创建套接字(WSAStartup()/socket());
2:绑定套接字到一个IP地址和一个端口上(bind());
3:将套接字设置为监听模式等待连接请求(listen());
4:请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept());
5:用返回的套接字和客户端进行通信(send()/recv());
6:返回,等待另一连接请求;
7:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。
服务器端代码如下:
#include <stdio.h>
#include <Winsock2.h>
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
listen(sockSrv,5);
SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);
while(1)
{
SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
char sendBuf[50];
sprintf(sendBuf,"Welcome s to here!",inet_ntoa(addrClient.sin_addr));
send(sockConn,sendBuf,strlen(sendBuf) 1,0);
char recvBuf[50];
recv(sockConn,recvBuf,50,0);
printf("s\n",recvBuf);
closesocket(sockConn);
}
}
客户端编程的步骤:
1:加载套接字库,创建套接字(WSAStartup()/socket());
2:向服务器发出连接请求(connect());
3:和服务器端进行通信(send()/recv());
4:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。
客户端的代码如下:
#include <stdio.h>
#include <Winsock2.h>
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
char recvBuf[50];
recv(sockClient,recvBuf,50,0);
printf("s\n",recvBuf);
send(sockClient,"hello",strlen("hello") 1,0);
closesocket(sockClient);
WSACleanup();
}
请问:
我的client,server都是同步,想用tcp协议提供的heartbeat机制检测网络连接是否中断。网上找来如下代码,请问如和使用?是在client,server的socket创建好后都要加如下代码还是只需要一方加上就行了?另外通过如下代码我recv受到的数据包应该是什么内容?下面代码需要写在一个线程中不停的判断WSAIoctl返回值吗?请高人指点。
//设置KeepAlive
BOOL bKeepAlive = TRUE;
nRet = ::setsockopt(m_sockDesc, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(bKeepAlive));
if (nRet !=0)
{
sprintf(m_pszError, "Winsock error : %s (Error Code %d)\n ", "Socket SetOpt failed ", WSAGetLastError());
return FALSE;
}
//设置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为10秒,并且发送次数为3次
inKeepAlive.onoff = 1;
inKeepAlive.keepaliveinterval = 10000; //两次KeepAlive探测间的时间间隔
inKeepAlive.keepalivetime = 3; //开始首次KeepAlive探测前的TCP空闭时间
nRet = WSAIoctl(m_sockDesc,
SIO_KEEPALIVE_VALS,
(LPVOID)&inKeepAlive,
ulInLen,
(LPVOID)&outKeepAlive,
ulOutLen,
&ulBytesReturn,
NULL,
NULL);
if(SOCKET_ERROR == nRet)
{
sprintf(m_pszError, "Winsock error : %s (Error Code %d)\n ", "Nonblocking socket call error ", WSAGetLastError());
return FALSE;
}
心跳是逻辑应用层的东西,需要自己实现,一般为自定义,当socket空闲时,发送心跳包,报文件格式自定义!
我现在也遇到心跳检测的问题.网上查没查到,感觉大多比较笼统,就自己做吧.
自己根据需要想了一下.客户端发自定义的心跳包,服务器端接收到心跳包向客户端返回个东西
然后客户端再处理.实现上是可以的.
因为水平有限,还是想学习一下标准的做法
心跳检测需要以下步骤:
1 客户端每隔一个时间间隔发生一个探测包给服务器
2 客户端发包时启动一个超时定时器
3 服务器端接收到检测包,应该回应一个包
4 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
5 如果客户端的超时定时器超时,请问:
我的client,server都是同步,想用tcp协议提供的heartbeat机制检测网络连接是否中断。网上找来如下代码,请问如和使用?是在client,server的socket创建好后都要加如下代码还是只需要一方加上就行了?另外通过如下代码我recv受到的数据包应该是什么内容?下面代码需要写在一个线程中不停的判断WSAIoctl返回值吗?请高人指点。
//设置KeepAlive
BOOL bKeepAlive = TRUE;
nRet = ::setsockopt(m_sockDesc, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(bKeepAlive));
if (nRet !=0)
{
sprintf(m_pszError, "Winsock error : %s (Error Code %d)\n ", "Socket SetOpt failed ", WSAGetLastError());
return FALSE;
}
//设置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为10秒,并且发送次数为3次
inKeepAlive.onoff = 1;
inKeepAlive.keepaliveinterval = 10000; //两次KeepAlive探测间的时间间隔
inKeepAlive.keepalivetime = 3; //开始首次KeepAlive探测前的TCP空闭时间
nRet = WSAIoctl(m_sockDesc,
SIO_KEEPALIVE_VALS,
(LPVOID)&inKeepAlive,
ulInLen,
(LPVOID)&outKeepAlive,
ulOutLen,
&ulBytesReturn,
NULL,
NULL);
if(SOCKET_ERROR == nRet)
{
sprintf(m_pszError, "Winsock error : %s (Error Code %d)\n ", "Nonblocking socket call error ", WSAGetLastError());
return FALSE;
}
心跳检测需要以下步骤:
1 客户端每隔一个时间间隔发生一个探测包给服务器
2 客户端发包时启动一个超时定时器
3 服务器端接收到检测包,应该回应一个包
4 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
5 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了
依然没有收到应答包,则说明服务器挂了
DWORD NetSessionEnum( LPSTR,
DWORD,
LPBYTE,
DWORD,
LPDWORD,
LPDWORD )
参数1: NULL表示枚举本机的网络连接
参数2: 不详.在枚举中是常量0x32.
参数3: 存放信息的缓冲区指针
参数4: 缓冲区长度
参数5: 指向返回连接个数
参数6: 指向总共连接个数
Top
在WINDOWS95&WINDOWS98下如何关闭网络连接
--------------------------------------------------------------------------------
如果你有这个方面的问题请到C/C++去提问或发表你的意见 来源:论坛转载无法确定出处,如有版权问题请与我们联系 在WINDOWS95&WINDOWS98下如何关闭网络连接
一.问题提出: 每当你通过WINDOWS95或WINDOWS98访问"网上邻居"时,系统自动的建立了两
台机器之间的网络连接关系,但是在访问结束后,并不自动的断开网络连接,
所以有时我们关闭WINDOWS系统时,会弹出一个对话框,询问是否关闭网络连
接,在回答"YES"后,才真正开始关闭计算机.
程序员编制系统关闭程序时,就需要考虑这种情况,虽然SDK提供了关机的API:
ExitWindowsEx()和ExitWindows(),但实际应用中我发现,在指定FORCE关机时
在特定情况下会出问题.所以,必须想办法首先断开网络连接.
二.编程接口:
WINDOWS95及WINDOWS98提供的这方面的网络编程接口在SVRAPI.DLL中,利用它
我们可以列举出当前网络连接状态,控制或删除网络连接.WINDOWS附件中的
NETWATCH.EXE工具就是这样实现的.
也许您会问,NetAPI的详细说明在开发工具的SDK文档中很详细了,没有必要在
此演示.但是,在查寻了很多资料后,我不得不说:MSDN中有关NetAPI的部分说
明是错误的,至少是不完整而且含混不清的,可以说,依靠这些文档,你不能实
现全部的功能!下面的代码是本人自己分析得来,使用后,您会发现正确的应用
和文档说明有多么大的差距.
三.API声明:
关闭网络连接的实现方法分两步: 枚举出当前所有的网络连接状况; 依次
断开枚举出的网络连接.
1.枚举出当前所有的网络连接状况:
依照开发帮助文档,这个API是这样的:
NET_API_STATUS NetSessionEnum(
LPWSTR servername,
LPWSTR UncClientName,
LPWSTR username,
DWORD level,
LPBYTE *bufptr,
DWORD prefmaxlen,
LPDWORD entriesread,
LPDWORD totalentries,
LPDWORD resume_handle
);
但是,实际情况是,在WINDOWS95和WINDOWS98平台下,
这样调用根本就无法连接上库文件.真正的API声明应该是:
DWORD NetSessionEnum( LPSTR,
DWORD,
LPBYTE,
DWORD,
LPDWORD,
LPDWORD )
参数1: NULL表示枚举本机的网络连接
参数2: 不详.在枚举中是常量0x32.
参数3: 存放信息的缓冲区指针
参数4: 缓冲区长度
参数5: 指向返回连接个数
参数6: 指向总共连接个数
可见,参数个数完全不同,另外参数意义也发生了变化.
2.依次断开枚举出的网络连接:
还算幸运的是,断开网络连接的API声明是正确的:
NET_API_STATUS NetSessionDel(
LPWSTR servername,
LPWSTR UncClientName,
LPWSTR username );
不过要注意的是,第2个和第3个参数的内容需要
从枚举得到的缓冲区中去取.具体方法参见程序.
四.源代码:
以下是实现断开网络连接的子程序,你可以方便的把它们加入到自己的项目中
去,而不用和我一样浪费时间去研究到底怎样实现网络枚举了.
注:由于本程序只实际用到了一个SVRAPI.DLL中的函数声明,简便期间,我没有
用原有的头文件,自己定义一下就可以了.
///////////////////////////////////////////////////////////////////
// File: NetClose.H
// Version: 1.01
#define NETBUFF_SIZE 0x208
#define NetSessionEnum_PROFILE ( DWORD (__stdcall *) ( LPSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD ) )
#define NetSessionDel_PROFILE ( DWORD (__stdcall *) ( LPSTR, LPSTR, DWORD ) )
///////////////////////////////////////////////////////////////////
// File: NetClose.CPP
// Version: 1.01
///////////////////////////////////////////////////////////////////
// Define: BOOL NetCloseAll( VOID )
// Parameters: None
// Return: TRUE or FALSE
//
// Author: Mr.Huang fei
// Time: 5/7/1998
// Note: Can only be used on Windows95 & Windows98
// Remark: Close all network conneCTions
///////////////////////////////////////////////////////////////////
BOOL NetCloseAll( VOID )
{
BYTE byBuff[NETBUFF_SIZE];
DWORD dwNetRet = 0;;
DWORD i = 0;
DWORD dwEntries = 0;
DWORD dwTotalEntries = 0;
LPSTR szClient = NULL;
DWORD dwUserName = 0;
BOOL bRet = FALSE;
LPBYTE lpbyBuff = (LPBYTE)byBuff;
DWORD (__stdcall * hookNetSessionEnum)( LPSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD );
DWORD (__stdcall * hookNetSessionDel)( LPSTR, LPSTR, DWORD );
HINSTANCE hMod = LoadLibrary( "SVRAPI.DLL" );
if( hMod != NULL )
{
// Get the address of function
hookNetSessionEnum = NetSessionEnum_PROFILE
GetProcAddress
( hMod, TEXT("NetSessionEnum") );
hookNetSessionDel = NetSessionDel_PROFILE
GetProcAddress
( hMod, TEXT("NetSessionDel") );
if( ( hookNetSessionDel != NULL ) &&
( hookNetSessionEnum != NULL ) )
{
dwNetRet = hookNetSessionEnum( NULL,
0x32, byBuff,
NETBUFF_SIZE, &dwEntries,
&dwTotalEntries );
if( dwNetRet == 0 )
{
bRet = TRUE;
for( i=0; i < dwTotalEntries; i++ )
{
szClient = (LPSTR)(((DWORD *)
lpbyBuff)[0]);
dwUserName = ((DWORD *)lpbyBuff)[2];
dwNetRet = hookNetSessionDel( NULL,
szClient, dwUserName );
if( dwNetRet != 0 )
{
bRet = FALSE;
break;
}
lpbyBuff += 26;
}
} // NetSessionEnum(...)
else
bRet = FALSE;
} // GetProcAddress(...)
else
bRet = FALSE;
FreeLibrary( hMod );
} // LoadLibrary(...)
return bRet;
}
五.总结:
以上是开发过程中的一点经验,希望对大家有所帮助,有不对的地方请谅解并
指出.另外,众所周知Microsoft的开发文档有相当一部分是未公开的,这些未
公开信息有时会给我们造成很大的困难,在此希望有类似体验的程序开发者
把自己的经验写出来,让后来者少走一些弯路.
8 楼sevencat(七猫)回复于 2003-10-05 15:47:13 得分 0 要稍微翻译一下吗?InternetGetConnectedState这个函数获得当前系统的连接状态。
参数
lpdwFlags 是一个指向整数的指针。
获取的状态就放在这里面
INTERNET_CONNECTION_CONFIGURED 有一个internet上的无效连接,不过当前可能不工作。
INTERNET_CONNECTION_LAN Local 用当地网络连上互连网的
INTERNET_CONNECTION_MODEM 用MODEM上网的
INTERNET_CONNECTION_MODEM_BUSY 不使用了。
INTERNET_CONNECTION_OFFLINE 脱状态下。
INTERNET_CONNECTION_PROXY 用代理服务器连上网的。
INTERNET_RAS_INSTALLED 用了RAS了。Top
2 楼Skt32(荒城之月)回复于 2003-10-03 21:08:49 得分 5DWORD NetSessionEnum( LPSTR,
DWORD,
LPBYTE,
DWORD,
LPDWORD,
LPDWORD )
参数1: NULL表示枚举本机的网络连接
参数2: 不详.在枚举中是常量0x32.
参数3: 存放信息的缓冲区指针
参数4: 缓冲区长度
参数5: 指向返回连接个数
参数6: 指向总共连接个数
Top
在WINDOWS95&WINDOWS98下如何关闭网络连接
一.问题提出:
每当你通过WINDOWS95或WINDOWS98访问"网上邻居"时,系统自动的建立了两 台机器之间的网络连接关系,但是在访问结束后,并不自动的断开网络连接, 所以有时我们关闭WINDOWS系统时,会弹出一个对话框,询问是否关闭网络连 接,在回答"YES"后,才真正开始关闭计算机. 程序员编制系统关闭程序时,就需要考虑这种情况,虽然SDK提供了关机的API: ExitWindowsEx()和ExitWindows(),但实际应用中我发现,在指定FORCE关机时
在特定情况下会出问题.所以,必须想办法首先断开网络连接.
二.编程接口:
WINDOWS95及WINDOWS98提供的这方面的网络编程接口在SVRAPI.DLL中,利用它 我们可以列举出当前网络连接状态,控制或删除网络连接.WINDOWS附件中的 NETWATCH.EXE工具就是这样实现的.
也许您会问,NetAPI的详细说明在开发工具的SDK文档中很详细了,没有必要在 此演示.但是,在查寻了很多资料后,我不得不说:MSDN中有关NetAPI的部分说 明是错误的,至少是不完整而且含混不清的,可以说,依靠这些文档,你不能实 现全部的功能!下面的代码是本人自己分析得来,使用后,您会发现正确的应用
和文档说明有多么大的差距.
三.API声明:
关闭网络连接的实现方法分两步: 枚举出当前所有的网络连接状况; 依次 断开枚举出的网络连接.
1.枚举出当前所有的网络连接状况:
依照开发帮助文档,这个API是这样的:
NET_API_STATUS NetSessionEnum(
LPWSTR servername,
LPWSTR UncClientName,
LPWSTR username,
DWORD level,
LPBYTE *bufptr,
DWORD prefmaxlen,
LPDWORD entriesread,
LPDWORD totalentries,
LPDWORD resume_handle
);
但是,实际情况是,在WINDOWS95和WINDOWS98平台下, 这样调用根本就无法连接上库文件.真正的API声明应该是:
DWORD NetSessionEnum( LPSTR,
DWORD,
LPBYTE,
DWORD,
LPDWORD,
LPDWORD )
参数1: NULL表示枚举本机的网络连接
参数2: 不详.在枚举中是常量0x32.
参数3: 存放信息的缓冲区指针
参数4: 缓冲区长度
参数5: 指向返回连接个数
参数6: 指向总共连接个数
可见,参数个数完全不同,另外参数意义也发生了变化.
2.依次断开枚举出的网络连接:
还算幸运的是,断开网络连接的API声明是正确的:
NET_API_STATUS NetSessionDel(
LPWSTR servername,
LPWSTR UncClientName,
LPWSTR username );
不过要注意的是,第2个和第3个参数的内容需要
从枚举得到的缓冲区中去取.具体方法参见程序.
四.源代码:
以下是实现断开网络连接的子程序,你可以方便的把它们加入到自己的项目中
去,而不用和我一样浪费时间去研究到底怎样实现网络枚举了.
注:由于本程序只实际用到了一个SVRAPI.DLL中的函数声明,简便期间,我没有
用原有的头文件,自己定义一下就可以了.
///////////////////////////////////////////////////////////////////
// File: NetClose.H
// Version: 1.01
#define NETBUFF_SIZE 0x208
#define NetSessionEnum_PROFILE ( DWORD (__stdcall *) ( LPSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD ) )
#define NetSessionDel_PROFILE ( DWORD (__stdcall *) ( LPSTR, LPSTR, DWORD ) )
///////////////////////////////////////////////////////////////////
// File: NetClose.CPP
// Version: 1.01
///////////////////////////////////////////////////////////////////
// Define: BOOL NetCloseAll( VOID )
// Parameters: None
// Return: TRUE or FALSE
//
// Author: Mr.Huang fei
// Time: 5/7/1998
// Note: Can only be used on Windows95 & Windows98
// Remark: Close all network conneCTions
///////////////////////////////////////////////////////////////////
BOOL NetCloseAll( VOID )
{
BYTE byBuff[NETBUFF_SIZE];
DWORD dwNetRet = 0;;
DWORD i = 0;
DWORD dwEntries = 0;
DWORD dwTotalEntries = 0;
LPSTR szClient = NULL;
DWORD dwUserName = 0;
BOOL bRet = FALSE;
LPBYTE lpbyBuff = (LPBYTE)byBuff;
DWORD (__stdcall * hookNetSessionEnum)( LPSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD );
DWORD (__stdcall * hookNetSessionDel)( LPSTR, LPSTR, DWORD );
HINSTANCE hMod = LoadLibrary( "SVRAPI.DLL" );
if( hMod != NULL )
{
// Get the address of function
hookNetSessionEnum = NetSessionEnum_PROFILE
GetProcAddress
( hMod, TEXT("NetSessionEnum") );
hookNetSessionDel = NetSessionDel_PROFILE
GetProcAddress
( hMod, TEXT("NetSessionDel") );
if( ( hookNetSessionDel != NULL ) &&
( hookNetSessionEnum != NULL ) )
{
dwNetRet = hookNetSessionEnum( NULL,
0x32, byBuff,
NETBUFF_SIZE, &dwEntries,
&dwTotalEntries );
if( dwNetRet == 0 )
{
bRet = TRUE;
for( i=0; i < dwTotalEntries; i++ )
{
szClient = (LPSTR)(((DWORD *)
lpbyBuff)[0]);
dwUserName = ((DWORD *)lpbyBuff)[2];
dwNetRet = hookNetSessionDel( NULL,
szClient, dwUserName );
if( dwNetRet != 0 )
{
bRet = FALSE;
break;
}
lpbyBuff += 26;
}
} // NetSessionEnum(...)
else
bRet = FALSE;
} // GetProcAddress(...)
else
bRet = FALSE;
FreeLibrary( hMod );
} // LoadLibrary(...)
return bRet;
}
五.总结:
怎么设置客户端自动连接服务器啊?
现在要开发一个网络通讯客户端程序,用的是TCP/IP协议,具体的协议已经拟订好了。简单的描述及时每隔30秒客户端自动向服务器端传输一组数据。因为我的客户端机器要一天24小时不停的运行,而且是无人值守的,那万一网络中断或是服务器端关机等异常情况发生,导致连接中断时,我的客户端程序必须保证在服务器端启动后,客户端能自动连接上服务器。我现在不清楚该怎么设置connect函数,才能做到这一点。
还有,对于这种程序,我该采用多线程阻塞模式好呢,还是采用非阻塞模式好些?对于我这一点,我始终都确定不了别人都是采取哪种方法实现的?
如果我要在程序中发送心跳包的话,该怎么做呢?