winsock编程问题

来源:互联网 发布:永恒之塔天族捏脸数据 编辑:程序博客网 时间:2024/04/28 17:36
套接字,就是一个指向传输提供者的句柄。Wi n 3 2中,套接字不同于文件描述符,所以它是一个独立的类型—S O C K E T。
套接字是由两个函数建立的:
SOCKET WSASocket(int af,int type,int protocal,LPWSAPROTOCOL_INFO lpProtocolInfor,GROUP g,DWORD dwFlags);
 
SOCKET socket(int af,int type, int protocal);

第一个参数a f,是协议的地址家族。比如,如果想建立一个U D P或T C P套接字,可用常量
A F _ I N E T来指代互联网协议( I P)。
第二个参数t y p e,是协议的套接字类型。套接字的类型可以是下面五个值: S O C K _ S T R E A M、S O C K _ D G R A M、S O C K _ S E Q PA C K E T、S O C K _ R AW和S O C K _ R D M。
第三个参数是p r o t o c o l。指定的地址家族和套接字类型有多个条目时,就可用这个字段来限定使用特定传输.
如果在W S A S o c k e t函数时,已经利用W S A E n u m P r o t o c o l s列举了所有协议,就可选定一个
W S A P R O TO C O L _ I N F O结构,并将它当作l p P r o t o c o l I n f o参数投递到W S A S o c k e t。之后,若
在前三个参数(a f、t y p e和p r o t o c o l)中都指定常量F R O M _ P R O TO C O L _ I N F O,就会转而采用W S A P R O TO C O L _ I N F O结构中提供的那三个值。以上便是教大家如何指定一个准确无误的协议条目。
最后两个W S A S o c k e t标志很简单。组参数始终为0,因为目前尚无可支持套接字组的Wi n s o c k版本。
 
 
Winsock的初始化
每个Wi n s o c k应用都必须加载Winsock DLL的相应版本。如果调用Wi n s o c k之前,没有加载
Wi n s o c k库,这个函数就会返回一个S O C K E T _ E R R O R,错误信息是W S A N O T I N I T I A L I S E D。
加载Wi n s o c k库是通过调用W S A S t a r t u p函数实现的。这个函数的定义如下:
 
int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);

w Ve r s i o n R e q u e s t e d参数用于指定准备加载的Wi n s o c k库的版本。高位字节指定所需要的Wi n s o c k库的副版本,而低位字节则是主版本。然后,可用宏M A K E W O R D ( X , Y )(其中,x是高位字节, y是低位字节)方便地获得w Ve r s i o n R e q u e s t e d的正确值。
l p W S A D a t a参数是指向L P W S A D ATA结构的指针, W S A S t a r t u p用其加载的库版本有关的信息.
 
客户机需要通过T C P或U D P和服务器通信时,必须指定服务器的I P地址和服务端口号。另外,服务器打算监听接入客户机请求时,也必须指定一个I P地址和一个端口号。Wi n s o c k中,应用通过S O C K A D D R _ I N结构来指定I P地址和服务端口信息,该结构的格式如下:
struct sockaddr_in
{
    short                      sin_family;
    u_shot                   sin_port;
  struck in_addr         sin_addr;
    char                      sin_zero[80]
}
 
 
创建套接字:
SOCKET server;
server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
或:server = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);
 
服务器API:
1.BIND
int bind(SOCKET s,const struct sockaddr FAR* name,int namelen);
 
一旦出错, b i n d 就会返回S O C K E T _ E R R O R 。对b i n d 来说,最常见的错误是
W S A E A D D R I N U S E。如使用的是T C P / I P,那么W S A E A D D R I N U S E就表示另一个进程已经同
本地I P接口和端口号绑定到了一起,或者那个I P接口和端口号处于T I M E _ WA I T状态。假如你
针对一个套接字调用b i n d,但那个套接字已经绑定,便会返回W S A E F FA U LT错误。
 
2.LISTEN
int listen(SOCKET s,int backlog);
 
第一个参数同样是限定套接字。b a c k l o g参数指定了正在等待连接的最大队列长度。
与l i s t e n对应的错误是非常直观的。到目前为止,最常见的错误是W S A E I N VA L。该错误
通常意味着,你忘记在l i s t e n之前调用b i n d。否则,与b i n d调用相反,使用l i s t e n时可能收到
W S A E A D D R I N U S E。这个错误通常是在进行b i n d调用时发生的。
 
3. accept和W S A A c c e p t
现在,我们已做好了接受客户连接的准备。这是通过a c c e p t或W S A A c c e p t函数来完成的。
a c c e p t格式如下:
 
SOCKET accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen);
 
Winsock 2引入了一个名为W S A A c c e p t的函数。它能根据一个条件函数的返回值,选择性
地接受一个连接。这个新函数的定义如下:
 
SOCKET WSAAccept(SOCKET s, struct sockaddr FAR* addr,LLPINT addrlen, LPCONDITIONPROC lpfnCondition,DWORD dwCallbackData);
 
客户机API:
1。c o n n e c t函数和W S A C o n n e c t函数
这是通过调用c o n n e c t函数或W S A C o n n e c t函数来完成的。我们先来
看看该函数的Winsock 1版本,其定义如下:
 
