第一章 Winsock简介

     
     Windows应用程序可以使用SDK提供的Winsock功能加入网络数据通信的功能。Winsock并非是一组用于网络数据通信的协议,而是基于现有的一些网络通信协议如TCP、UDP、IP等等通过一组API函数为Windows程序员提供了一个网络数据通信接口,通过这个接口Windows程序员不必了解网络底层通信协议的细节就能够编写出带有网路通信功能的应用程序。
     使用Winsock的应用程序都必须在使用这组API函数之前初始化使用环境而在使用完成以后清理使用环境。我们通过调用WSAStartup函数来初始化使用环境,这个函数接收两个参数,第一个参数是一个DWORD值,他的高位表示了要使用的Winsock的主版本号而低位表示副版本号,在使用中我们通常通过MAKEWORD这个宏来获得这个DWORD值;第二个参数是一个指向WSADATA对象的指针,这个指针指向的对象在函数返回时保存着当前操作系统所包含的Winsock信息。我们通常在使用Winsock后通过调用WSAClean来清理环境。下面我们将通过有连接传输(TCP)和无连接传输(UDP)两个部分来介绍Winsock的数据传输功能,在两个部分当中我们都使用C/S的程序结构。
一、有连接传输
1.服务器端
     在服务器端程序行为可以概括如下:初始化环境、获得SOCKET对象、设置IP地址值、绑定SOCKET对象和IP地址、开始监听、允许客户端连入、传输数据、关闭连接、清理环境。针对初始化环境和清理环境的部分上文已经详细描述过了,这里就不再描述。
     获得SOCKET对象;每个使用Winsock功能进行网络数据通信的应用程序都必须在数据通信之前获得一个SOCKET对象。获得SOCKET对象的方法是调用socket函数,该函数的原型如下:
 

     设置IP地址值;对于服务器我们需要设置的是服务器在主机上说要监听的端口号,而对于IP地址由于服务器应该为所有的联网计算机提供服务因此我们必须将他设置成一个特殊的常量值:IPADDR_ANY。设置IP地址的方法是设置一个SOCKADDR_IN结构,该结构的原型如下:

 
     绑定;在获得SOCKET对象和设置IP地址以后我们需要将两者绑定,绑定的方法是调用bind函数,原型如下:
 
     开始监听;现在已经一切就绪,这个时候我们只需要将服务器置入监听状态等待客户端连接的到来。置入监听状态的方法是调用listen函数,原型如下:
 
     允许客户端连入;当有客户端连入时,我们可以调用accept函数来允许连入,该函数原型如下:

参数当中的所有SOCKET对象以及IP地址对象都是针对服务器的,而返回的一个SOCKET对象是连入的客户端的SOCKET对象,后面我们将要使用这个SOCKET对象来收取客户端发来的信息。
     接收信息;此时我们拥有了客户端的SOCKET对象,这样我们利用这个对象和recv函数就可以开始数据的接收了,数据接收需要一个数据缓冲区来储存。recv函数的原型如下:
     关闭连接;此时数据传送完毕我们可以通过调用closesocket来关闭客户端和服务器端的SOCKET对象。
下面是一个简单的例程:
在这个例程当中我们多次看到htons这个函数,这个函数的作用是将主机中2进制排列转换为网络上标准的数据排列,相关的还用htonl、ntohs等等,n和h表示转换关系,而l和s表示转换的是short或者long类型的数据。
2.客户端
     客户端的行为可以概括为初始化环境、获得SOCKET对象、设置IP地址值、连接到服务器、传输数据、关闭连接、清理环境。针对初始化环境、获得SOCKET对象、设置IP地址值和清理环境的部分上文已经详细描述过了,这里就不再描述。
     连接到服务器我们需要调用connect函数,这个函数的原型如下:
 
     传输数据;连接到服务器后我们可以通过调用send函数向服务器发送数据,send函数的原型如下:


     完成传输数据后我们必须要关闭客户端到服务器端的连接,客户端的关闭连接相对服务器端要复杂,首先我们要调用一个带SD_SEND参数的shutdown函数来告诉服务器端客户端不在发送数据(从底层讲等于是向服务器端发送一个FIN包);然后再调用closesocket函数来关闭客户端的SOCKET对象。
     下面是一个客户端的简单例程:
3.一个注意事项
     由于TCP协议是一个流式的协议,对于流式的网络传输协议,假设我们要求传输N字节的数据,而事实上一次传输并不一定传输N字节,它会根据网络实际状况、接收端窗口大小等将数据分块传输,我们在send和recv函数的返回值上也看到了它标示一个已传输的值,因此根据这个值我们有可能需要重新定位并重新传输。下面是一个简单的例程,这里仅仅包括传输部分的代码,初始化和清理的部分均被省略:
char data[2048];
int nLeft = 2048;
int nIndex = 0;
while (nLeft > 0)
{
    int Ret = send(ClientSocket,&data[nIndex],nLeft,0);
    nLeft -= Ret;
    nIndex += Ret;
}
二、无连接传输
 1.服务器端
      无连接传输的服务器端行为可以描述为:初始化环境、创建SOCKET对象、设置地址对象、绑定SOCKET与地址、接收数据、清理环境。
      这里的大部分内容与有连接传输相似,不同的是首先我们在创建SOCKET对象时需要将它设置成UDP的方式,方法是将第二和第三个参数设置成SOCK_DGRAM和IPPROTO_UDP;另外我们不在需要监听窗口和接收客户端连接,而只需要使用recvform函数来接收数据,recvfrom函数的原型如下:
下面是一个简单的服务器端例程:
2.客户端
     客户端的行为可以描述为:初始化环境、创建SOCKET对象、设置地址、发送数据、释放环境。客户端的行为与有连接方式相似,不同之处在于SOCKET对象必须被设置为UDP方式,而发送数据必须使用sendto函数,该函数的原型如下:
下面是一个简单的客户端例程:
三、两种方式的区别
     TCP和UDP两种方式从其原理上讲他们之间的区别在于数据的传输是否可靠,根据TCP协议的内容,在进行正式的数据传输之前服务器端和客户端必须进行两次连接确认(过程是,客户端向服务器端发送SYN包,服务器接收到客户端的SYN包后向客户端发送SYN_ACK包,此时客户端接收到SYN_ACK包进入数据发送等待状态,并向服务器端发送ACK包,服务器端接收到ACK包后进入数据接收等待状态并且开始数据的收发),因此TCP协议一旦开始收发数据可以保证服务器端与客户端正确连接;而UDP协议没有这个确认的过程,因此无论服务器与客户端有没有正确连接数据都会被发送。另外一个区别是TCP是基于流的一种传输方式,而UDP是一种基于离散数据包的传输方式。
     面向流方式和面向数据包方式的区别在于:面向数据包的方式发送端离散的发送数据包而接收端接收到的这些数据包仍然是离散的;面向流方式发送端发送的数据包会进入网络缓存而不是直接发往线上,在缓存当中的数据包会根据一个特定的算法平衡接收端等待时间和数据包大小对于校验的资源消耗来合并为一个较大的数据包然后被发送到线上,作为接收端接收到的将是一个被合并过的较大的数据包,这时接收端可以通过指定需要数据的大小从缓存中获取被合并的数据包当中的部分需要数据。作为这两种方式的一种妥协还有一种称之为伪流的方式,这种方式的实质是面向数据包的方式,而他的改进在于接收端,他将接收端接收到的数据根据某种算法进行合并。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值