首页
社区
课程
招聘
[翻译][翻译]《Windows网络编程-第二版》(二)
发表于: 2007-5-21 00:00 7210

[翻译][翻译]《Windows网络编程-第二版》(二)

2007-5-21 00:00
7210
【译者语】先声明自己是菜鸟,而且在安全类论坛发此类文也不甚合适;只是有感于这里气氛如家,便想为各位大虾小虾做点啥。可惜本人在技术上实在没啥可称道,连个像样原创也拿不出。也不敢跑到翻译区去班门弄斧。于是乎找了一本自己还算能看懂的E书随便开始译了,就当提高自己的E文水平罢。既然所有译书的人都曾曰过:“本人水平有限,错误之处在所难免,忘各位批评指正!”那本人也不例外,水平有限…… 大家看完别骂就万幸了
因为原书也不算薄,所以要要经过一个比较漫长的过程才能看完全部,请忍耐……

===============================
【书接上回。。。】
1.7 创建套接字
实际上,一个socket(套接字,以后就不翻译了,因为还是叫socket顺口)就是一个传输提供者的句柄。有两个可创建socket的函数:socket函数以及WSASocket函数。本章,我们暂时先讨论socket函数。
socket函数定义如下:
SOCKET socket (
int af,
int type,
int protocol
);


第一个参数,af,是协议地址蔟。我们目前先学习使用的是IPV4协议,当然你也可以把该字段设置位"AF_INET"。第二个参数,type,是协议socket类型。当你打算用TCP/IP建立socket时,该字段应填充为"SOCK_STREAM";如果打算用UDP/IP建立socket,应填充为"SOCK_DGRAM"。第三个参数,protocol,用于指定通信协议所使用的编址方案(若某一协议有不止一种的编址方案),对于TCP协议,应该填充"IPPROTO_TCP";对于UDP协议,应该填充"IPPRORO_UDP"。

在Winsock编程中,有两种常见的通信方法:基于连接的和基于非连接的通信。

1.8 基于链接的通信示例
在本节,我们会讲到接收连接和拆除连接的必要函数。首先我们来看如何建立一个用于侦听客户连接请求的服务器端程序,并且看看该如何响应或者拒绝它的请求。然后我们再来学习如何建立一个客户端,使得它能够和服务器端程序建立初始连接。最终,我们在此基础上来实现基于连接的会话。

★服务器端的API函数
服务器端程序实际上就是等待来自客户端的请求并进行响应的程序。所以服务器端必须要侦听该请求,并且它要有一个客户端知道的名称,以便客户端可以向它发出请求。在TCP/IP协议下,该名称就是IP地址和端口号。
每种协议都有不同的编址(即命名)方案。首先,我们用socket函数或WSASocket函数(后面会讲到)来建立socket,然后赋予协议一个用于通讯的名称(地址),我们把这一步操作叫做地址绑定。然后,我们通过调用linsten函数(使用前先要完成地址绑定)把socket转化成侦听模式。最后,有当客户端试图连接时,服务器端就可以调用accept函数或者WSAAccept函数来接受连接(这两个函数也需要地址绑定后才能正常使用)。

地址绑定
一旦我们建立了某一个协议的socket,紧接着就要进行地址绑定。bind函数可以实现这一操作:
int bind(
    SOCKET s, 
    const struct sockaddr FAR* name, 
    int namelen
);

参数s,是一个将要接受客户端连接的socket。第二个参数name是为socket指定的地址,它是一个指向sockaddr类型结构体的指针;所以你在调用bind函数时必须给出一个填写好的sockaddr结构体。Winsock的头文件中,定义SOCKADDR为sockaddr类型的结构体,我们暂时就先用它。第三个参数name简单说来,就是当前已传递的特定协议地址结构的大小。也许这么说不太易于理解,那么先看看下面这个使用TCP连接的例子:
SOCKET               s;    
SOCKADDR_IN          tcpaddr;
int                  port = 5150;
 
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 
tcpaddr.sin_family = AF_INET;
tcpaddr.sin_port = htons(port);    
tcpaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 
bind(s, (SOCKADDR *)&tcpaddr, sizeof(tcpaddr));

通过这个例子,你首先看到一个socket被创建,紧接着建立了TCP/IP地址结构。在本例中,socket被绑定到系统默认IP接口上(通过使用INADDR_ANY这个特殊地址),并且指定占用5150端口。我们也可以明确的指明一个正确的IP地址,不过如果我们用INADDR_ANY,我们就可以使用现有系统中任何存在的接口,而且可以接收正在侦听的socket上来自户要求的任何接口(当然端口号要正确)。bind函数的调用,实现了在socket与本地IP接口以及端口进行绑定。

