`
abs200311235
  • 浏览: 122529 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

c++ socket

阅读更多
参考:http://tech.163.com/school http://tb.donews.net/TrackBack.aspx?PostId=101035
在VC中进行WINSOCK的API编程开发的时候,需要在项目中使用下面三个文件,否则会出现编译错误。
  1.WINSOCK.H: 这是WINSOCK API的头文件,需要包含在项目中。
  2.WSOCK32.LIB: WINSOCK API连接库文件。在使用中,一定要把它作为项目的非缺省的连接库包含到项目文件中去。
  3.WINSOCK.DLL: WINSOCK的动态连接库,位于WINDOWS的安装目录下。
1)在初始化阶段调用WSAStartup()
  此函数在应用程序中初始化Windows Sockets DLL ,只有此函数调用成功后,应用程序才可以再调用其他Windows Sockets DLL中的API函数。在程式中调用该函数的形式如下:
int WSAStartup (WORD wVersionRequested,LPWSADATA lpWSAData);
其中第一个参数为你所想需要的Winsock版本!低字节为主版本,高字节为副版本!由于目前Winsock有两个版本:1.1和2.2,因此该参数可以是0x101或0x202;二个参数是一个WSADATA结构,用于接收函数的返回信息!WSAStartup函数调用成功会返回0,否则返回非0值!在DLL内部维持着一个计数器,只有第一次调用WSAStartup才真正装载DLL,以后的调用只是简单的增加计数器,而WSACleanup函数的功能则刚好相反,每调用一次使计数器减1,当计数器减到0时,DLL就从内存中被卸载!因此,你调用了多少次WSAStartup,就应相应的调用多少次的WSACleanup。
使用示例:
WSADATA wsaData;
if(WSAStartup(0x101,&wsaData))
{
//错误处理!
}
  2)建立Socket
  创建套接字有两个函数,socket和WSASocket,前者是标准的Socket函数,而后者是微软对Socket的扩展函数。socket函数调用成功返回一个套接字描述符,错误则返回SOCKET_ERROR。
SOCKET PASCAL FAR WSASocket ( int af, int type, int protocol )
参数:
af: 第一个是指定通信发生的区域,在UNIX下有AF_UNIX、AF_INET、AF_NS等,而在Winsock1.1下只支持AF_INET,到了2.2则添了AF_IRDA(红外线通信)、AF_ATM(异步网络通信)、AF_NS、AF_IPX等。
type:第2个参数是套接字的类型,在AF_INET地址族下,有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW三种套接字类型。SOCK_STREAM也就是通常所说的TCP,而SOCK_DGRAM则是通常所说的UDP,而SOCK_RAW则是用于提供一些较低级的控制的;
protocol:依赖于第2个参数,用于指定套接字所用的特定协议,设为0表示使用默认的协议。
使用示例:
SOCKET sk;
sk=socket(AF_INET,SOCK_STREAM,0);
if(sk==SOCKET_ERROR)
{
//错误处理
}
  3)绑定端口
  接下来要为服务器端定义的这个监听的Socket指定一个地址及端口(Port),这样客户端才知道待会要连接哪一个地址的哪个端口,为此我们要调用bind()函数,该函数调用成功返回0,否则返回SOCKET_ERROR。
int PASCAL FAR bind( SOCKET s, const struct sockaddr FAR *name,int namelen );
参 数: s:Socket对象名;
name:Socket的地址值,这个地址必须是执行这个程式所在机器的IP地址;
namelen:name的长度;
  如果使用者不在意地址或端口的值,那么可以设定地址为INADDR_ANY,及Port为0,Windows Sockets 会自动将其设定适当之地址及Port (1024 到 5000之间的值)。此后可以调用getsockname()函数来获知其被设定的值。
  4)监听
  当服务器端的Socket对象绑定完成之后,服务器端必须建立一个监听的队列来接收客户端的连接请求。listen()函数使服务器端的Socket 进入监听状态,并设定可以建立的最大连接数(目前最大值限制为 5, 最小值为1)。该函数调用成功返回0,否则返回SOCKET_ERROR。
int PASCAL FAR listen( SOCKET s, int backlog );
参 数: s:需要建立监听的Socket;
backlog:最大连接个数;
  服务器端的Socket调用完listen()后,如果此时客户端调用connect()函数提出连接申请的话,Server 端必须再调用accept() 函数,这样服务器端和客户端才算正式完成通信程序的连接动作。为了知道什么时候客户端提出连接要求,从而服务器端的Socket在恰当的时候调用accept()函数完成连接的建立,我们就要使用WSAAsyncSelect()函数,让系统主动来通知我们有客户端提出连接请求了。该函数调用成功返回0,否则返回SOCKET_ERROR。
int PASCAL FAR WSAAsyncSelect( SOCKET s, HWND hWnd,unsigned int wMsg, long lEvent );
参数: s:Socket 对象;
hWnd :接收消息的窗口句柄;
wMsg:传给窗口的消息;
lEvent:被注册的网络事件,也即是应用程序向窗口发送消息的网路事件,该值为下列值FD_READ、FD_WRITE、FD_OOB、FD_ACCEPT、FD_CONNECT、FD_CLOSE的组合,各个值的具体含意为FD_READ:希望在套接字S收到数据时收到消息;FD_WRITE:希望在套接字S上可以发送数据时收到消息;FD_ACCEPT:希望在套接字S上收到连接请求时收到消息;FD_CONNECT:希望在套接字S上连接成功时收到消息;FD_CLOSE:希望在套接字S上连接关闭时收到消息;FD_OOB:希望在套接字S上收到带外数据时收到消息。
  具体应用时,wMsg应是在应用程序中定义的消息名称,而消息结构中的lParam则为以上各种网络事件名称。所以,可以在窗口处理自定义消息函数中使用以下结构来响应Socket的不同事件:  
switch(lParam) 
  {case FD_READ:
    …  
  break;
case FD_WRITE、
    …
  break;    …
}
  5)服务器端接受客户端的连接请求
  当Client提出连接请求时,Server 端hwnd视窗会收到Winsock Stack送来我们自定义的一个消息,这时,我们可以分析lParam,然后调用相关的函数来处理此事件。为了使服务器端接受客户端的连接请求,就要使用accept() 函数,该函数新建一Socket与客户端的Socket相通,原先监听之Socket继续进入监听状态,等待他人的连接要求。该函数调用成功返回一个新产生的Socket对象,否则返回INVALID_SOCKET。
SOCKET PASCAL FAR accept( SCOKET s, struct sockaddr FAR *addr,int FAR *addrlen );
参数:s:Socket的识别码;
addr:存放来连接的客户端的地址;
addrlen:addr的长度
  6)结束 socket 连接
  结束服务器和客户端的通信连接是很简单的,这一过程可以由服务器或客户机的任一端启动,只要调用closesocket()就可以了,而要关闭Server端监听状态的socket,同样也是利用此函数。另外,与程序启动时调用WSAStartup()函数相对应,程式结束前,需要调用 WSACleanup() 来通知Winsock Stack释放Socket所占用的资源。这两个函数都是调用成功返回0,否则返回SOCKET_ERROR。
int PASCAL FAR closesocket( SOCKET s );
参 数:s:Socket 的识别码;     
int PASCAL FAR WSACleanup( void );
参 数: 无
二、客户端Socket的操作
  1)建立客户端的Socket
  客户端应用程序首先也是调用WSAStartup() 函数来与Winsock的动态连接库建立关系,然后同样调用socket() 来建立一个TCP或UDP socket(相同协定的 sockets 才能相通,TCP 对 TCP,UDP 对 UDP)。与服务器端的socket 不同的是,客户端的socket 可以调用 bind() 函数,由自己来指定IP地址及port号码;但是也可以不调用 bind(),而由 Winsock来自动设定IP地址及port号码。
http://www.iteye.com/topic/352490
2)提出连接申请
  在成功调用了socket函数后,对客户端来说就是与服务器端建立连接。同样,建立连接需要两个函数:connect和WSAConnect。前者是标准的Socket函数,后者是微软的扩展函数。
int PASCAL FAR WSAConnect ( SOCKET s, const struct sockaddr FAR *name, int namelen );
参 数:
s:所使用的套接字描述符;
name:一个sockaddr结构,sockaddr结构是一个通用的结构,它只是简单地定义了一个字节数组,在TCP/IP下一般将其解释为sockaddr_in结构,第3个参数则是该结构的长度,一般用sizeof函数来取得;
namelen:name的长度
示例代码:
sockaddr_in sock;
sock.sin_family=AF_INET;
sock.sin_port=htons(80);
sock.sin_addr.s_addr=inet_addr(“202.205.210.1”);
if(connect(sk,(sockaddr*)&sock,sizeof(sock)==SOCKET_ERROR)
{ 
//错误处理 
}
sockaddr_in结构体
struct sockaddr_in {
       //地址族(指定地址格式) ,设为AF_INET
       short sa_family;
       u_short  sin_port; //端口号
      struct in_addr sin_addr; //IP地址
       char sin_zero[8]; //空子节,设为空
       }

sockaddr结构体
struct sockaddr{
       u_short  sa_family;
       char       sa_data[14];
       }
这里有一点要说明的是,用于填写sockaddr_in结构的值必须是以网络字节顺序表示的值,而不能直接使用本机字节顺序的值。之所以这样规定是因为在网络上存在不同的系统,不同的系统中数据存储时所采用的字节排列顺序是不同的,有的是高字在前,低字在后,而有的刚好相反。为了统一,规定了一个所谓的网络字节顺序。htonl函数可以将本地的unsigned long数据转换为网络字节顺序的数据。htons则是将unsigned short的数据转换为网络字节顺序的数据。而ntohs、ntohl的功能则是刚好相反。另外,sockaddr_in结构的sin_addr.s_addr成员要求是用来描述对方地址的一个值,即网际地址值,而实际应用中,我们得到的大多是IP地址或域名,如202.210.205.1或www.cfan.cn.net,可以用inet_addr函数将点分法表示的IP地址转换为所要求的值,可以用gethostbyname、WSAAsynGetHostbyName取回用易用名表示的主机的信息。gethostbyname函数调用成功会返回一个hostent结构的指针,若错误则返回NULL。下面介绍一下gethostbyname函数的用法。
hostent *host;
.......
host=gethostbyname(“www.cfan.cn.net”)
if(host==NULL)
{ 
//错误处理 
sock.sin_addr.s_addr=*((unsigned long*)host→h_addr_list[0]);
......
  三、数据的传送和接收
  于这里建立的是SOCK_STREAM类型的连接,故发送可以采用的函数有send和WSASend,而接收可以采用recv和WSARecv,同样,全小写的函数是标准的Socket函数,以WSA开头的是微软的扩展函数. Send、recv调用成功返回所发送或接收的字节数,如果调用失败则返回SOCKET_ERROR!
int PASCAL FAR WSASend ( SOCKET s, const char FAR *buf,int len, int flags );
参数:
s:发送操作所用的套接字描述符
buf:发送的数据缓冲区的地址,为char*类型,至于其它类型的数据可以用强制类型转换(char*)。在接收端再用强制类型转换回来!
len buf:所发送的缓冲区的大小,也就是所要发送的字节数!
flags:一个附加标志,可以为0、MSG_OOB、MSG_DONTROUTE. 如果对所发送的数据没特殊要求,直接设为0。
  对于Datagram Socket而言,若是 datagram 的大小超过限制,则将不会送出任何资料,并会传回错误值。对Stream Socket 言,Blocking 模式下,若是传送系统内的储存空间不够存放这些要传送的资料,send()将会被block住,直到资料送完为止;如果该Socket被设定为 Non-Blocking 模式,那么将视目前的output buffer空间有多少,就送出多少资料,并不会被 block 住。flags 的值可设为 0 或 MSG_DONTROUTE及 MSG_OOB 的组合。

int PASCAL FAR recv( SOCKET s, char FAR *buf, int len, int flags );
参数说明同发送。
示例代码(send函数):
SOCKET sk;
char szTest[]=“This is an example!”
int iRet;
......(这里省略创建套接字,连接...)
iRet=send(sk,szTest,strlen(szTest),0);
if(iRet==SOCKET_ERROR) 
{
//错误处理 
}
else if(iRet!=strlen(szTest))
MessageBox(NULL,“未发送所有的数据”,“警告”,MB_OK);
示例代码(recv函数)
SOCKET sk;
char szTest[20]
int iRet;
......(这里省略创建套接字,连接......)
iRet=recv(sk,szTest,20,0);
if(iRet==SOCKET_ERROR)
{
//错误处理
}
szTest[iRet]=`\0`;//这一行代码不可少!因为recv函数不会自动将数据缓冲末尾设为表示数据结束的空中止符(`\0`),因此,一不留神就会出现缓冲区越界。当然也可以在调用recv函数前先将缓冲区清0(用ZeroMemory或memset),不过还是建议加上这一句。
http://www.iteye.com/topic/353304
一个典型的TCP端口扫描器,通过用connect函数对服务器进行尝试连接来判断该服务器上的端口是否开放。这个扫描器是多线程的,现在的Winsock编程大多数采用多线程技术,这样可以充分利用带宽,如Netants的5个蚂蚁下载,一些FTP软件的多线程上传,等等!
//Source Code In C++Builder5
Cpp代码
#pragma hdrstop  
#include “Unit1.h”  
#define threadNum 10//线程数  
#define mutexName “Welcome to LoveBcb.yeah.net”  
#pragma package(smart_init)  
#pragma resource “*.dfm”  
typedef struct g_scan //这是一个自定义的结构  
{  
char szFile[40];//用于存放结果的文件名  
char szMutex[40];//用于存放互斥体的名字,这是多线程保证线程安全的一种方法  
unsigned short sPort;//扫描的起始端口,本机字节顺序  
unsigned short ePort;//扫描的终止端口,本机字节顺序  
unsigned long goalI;//目标主机IP,网络字节顺序  
int Result;//用于存放结果  
}*PG_SCAN;  
 
TForLover *ForLover;//这是窗体  
HANDLE hThread[threadNum];  
g_scan gscan[threadNum];  
DWORD dwThreadId,dwThreadCode;  
unsigned short usPart;//用于分割所要扫描的端口数,分配给各个线程  
unsigned long ulIp;  
int iLiveThread;//用于存放活动的线程数  
unsigned long ServerIp(char*serverip);  
DWORD WINAPI ScanPort(LPVOID lp)  
/*这是主线程函数ScanPort*/ 
DWORD WINAPI ScanPort(LPVOID lp)  
{  
PG_SCAN pgscan=(PG_SCAN)lp;  
char szResult[40];  
sockaddr_in sock;  
unsigned short nowPort=pgscan→sPort-1;//用于存放当前扫描的端口号  
FILE*fp;//文件指针  
 
HANDLE hMutex=OpenMutex(MUTEX_ALL_ACCESS,false,pgscan→szMutex);  
SOCKET sk=socket(AF_INET,SOCK_STREAM,0);  
sock.sin_family=AF_INET;  
sock.sin_addr.s_addr=pgscan→goalIp;  
while(nowPort   
{  
sock.sin_port=htons(++nowPort)  
if(connect(sk,(sockaddr*)&sock,sizeof(sock))==SOCKET_ERROR)  
continue;  
/*由于这里用的是阻塞方式的套接字,所以返回SOCKET_ERROR一般意味着无法连接,于是用continue结束本次循环,即重新开始一次循环。如果返回值不是SOCKET_ERROR的话,表示连接成功,也就是说目标主机上开放了此端口*/ 
wsprintf(szResult,“目标主机:%s端口:%d开放\r\n”,inet_ntoa(sock.sin_addr),nowPort); WaitForSingleObject(hMutex,INFINITE);  
 
/*用WaitForSinleObject保证线程安全INFINITE表示一直等待,直到互斥体有信号*/ 
fp=fopen(pgscan→szFile,“a”);  
fwrite(szResult,sizeof(char),strlen(szResult),fp);  
fclose(fp);  
pgscan→Result++;  
ReleaseMutex(hMutex);//释放互斥体  
closesocket(sk);//由于已经建立了连接,所以这里要关闭连接  
sk=socket(AF_INET,SOCK_STREAM,0);//重新创建一个套接字  
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics