blog

枪手亨利

博客园 首页 新随笔 联系 订阅 管理

第一步:The WSAStartup function initiates use of  WS2_32.DLL by a process.

在所有 Windows Sockets 函数中,只有启动函数 WSAStartup() 和终止函数 WSACleanup() 是必须使用的。

  启动函数必须是第一个使用的函数,而且它允许指定 Windows Sockets API 的版本,并获得 SOCKETS的特定的一些技术细节。本结构如下:

int PASCAL FAR WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
  其中 wVersionRequested 保证 SOCKETS 可正常运行的 DLL 版本,如果不支持,则返回错误信息。
我们看一下下面这段代码,看一下如何进行 WSAStartup() 的调用

WORD wVersionRequested;// 定义版本信息变量
WSADATA wsaData;//定义数据信息变量
int err;//定义错误号变量
wVersionRequested = MAKEWORD(1,1);//给版本信息赋值
err = WSAStartup(wVersionRequested, &wsaData);//给错误信息赋值
if(err!=0)
{
return;//告诉用户找不到合适的版本
}
//确认 Windows Sockets DLL 支持 1.1 版本
//DLL 版本可以高于 1.1
//系统返回的版本号始终是最低要求的 1.1,即应用程序与DLL 中可支持的最低版本号
if(LOBYTE(wsaData.wVersion)!= 1|| HIBYTE(wsaData.wVersion)!=1)
{
WSACleanup();//告诉用户找不到合适的版本
return;
}
//Windows Sockets DLL 被进程接受,可以进入下一步操作
  关闭函数使用时,任何打开并已连接的 SOCK_STREAM 套接字被复位,但那些已由 closesocket() 函数关闭的但仍有未发送数据的套接字不受影响,未发送的数据仍将被发送。程序运行时可能会多次调用 WSAStartuo() 函数,但必须保证每次调用时的 wVersionRequested 的值是相同的。

第二步:在初始化了WSAStartup后建立Socket

               MSDN:The socket function creates a socket that is bound to a specific service provider.

初始化WinSock的动态连接库后,需要在服务器端建立一个监听的Socket,为此可以调用Socket()函数用来建立这个监听的Socket,并定义此Socket所使用的通信协议。此函数调用成功返回Socket对象,失败则返回INVALID_SOCKET(调用WSAGetLastError()可得知原因,所有WinSocket 的函数都可以使用这个函数来获取失败的原因)。

SOCKET PASCAL FAR socket( int af, int type, int protocol )
参数: af:目前只提供 PF_INET(AF_INET);
type:Socket 的类型 (SOCK_STREAM、SOCK_DGRAM);
protocol:通讯协定(如果使用者不指定则设为0);

如果要建立的是遵从TCP/IP协议的socket,第二个参数type应为SOCK_STREAM,如为UDP(数据报)的socket,应为SOCK_DGRAM。

第三步:绑定(服务器端需要)

MSDN:The bind function associates a local address with a socket.

     为服务器端定义的这个监听的Socket指定一个地址及端口(Port),这样客户端才知道待会要连接哪一个地址的哪个端口,为此我们要调用bind()函数,该函数调用成功返回0,否则返回SOCKET_ERROR。
int PASCAL FAR bind( SOCKET s, const struct sockaddr FAR *name,int namelen );

参 数: s:Socket对象名;
name:Socket的地址值,这个地址必须是执行这个程式所在机器的IP地址;
namelen:name的长度;
     如果使用者不在意地址或端口的值,那么可以设定地址为INADDR_ANY,及Port为0,Windows Sockets 会自动将其设定适当之地址及Port (1024 到 5000之间的值)。此后可以调用getsockname()函数来获知其被设定的值。

上面那个socketaddr是这样定义的:The sockaddr structure varies depending on the protocol selected. Except for the sin*_family parameter, sockaddr contents are expressed in network byte order.

这个结构经常被

struct sockaddr_in {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
};

所代替

in_addr是一个联合体

定义如下

typedef struct in_addr {
  union {
    struct {u_char s_b1,s_b2,s_b3,s_b4;} S_un_b;
    struct {u_short s_w1,s_w2;} S_un_w;u_long S_addr;
            }S_un;
} in_addr;

