WinSocket API

WinSocket API —— TCP\IP连接

要进行网络通信编程,就要用到socket(套接字)。套接字代表一个通信端口,有地址,有端口号,可连接(按类型),可收,可发。

要进行socket编程,要为工程导入库文件,添加头文件,并在程序里加载套接字库。

#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib") //2.0版本

#include <winsock.h>
#pragma comment(lib, "wsock32.lib") //1.0版本

winsock是Windows平台下的网络套接字(Socket)接口规范。它从Unix平台的Berkele y(BSD)套接字方案借鉴了许多东西。Winsock是网络编程接口,而不是协议。建立Winsock2规范的主要目的是提供一个与协议无关的传送接口。使用winsock API,可以编写基于各种网络协议(如TCP、UDP、IP)的应用程序。实际上Windows下多数要访问网络的程序其底层都调用了winsock API。winsock2的winsock API的dll文件是ws2_32.dll。

    

服务端监听过程:

1.  加载套接字库,主要是版本,注意拼接次序。

 WSADATA{ wVersion,wHighVersion,  iMaxSockets\iMaxUdpDg };

   0  == WSAStartup(MAKEWORD(1,2), &WSADATA) ;  //载入WinSocket API dll库

     w = MAKEWORD(a, b);   a = LOBYTE(w);   b = HIBYTE(w);  //版本2.1,makeword(1,2)

     l  =  MAKELONG(a, b);   a = LOWORD(l);   b = HIWORD(w);

   

2. 创建一个服务器端socket,这是一个监听套接字

    INVALID_SOCKET(socket类) !=   socket(AF_INET\UNIX\…, SOCK_STREAM\DGRAM\…, 0); //通信端点

          

3. 创建一个地址结构体,用bind函数把socket和地址绑定,只有服务器端监听套接字需要绑定:

  SOCKADDR_IN{ sin_family,  //指定地址家族

          sin_port = htons(short),

          sin_addr.s_addr = htonl(INADDR_ANY) 

                   = inet_addr("172.29.1.1")

     }               //inet_ntoa(long)

    SOCKET_ERROR(-1) !=  bind(m_Socket, (struct sockaddr *)&SOCKADDR_IN, sizeof());

 

4. 设置为监听模式:

    listen(m_Socket, SOMAXCONN); //放入等待连接队列,超出的直接丢弃

 

5. 等待客户端的连接请求:

    real_conn_Socket = accept (m_Socket, (SOCKADDR*) &SOCKADDR_IN, &len);

    //real_conn_Socket

当有客户端请求连接m_Socket,accept 函数就会执行。注意,accept函数返回一个real_conn_Socket,其实当成功连接后,与客户端custom_Socket连接的是real_conn_Socket,而不是m_Socket,进行数据传送的也是real_conn_Socket。简单的说,服务器端m_Socket只负责监听和接收连接请求。

  

6. 卸载套接字库:

   WSACleanup();

 

客户端请求连接过程:

3. 向服务器端socket发出连接请求:

    SOCKADDR_IN{ sin_family,  //指定地址家族

          sin_port = 服务器端监听套接字bind的端口,

          sin_addr.s_addr = 服务器端监听套接字bind的IP

   }

   connect(custom_Socket, (SOCKADDR*)&SOCKADDR_IN,  sizeof(SOCKADDR_IN));

客户端socket并不需要绑定IP地址和端口,服务器端socket需要绑定。如果服务器端socket不绑定,客户端socket又知道向哪个IP地址和端口发起连接请求呢?而系统会自动为客户端socket绑定一个随机的端口,服务器只要用accept函数返回的socket与客户端socket交换数据就行了,如果服务器需要查询客户端socket的IP和端口,可以查看accept函数的第二个参数记录的客户端socket地址信息。

WinSocket API —— 收发数据


然后是进行数据的传输,发送数据send:

建立连接后,服务器端的sockConn与客户端的sockClient就连接起来并且可以互相传输数据了,

int send(

SOCKET s,                    //连接socket,非监听socket

const char FAR *buf,     //要发送数据buf的地址

int len,                         //buf的长度

int flags                        //一般设置为0即可。

); 

接收数据使用recv函数

int recv(

SOCKET s,              

char FAR *buf,         //要发送数据buf的地址。

int len,                     //要接收数据buf的长度

int flags                    //一般设置为0即可。

);

  

    

使用closesocket函数关闭其中一端socket后,连接断开发生的各种情况:

关闭socketA之后:

对socketA使用recv函数接收数据,和使用send函数发送数据,都会马上返回SOCKET_ERROR。

之后另一端socketB:

1.socketB第一次send函数能成功返回发送数据的大小,并不会返回SOCKET_ERROR 。但socketA是无法接收到的。之后socketB使用send函数返回的都是SOCKET_ERROR 。

2.socketB使用recv函数接收数据。如果socketB之前没有使用send函数,那么recv函数的返回值总是0。直到socketB调用过send函数之后,recv函数的返回值总是SOCKET_ERROR。

因此要考虑A端和B端如何协调关闭连接。socketA想要关闭连接,除了closesocket(socketA)之外,还要另外通知socketB,不然socketB还在那里傻傻的接收/发送数据。

如果不想显式的通知,socketB就要自己判断,如果socketB多次调用send函数总是返回SOCKET_ERROR或者socketB多次调用recv函数总是返回0或SOCKET_ERROR,那就要意识到连接很可能已经断开了。

 

数据的流向是单向的:

例如说数据只从socketA流向socketB(类似文件传输就是这样),那么socketA只会调用send函数,而socketB只会调用recv函数,这时候如果其中一方要停止数据的传输,就会有两种情况出现:

1.如果socketA的计算机不想发送数据而closesocket(socketA),由于socketB从来不调用send函数,因此socketB的recv函数总是返回0,那么socketB的计算机就要意识到socketA很可能已经关闭了,让socketB的计算机closesocket(socketB)。

2.如果socketB的计算机不想接收数据而closesocket(socketA),这时socketA继续调用send函数发送数据,第一次send还是成功的,但从第二次send开始就总会返回SOCKET_ERROR,但是socketA的计算机无法判断send函数返回SOCKET_ERROR是由socketB关闭造成的还是Asocket关闭造成的(因为socketA关闭后socketA调用send函数也是返回SOCKET_ERROR),因此socketA的计算机无法判断socketA关闭了没有,简单的解决方法是如果socketB的计算机不想接收数据,先不要关闭socketB,而是发通知给Asocket的计算机告诉它我不想收数据了,socketA的计算机收到通知后关闭socketA,这样情形就回到上面情况1去了,而且也知道socketA调用send函数返回SOCKET_ERROR肯定是由于socketA关闭造成的而不是由socketB关闭造成的。


你可能感兴趣的:(api,socket,网络,服务器,网络协议,Sockets)