socket编程为啥要用wsastartup

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket编程为啥要用wsastartup相关的知识,希望对你有一定的参考价值。

参考技术A An application must call the WSACleanup function for every successful time the WSAStartup function is called. This means, for example, that if an application calls WSAStartup three times, it must call WSACleanup three times. The first two calls to WSACleanup do nothing except decrement an internal counter; the final WSACleanup call for the task does all necessary resource deallocation for the task.

这是微软的解释,调用一次WSAStartup()就必须要调用一次WSACleanup(),如果连续3次调用WSAStartup(),那么前2次调用WSACleanup()并不释放任何资源,只是把它内部的计数器减2,直到最后一次,计数器清零,释放所有资源。本回答被提问者和网友采纳

客户端 SOCKET 编程

建立客户端的 Socket:

客户端应用程序首先也是调用 WSAStartup() 函数来初始化 Winsock 的动态连接库,然后同样

调用 socket() 来建立一个 TCP 或 UDP Socket(相同协议的Socket 才能相遇,TCP 对 TCP,UDP 对 UDP)。

与服务器的 Socket 不同的是,客户端的 Socket 可以调用 bind() 函数,来指定 IP 地址及端口号 port。

但也可以不调用 bind() 函数。而由 Winsock 来自动设定 IP 地址及端口号 port。

 

发起连接申请:

当客户端程序需要连接服务器时,客户端程序的 Socket 调用 connect() 函数监听,与 name 所指定的计算机的特定

端口上的服务器端 Socket 进行连接。函数调用成功返回 0,否则返回 SOCKET_ERROR。

 

connect() 函数原型

int connect(

    SOCKET s,          // 客户端流套接字

    const struct sockaddr FAR *name,  // 要连接的套接字的地址结构。

    int namelen        // 指明套接字的地址结构的长度。

);

函数示例:

//...

     struct sockaddr_in name;

     memset((void*)&name,0,sizeof(name));

     name.sin_family = FA_INET;

     name.sin_port = htons(80);

     name.sin_addr.s_addr = inet_addr("");

     connect(sSocket,(struct sockaddr *)&name,sizeof(name));

//...

 

inet_addr() 可以用来转化字符串,主要用来将一个十进制的数转化为二进制数,多用于 IPv4 的 ip 转化。

返回:若字符串有效则将字符串转换为32位二进制网络字节序的IPV4地址,否则为INADDR_NONE。

用 Socket 实现数据的传送:

 

服务器和客户端分别创建 Socket 并连接后,就开始数据的传递。网络编程数据传送涉及两大协议:TCP/IP 和 UDP 协议。

Stream (TCP) Socket:提供双向,可靠,有次序,不重复的资料传送。

Datagram (UDP) Socket:虽然提供双向的通信,但是没有 可靠,有次序,不重复的保证,

                                           所以 UDP 传送数据可能收到无次序,重复的资料,甚至资料在传输过程中会泄露。

一般情况下 TCP Socket 的数据发送和接收是调用 send() 和 recv() 这两个函数来完成的,

而 UDP Socket 则是调用 sendto() 和 recvfrom() 这两个函数,这两个函数调用成功返回发送或接收的资料长度,

否则返回 SOCKET_ERROR。

 

send() 函数原型:

int send(

    SOCKET s,          // 指定发送端套接字描述符。

    const char FAR *buf,  // 指明一个存在应用程序要发送数据的缓冲区。

    int len,      // 指明实际要发送的数据的字节数。

    int flags    // 一般置 0。

);

 

不论是客户端还是服务端的应用程序都用 send() 函数向 TCP 连接的另一端发送数据。

客户端一般用 send() 函数 向服务器发送请求,而服务器通常用 send() 函数来向客户端程序发送应答。

对于同步 Socket 的 send() 函数执行流程,当调用它时,send 先比较待发送数据的长度(len)和套接字s 的发送端缓冲区大小。

如果 len 大于 s 的发送缓冲区的长度,该函数就返回 SOCKET_ERROR,发送失败。

反之,那么 send 先检查协议是否正在发送 s 发送缓冲区的数据。如果是,就等待协议把数据发送完, 

如果协议还没有开始发送 s 的发送缓冲区的数据 或者 s 的发送缓冲区中没有数据,

