第一章 Winsock简介

Winsock是一种标准的API(应用程序编程接口),主要用于网络中的数据通信,它允许两个或多个应用程序(或进程)在同一台机器上或通过网络相互通信。

1.1 Winsock头文件及库文件

  WINSOCK2.H , WINSOCK.H , MSWSOCK.H(此头文件用于微软专用编程扩展,这些扩展常用于高效Winsock应用程序的开发)

  在编译采用了WINSOCK2.H的应用程序时,须链接到WS2_32.LIB库。使用WINSOCK.H(比如在Windows CE中)时须使用WSOCK32.LIB。如果从MSWSOCK.H中使用扩展API,还必须链接MSWSOCK.DLL。一旦包含了必需的头文件和链接环境,就可以开始编写应用程序代码了,这时需要初始化Winsock。

1.2 Winsock的初始化

  每个Winsock应用都必须加载合适的Winsock DLL版本。加载Winsock库是通过调用WSAStartup函数实现的。这个函数的定义如下:

  int WSAStartup(

    WORD wVersionRequested , //用于指定准备加载的Winsock库的版本

    LPWSADATA lpWSAData  //指向LPWSADATA结构的指针

  );

  在使用Winsock接口编写好应用程序之后,应该调用WSACleanup函数。这个函数能使Winsock释放所有由Winsock分配的资源,并取消这个应用程序挂起的Winsock调用。WSACleanup函数的定义为:

  int WSACleanup(void);

1.3 错误检查和处理

  如果调用Winsock函数时出现了错误,可以用WSAGetLastError函数来获得一段代码,这段代码专用来说明错误。该函数定义为:int WSAGetLastError(void);

1.4 协议寻址

  在Winsock中,应用程序通过SOCKADDR_IN结构来指定IP地址和服务端口信息,该结构的格式如下:

  struct sockaddr_in

  {

    short  sin_family;   //必须设为AF_INET,以告知Winsock此时正在使用IP地址族

    u_short  sin_port;

    struct   in_addr  sin_addr;//把IPv4地址作为一个4字节的量存储起来,它是无符号长整数类型

    char  sin_zero[8];//只充当填充项,以使SOCKADDR_IN结构和SOCKADDR结构的长度一样

  };

  unsigned long inet_addr(//此函数可把一个点分IP地址转换为一个32位的无符号长整数

    const char FAR *cp   //空终止字符串,用于接受点分表示法的IP地址

  );

1.5 创建套接字

  熟悉Winsock的人应该知道,API是建立在套接字概念基础上的。套接字是传输提供程序的句柄。

  SOCKET socket(int af , int type , int protocol);

1.6 面向连接的通信

1.6.1 服务器API函数

  服务器:socket()/WSASocket()-->绑定-->监听-->Accept()/WSAAccept()

  客户机:socket()/WSASocket()-->地址解析-->Connect()/WSAConnect()

1.6.1.1 绑定

  int bind(

    SOCKET s,  //代表用来等待客户机连接的那个套接字

    const struct sockaddr FAR* name,  //一个普通的缓冲区

    int namelen  //代表要传递的,由协议决定的地址结构的长度

  );

1.6.1.2 监听

  int listen(

    SOCKET s,  //被绑定的套接字

    int backlog  //指定了被搁置的连接的最大队列长度

  );

注意:一旦服务器接受了一个连接,那个连接请求就会从队列中删去,以便别人可继续发出请求。

1.6.1.3 接受连接

  SOCKET accept(

    SOCKET s,  //被绑定的套接字,它处于监听模式

    struct sockaddr FAR* addr,  //一个有效的SOCKADDR_IN结构的地址

    int FAR* addrlen  //SOCKADDR_IN结构的长度

  );

accept函数返回后,addr结构中会包含发出连接请求的那个客户机的IPv4地址信息,而addrlen参数则指出addr结构的长度。此外,accept会返回一个新的套接字描述符,它对应于已经被接受的那个客户机连接。该客户机后续的所有操作都应该使用这个新的套接字。至于原来那个监听套接字,它仍然用于接受其他客户机连接,而且仍处于监听模式。

1.6.2 客户端API函数

创建客户机只需3步操作:

1.创建一个套接字。

2.建立一个SOCKADDR地址结构,结构名称为准备连接到的服务器名(以下层协议为准)。对于TCP/IP,这是客户机应用程序所监听的服务器的IP地址和端口号。

3.用connect或WSAConnect初始化客户机与服务器的连接。

补充:

  对每个套接字来说,它的初始状态都是CLOSED。若客户机初始化了一个连接,就会向服务器发送一个SYN包,同时将客户机套接字状态置为SYN_SENT。服务器收到SYN包后,会发出一个SYN-ACK包,客户机需要用一个ACK包对它做出响应。此时,客户机的套接字将处于ESTABLISHED状态。如果服务器一直不发送SYN-ACK包,客户机就会超时,并返回CLOSED状态。

  若服务器的套接字同本地接口及端口绑定起来,并在它上面进行监听,那么套接字的状态便是LISTEN。客户机试图与服务器连接时,服务器就会收到一个SYN包,并用一个SYN-ACK包做出回应。服务器套接字的状态就变成SYN_RCVD。最后,客户机发出一个ACK包,它将服务器套接字的状态变成ESTABLISHED。

  一旦应用程序处于ESTABLISHED状态,就可以通过两种方法来关闭它。如果由应用程序来关闭,便叫做“主动套接字关闭”;否则,套接字的关闭便是被动的。如主动关闭,应用程序便会发出一个FIN包。应用程序调用closesocket或shutdown时(把SD_SEND当作第2个参数),会向通信对方发出一个FIN包,而且套接字的状态将变成FIN_WAIT_1。正常情况下,通信对方会用一个ACK包作为回应,套接字的状态随之变成FIN_WAIT_2。如通信对方也关闭了连接,它会发出一个FIN包,我们的机器则会以一个ACK包作为回应,并将套接字的状态置为TIME_WAIT。

   TIME_WAIT状态的主要作用是,在TCP连接处于2MSL等待状态的时候,定义那个连接的一对套接字不可以被重新使用。这对套接字由本地IP端口以及远程IP端口组成。某些TCP实施方案不允许重新使用处于TIME_WAIT状态下的套接字对中的任何端口号。

  int connect(

    SOCKET s,

    const struct sockaddr FAR* name,//TCP的套接字地址结构(SOCKADDR_IN)表示要连接到的服务器

    int namelen

  );

