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),不过还是建议加上这一句。