那么send 就比较 s  的发送缓冲区的剩余空间和 len 的大小。如果 len 大于剩余空间大小,

send 就一直等待协议把 s 的发送缓冲区中的数据发送完,反之,send 就仅仅把 buf 中的数据复制到剩余空间里

 注意并不是 send 把 s 的发送缓冲区中的数据传到连接的另一端的,而是协议传的,

send 仅仅是把 buf 中的数据复制到 s 的发送缓冲区的剩余空间里)。

如果 send() 函数复制数据成功,就返回实际复制的字节数,否则,就返回 SOCKET_ERROR。

如果 send() 函数在等待协议传送数据时网络断开,也返回 SOCKET_ERROR。

send() 函数把 buf 中的数据成功复制到 s 的发送缓冲区的剩余空间中后它就返回了,但是此时这些数据

并不一定马上就传到连接的另一端。

如果协议在后续的传递过程中出现网络错误的话,那么下一个 socket 函数就会返回 SOCKET_ERROER。

  

提示: 

每一个除 send 外的 socket 函数在执行最开始总要等待套接字的发送缓冲区中的数据被协议传送完毕才能继续,

如果出现网络错误,那么该 socket 函数就返回 SOCKET_ERROR。

 

recv() 函数原型:

int recv(

    SOCKET s,      // 指定接收端套接字描述符。

    char FAR *buf,  // 指明一个缓冲区,该缓冲区用来存放 recv() 函数接收到的数据。

    int len,     // 指明 buf 的长度。

    int flags   // 一般置 0。

);

不论客户端还是服务器端应用程序都用 recv() 函数从 TCP/IP 连接的另一端接收数据。

对于同步 Socket 的 recv() 函数执行流程,当调用它时,recv() 函数先等待 s 的发送缓冲区中的数据传输完毕。

如果协议在传送 s 的发送缓冲区中的数据时出现网络错误的话,那么 recv() 函数就会返回 SOCKET_ERROER。

如果 s 的发送缓冲区中没有数据 或者数据被协议接收完毕后,recv() 函数 会先检查套接字 s 的接收缓冲区,

如果 s 的接收缓冲区中没有数据或协议正在接收数据,那么 recv() 函数就会一直等待,直到数据接收完毕。

当协议把数据接收完毕,recv() 函数就把 s 的接收缓冲区中的数据拷贝到 buf 中。

(注意协议接收到的数据可能大于 buf 的长度,所以在这种情况下需要调用几次 recv() 函数才能把 s

   接收缓冲区的数据拷贝完。recv() 函数仅仅是拷贝数据,真正的接收数据是协议完成的。)

recv() 函数返回其实是拷贝的字节数,如果 recv() 函数在拷贝时出错,那么它返回 SOCKET_ERROR。

如果 recv() 函数在等待协议接收数据时网络中断,那么它返回 0。

 

sendto() 函数原型:

int sendto(

    SOCKET s,         // 指定发送端套接字描述符。

    const char *buf,  // 指明一个存放应用程序要发送数据的缓冲区。

    int len,     // 指明实际要发送的数据字节数。

    int flags,   // 一般置 0。

    const struct sockaddr* to,  // 为指向目的地址的指针。(表示目的机的 ip 和 port).

    int tolen    // 为 to 的长度。(一般赋值为 sizeof(sturct sockaddr).

);

 

recvfrom() 函数原型:

int recvfrom(

    SOCKET s,   // 指定接收端的套接字描述符。

    char *buf,  // 指明一个缓冲区,该缓冲区用来存放 recvfrom() 函数接收到的数据。

    int len,    // 指明 buf 的长度。

    int flags,  // 一般置 0.

    struct sockaddr *from,  // 为数据包的来源地址。

    int *fromlen   // 数据包的长度。

);

 

以上是关于socket编程为啥要用wsastartup的主要内容,如果未能解决你的问题,请参考以下文章

socket编程

Socket编程,为啥客户端无法接收来自服务器的数据?

socket编程中为啥client端的可以不用bind函数绑定.而客户端必须呢?

为啥socket后,客户端可以不适用bind函数

何时/为啥使用 s.shutdown(socket.SHUT_WR)?

为啥在c++编程中,用close()关闭socket 的端口号时,提示close找不到标识符,我