1.6.3 数据传输

注意:所有关系到收发数据的缓冲区都属于简单的char类型,即面向字节的数据。事实上,它可能是一个包含任何原始数据的缓冲区,至于这个原始数据是二进制数据,还是字符型数据,则无关紧要。

1.6.3.1 send和WSASend

  int send(

    SOCKET s,   //用于发送数据的套接字

    const char FAR * buf,  //指向字符缓冲区的指针,该缓冲区中包含即将发送的数据

    int len, //指定即将发送的缓冲区内的字符数

    int flags

  );

  int WSASend(

    SOCKET s,

    LPWSABUF lpBuffers,  //指向一个或多个WSABUF结构的指针

    DWORD dwBufferCount,  //准备传递的WSABUF结构数量

    LPOWORD lpNumberOfBytesSent,//指向DWORD(WSASend调用返回)的指针,包含已发送字节数

    DWORD dwFlags,

    LPWSAOVERLAPPED lpOverlapped,

    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

  );

1.6.3.2 WSASendDisconnect

  int WSASendDisconnect(

    SOCKET s,

    LPWSABUF lpOutboundDisconnectData

  );

1.6.3.3 recv和WSARecv

  int recv(

    SOCKET s,

    char FAR* buf,  //用于接收数据的字符缓冲区

    int len,  //准备接收的字节数或buf缓冲区的长度

    int flags

  );

  int WSARecv(

    SOCKET s,

    LPWSABUF lpBuffers,  //指向一个或多个WSABUF结构的指针

    DWORD dwBufferCount,  //准备传递的WSABUF结构数量

    LPOWORD lpNumberOfBytesRecvd,//指向DWORD(WSASend调用返回)的指针,包含已发送字节数

    LPDWORD lpFlags,

    LPWSAOVERLAPPED lpOverlapped,

    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

  );

1.6.3.4 WSARecvDisconnect

  int WSARecvDisconnect(

    SOCKET s,

    LPWSABUF lpInboundDisconnectData

  );

1.6.4 流协议

1.6.5 中断连接

1.6.5.1 shutdown

  int shutdown(

    SOCKET s,

    int how  //SD_RECEIVE , SD_SEND , SD_BOTH

  );

1.6.5.2 closesocket

  int closesocket(SOCKET s);

1.7 无连接通信

1.7.1 接收端

  int recvfrom(

    SOCKET s,

    char FAR* buf,

    int len,

    int flags,

    struct sockaddr FAR* from,

    int FAR* fromlen

  );

  int WSARecvFrom(

    SOCKET s,

    LPWSABUF lpBuffers,  //指向一个或多个WSABUF结构的指针

    DWORD dwBufferCount,  //准备传递的WSABUF结构数量

    LPOWORD lpNumberOfBytesRecvd,//指向DWORD(WSASend调用返回)的指针,包含已发送字节数

    LPDWORD lpFlags,

    struct sockaddr FAR * lpFrom,

    LPINT lpFromlen,

    LPWSAOVERLAPPED lpOverlapped,

    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

  );

 1.7.2 发送端

  int sendto(

    SOCKET s,

    const char FAR * buf,

    int len,

    int flags,

    const struct sockaddr FAR * to,

    int tolen

  );

  int WSASendTo(

    SOCKET s,

    LPWSABUF lpBuffers,

    DWORD dwBufferCount,

    LPDWORD lpNumberOfBytesSent,

    DWORD dwFlags,

    const struct sockaddr FAR * lpTo,

    int iToLen,

    LPWSAOVERLAPPED lpOverlapped,

    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

  );

1.7.3 基于消息的协议

1.7.4 释放套接字资源

1.8 其他API函数

1.8.1 getpeername

该函数用于获得通信方的套接字地址信息,该信息是关于已建立连接的那个套接字的。

  int getpeername(

    SOCKET s,

    struct sockaddr FAR* name,

    int FAR* namelen

  );

1.8.2 getsockname

该函数是对应getpeername的。它返回的是给定套接字的本地接口的地址信息。

  int getsockname(

    SOCKET s,

    struct sockaddr FAR* name,

    int FAR* namelen

  );

1.8.3 WSADuplicateSocket

此函数用来建立WSAPROTOCOL_INFO结构,该结构可传递到另一个进程,这样就可用另一个进程打开指向同一个下层套接字的句柄,如此一来,这个进程也能对该资源进行操作。注意,这一点只在进程之间的操作时才有必要;同一个进程中的线程可自由传递套接字描述符。

  int WSADuplicateSocket(

    SOCKET s,  //准备复制的套接字句柄

    DWORD dwProcessId,  //打算使用所复制的套接字为进程的ID

    LPWSAPROTOCOL_INFO lpProtocolInfo

  );

1.9 Windows CE

winsock.h , WinMain

posted @ 2013-09-20 15:41  momo_Unique  阅读(756)  评论(0编辑  收藏  举报