侦听
bind函数帮我们完成了地址绑定,而listen函数可以把当前socket的状态转化为“侦听”,该函数定义如下:
int listen(
    SOCKET s, 
    int    backlog
);

介绍一下参数含义吧:第一个参数是一个已进行了地址绑定的socket。第二个参数backlog,指定了未决连接队列的最大长度。这个参数对于服务器端程序处理并发连接非常重要。举例来说,如果我们把backlog参数设置成2,此时却同时收到3个客户端请求,则其中两个会被放进peding队列中,服务器程序能够处理该请求。剩下的那个请求会以WSAECONNREFUSED(拒绝连接)失败。一但服务器端程序接受了连接,相应连接请求就会从对列中删除。
在调用listen函数时,最有可能遇到两个错误:一个是WSAEINVAL(winsocket初始化错误),通常是由于你忘记进行地址绑定而引发的;另一个是WSAEADDRINUSE(winsocket地址被占用),通常是因为你正在调用的listen函数同地址绑定相冲突。该错误多数是由于调用bind函数有问题而造成的。

接受连接
接受连接的函数有accept、WSAAccept、AcceptEx。先讲accept函数,另外两个以后在讲。accept函数定义如下:
SOCKET accept(
    SOCKET s, 
    struct sockaddr FAR* addr, 
    int FAR* addrlen
);

参数s是一个已置于侦听状态的进行了地址绑定的socket。参数addr可以填充一个SOCKADDR_IN类型的地址结构,而相应的,参数addrlen是SOCKADDR_IN结构的长度。该函数接收并发连接队列中的第一个请求。当accept函数返回时,add结构体容纳了客户端的IPV4地址信息,而addrlen参数包含addr结构体的长度。另外,accept函数返回了一个代表所接受客户端的新socket。在所有对客户端的操作,该新的socket都会被用到。而原有的侦听socket然保持侦听状态以接收其它客户端连接请求。
如果有错误出现, 该函数将会返回INVALID_SOCKET。最常见的错误是WSAWOULDBLOCK;当处于“监听”状态的socket异步运行时会出现这个错误;当socket处于非阻塞状态或没有可以接受的连接时也可能会出现。至于阻塞、非阻塞等概念在第5章介绍。
到此,我们讨论了用于构建基本TCP/IP服务器端的所有因素。下面给出一个代码片段做为完整示例(【译注】注释就不译了,如果强烈要求翻译,请给我发消息):
#include <winsock2.h>
void main(void)
{
   WSADATA              wsaData;
   SOCKET               ListeningSocket;
   SOCKET               NewConnection;
   SOCKADDR_IN          ServerAddr;
   SOCKADDR_IN          ClientAddr;
   int                  Port = 5150;
 
   // Initialize Winsock version 2.2
 
   WSAStartup(MAKEWORD(2,2), &wsaData);
 
      // Create a new socket to listen for client connections.
      ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      // Set up a SOCKADDR_IN structure that will tell bind that we
      // want to listen for connections on all interfaces using port
      // 5150. Notice how we convert the Port variable from host byte
      // order to network byte order.
      ServerAddr.sin_family = AF_INET;
      ServerAddr.sin_port = htons(Port);  
      ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
 
      // Associate the address information with the socket using bind.
 
      bind(ListeningSocket, (SOCKADDR *)&ServerAddr,
      sizeof(ServerAddr));
 
  // Listen for client connections. We used a backlog of 5, which
   // is normal for many applications.
 
      listen(ListeningSocket, 5); 
 
   // Accept a new connection when one arrives.
 
      NewConnection = accept(ListeningSocket, (SOCKADDR *)
                          &ClientAddr,&ClientAddrLen));
 
   // At this point you can do two things with these sockets. Wait
   // for more connections by calling accept again on ListeningSocket
   // and start sending or receiving data on NewConnection. We will
   // describe how to send and receive data later in the chapter.
   // When you are finished sending and receiving data on the
   // NewConnection socket and are finished accepting new connections
   // on ListeningSocket, you should close the sockets using the
   // closesocket API. We will describe socket closure later in the 
   // chapter.
 
      closesocket(NewConnection);
      closesocket(ListeningSocket);
 
   // When your application is finished handling the connections,
   // call WSACleanup.
      WSACleanup();
}
 


【未完待续。。。】

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 10395
活跃值: (2787)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
抢个SF慢慢跟楼主学吧!
2007-5-21 16:47
0
雪    币: 314
活跃值: (15)
能力值: ( LV12,RANK:410 )
在线值:
发帖
回帖
粉丝
3
支持一下
2007-5-24 17:38
0
游客
登录 | 注册 方可回帖
返回