绑定套接字

 //绑定套接字
 sockaddr_in service;
 service.sin_family = AF_INET;
 service.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
 service.sin_port = htons(27015);

 bind(SocketSvr,(SOCKADDR*)&service,sizeof(service));

 //调用listen来监听客户端发来的消息
 //下面的是引用与MSDN上面的文档说明
 //To accept connections, a socket is first created with the socket function and bound to a local address with the bind function,
 //a backlog for incoming connections is specified with listen,
 //and then the connections are accepted with the accept function.
 //Sockets that are connection oriented, those of type SOCK_STREAM for example,
 //are used with listen. The socket s is put into passive mode where incoming connection requests are acknowledged and queued pending acceptance by the process.
    //下面是我的翻译
 //为了接收消息,开始应该用一个socket创建一个套接字对象,然后利用bind函数把本地地址和创建
 //的socket对象帮定到一起
 //blaklog表示同时允许的监听最大的数,然后用accept函数去接受消息队列中的消息
 //面向连接的socket,以SOCK_STREAM类型为例,应该使用listen方法
 //当外来连接请求被进程确认并置于末端队列中时sockets进入被动模式 

第四步:监听,接受和处理套接字

 当服务器端的Socket对象绑定完成之后,服务器端必须建立一个监听的队列来接收客户端的连接请求。listen()函数使服务器端的Socket 进入监听状态,并设定可以建立的最大连接数(目前最大值限制为 5, 最小值为1)。该函数调用成功返回0,否则返回SOCKET_ERROR。

int PASCAL FAR listen( SOCKET s, int backlog );
参 数: s:需要建立监听的Socket;
backlog:最大连接个数;

  服务器端的Socket调用完listen()后,如果此时客户端调用connect()函数提出连接申请的话,Server 端必须再调用accept() 函数,这样服务器端和客户端才算正式完成通信程序的连接动作。为了知道什么时候客户端提出连接要求,从而服务器端的Socket在恰当的时候调用accept()函数完成连接的建立,我们就要使用WSAAsyncSelect()函数,让系统主动来通知我们有客户端提出连接请求了。该函数调用成功返回0,否则返回SOCKET_ERROR。

代码于下

listen(SocketSvr,5);
 
 
 //accept函数的使用
 
 sockaddr_in client;
 int ClientLen=sizeof(sockaddr_in);
 
 while(1)
 {
  //accept函数调用成功后将返回一个soket对象,所以在使用accept的时候要建立一个socket对象
  //如果建立失败参考MSDN
  SOCKET Receive;
  Receive=accept(SocketSvr,(SOCKADDR*)&client,&ClientLen);
  //建立一个内存缓冲区来存放接收到的字符串
  char buffer[50];
  //格式化该内存区
  sprintf(buffer,"%s:欢迎来到深蓝空间",inet_ntoa(client.sin_addr));
  //调用send发送消息
  send(Receive,buffer,sizeof(buffer)+1,0);
  //调用recv接收消息
  char recvBuffer[100];
   recv(Receive,recvBuffer,100,0);
  printf("%s",recvBuffer);
  closesocket(Receive);
 }

下面就完整的服务器端代码

#include <Winsock2.h>
#include <stdio.h>

int main()
{
    WORD wVersionRequested;
 WSADATA wsaData;
 int err;
 
 wVersionRequested = MAKEWORD( 1, 1 );
 
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( err != 0 ) {
  
  return 0;
 }
 
 
 if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )
 {
  WSACleanup( );
  return 0;
 }
 SOCKET SocketSvr;
 SocketSvr=socket(AF_INET,SOCK_STREAM,0);
 sockaddr_in service;
 service.sin_family = AF_INET;
 service.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
 service.sin_port = htons(27015);
 
 bind(SocketSvr,(SOCKADDR*)&service,sizeof(service));
 
 listen(SocketSvr,5);
 

 sockaddr_in client;
 int ClientLen=sizeof(sockaddr_in);
 
 while(1)
 {
  
  SOCKET Receive;
  Receive=accept(SocketSvr,(SOCKADDR*)&client,&ClientLen);
  char buffer[50];
  sprintf(buffer,"%s:欢迎来到深蓝空间",inet_ntoa(client.sin_addr));
  send(Receive,buffer,sizeof(buffer)+1,0);
  char recvBuffer[100];
  recv(Receive,recvBuffer,100,0);
  printf("%s",recvBuffer);
  closesocket(Receive);
 }
 
 return 0;
}

终于搞完了

晚上把客户端搞定 看MSDN看得我累死

还有一点就在连接的时候别忘记了包含Ws2_32.lib.头文件

posted on 2005-11-13 09:16  henry  阅读(235)  评论(0)    收藏  举报