int connect(SOCKET s,const struct sockaddr FAR* name,int namelen);
 
Winsock 2版本中,它的定义是这样的:
 
int WSAConnect(SOCKET s,const struct sockaddr FAR* name,int namelen,LPWSABUF lpCallerData,LPWSABUF lpCalleeData,LPQOS lpSQOS,LPQOS lpGQOS);
 
如果你想连接的计算机没有监听指定端口这一进程, c o n n e c t调用就会失败,并发生错误
W S A E C O N N R E F U S E D。另一个错误可能是W S A E T I M E D O U T,这种情况一般发生在试图连
接的计算机不能用时(亦可能因为到主机之间的路由上出现硬件故障或主机目前不在网上)。
 
2. send和W S A S e n d
要在已建立连接的套接字上发送数据,第一个可用的A P I函数是s e n d,其原型为:
 
int send(SOCKET s,const char FAR* buf,int len,int flags);
 
对返回数据而言,s e n d返回发送的字节数;若发生错误,就返回S O C K E T _ E R R O R。常见
的错误是W S A E C O N N A B O RT E D,这一错误一般发生在虚拟回路由于超时或协议有错而中断
的时候。发生这种情况时,应该关闭这个套接字,因为它不能再用了。远程主机上的应用通
过执行强行关闭或意外中断操作重新设置虚拟虚路时,或远程主机重新启动时,发生的则是
W S A E C O N N R E S E T错误。再次提醒大家注意,发生这一错误时,应该关闭这个套接字。最
后一个常见错误是W S A E T I M E O U T,它发生在连接由于网络故障或远程连接系统异常死机而
引起的连接中断时。
send API函数的Winsock 2版本是W S A S e n d,它的定义如下:
 
int WSASend(SOCKET s,LPWSABUF lpBuffers,DWORD dwBufferCount,LPDWORD lpNumberOfBytesSent,DWORD dwFlags,LPWSAOVERLAPPED lpOVERlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE);
 
这个套接字是一个连接会话的有效句柄。第二个参数是指向一个或多个W S A B U F结构的
指针。它既可是一个独立的结构,又可以是一组结构。第三个参数指明准备投递的W S A B U F
结构数。记住,每个W S A B U F结构本身就是一个字符缓冲和缓冲长度。为何打算同时发送多
个缓冲呢?也许大家不太明白其中的原因。这就是我们稍后要讲的“分散集中I / O模式”;但
是,在一个已建立连接的套接字上利用多缓冲来发送数据时,顺序是从第一个到最后一个
W S A B U F结构。l p N u m b e r O f B y t e s S e n t是指向D W O R D(是W S A S e n d调用返回的)的指针,其中
包含字节总发送数。d w F l a g s参数相当于它在s e n d中的等同物。最后两个参数—l p O v e r l a p p e d
和l p C o m p l e t i o n R O U T I N E—用于重叠I / O.
 
 
4 recv和W S A R e c v
对在已连接套接字上接受接入数据来说, r e c v函数是最基本的方式。它的定义如下:
 
int recv(SOCKET s,char FAR* buf,int len, int flags);
 
W S A R e c v函数在r e c v的基础上增加了一些新特性。比如说重叠I / O和部分数据报通知。
W S A R e c v的定义如下:
 
int WSARecv(SOCKET s,LPWSABUF lpBuffers,DWORD dwBufferCount,LPDWORD lpNumberOfBytesRecvd,LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE);
------------------------------------------
TCP server示例
 
#include <windows.h>
#include <iostream>
#include <winsock.h>
#pragma comment( lib, "Ws2_32.lib" )
using namespace std;
const int MY_ERROR = -1;
int main( )
{
     WSADATA wsaData;
    
     //初始化Windows Socket 库
     if( WSAStartup(MAKEWORD( 2, 2 ), &wsaData)!=0 )
    {
        cout<<"Error in WSAStartup";
        return MY_ERROR;
     }
 
    //创建一个socket->bind->listen->accept(阻塞)
    SOCKET server;
    server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(server==INVALID_SOCKET)
    {
        return 0;
    }
    sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(8080);
    local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    if( bind(server, (sockaddr*)(&local), sizeof(local))!=0 )
    {
        cout<<"Error in bind";
        return MY_ERROR;
     }
    if( listen(server, 10)!=0 )
    {
        cout<<"Error in listen";
        return MY_ERROR;
     }

    SOCKET client;
    sockaddr_in from;
    int fromLen = sizeof(from);
    memset(&from, 0, fromLen);
    cout<<"waiting for connect...";
    client = accept(server, (sockaddr*)(&from), &fromLen);
    char temp[512];
    sprintf(temp,"Your IP is %s/r/n",inet_ntoa(from.sin_addr));
 
    send(client, temp, strlen(temp), 0);
    cout << "Connection from " << inet_ntoa(from.sin_addr) <<"/r/n";
 
    closesocket(client);
 
    closesocket(server);
    WSACleanup();
    return 0;
}
------------------------------------------
原创粉丝点击