[MFC学习笔记]--网络编程实际操作编写服务器端

平台:win7 64bits   /   VC6++ 

语言:C++

 

函数介绍

int WSAStartup( 

                           WORDwVersionRequested,

                           LPWSADATAlpWSAData

                           );

wVersionRequested参数用于指定准备加载的Winsock库的版本。高位字节指定所需要的Winsock库的副版本,而低位字节则是主版本。由于需要返回WORDstruct,所以使用了

WORD MAKEWORD(  BYTE bLow,   BYTE bHigh );    这个函数就是返回WORD structbLow返回低位字节,bHigh返回高位字节。

wVersionRequested = MAKEWORD(2,2);

 

lpWSAData:参数是指向WSADATA结构的指针,WSAStartup用其加载的库版本有关的信息填在这里:[out] Pointer to theWSADATA data structure that is to receive details of the Windows Sockets implementation. [out]是只这个参数作为函数的返回值

//////////////////////////////////////////////////////alt+F8可以自动调整代码格式

WSAData

typedef struct WSAData { 

        WORD          wVersion;  //

       WORD                  wHighVersion

        char                  szDescription[WSADESCRIPTION_LEN+1]; 

       char                  szSystemStatus[WSASYS_STATUS_LEN+1]; 

       unsigned short        iMaxSockets;

       unsigned short        iMaxUdpDg

       char FAR *            lpVendorInfo;

} WSADATA, *LPWSADATA;

///////////////////////////////////////////////////////////////

SOCKET socket(  int af,         inttype,       intprotocol  );

 

af

[in] Address family specification. 也就是af指定了地址簇。winsock只支持AF_INET地址簇。所以设置为AF_INET

type:[in] Type specification for the new socket.   只能用SOCK_STREAM(流式套接字)与SOCK_DGRAM(数据报套接字)

 

protocol

[in] Protocol to be used with the socket that is specific to the indicated address family. 如果设置为0,那么他就会根据地址格式和套接字类别,自动为你选择一个合适的协议。这是比较推荐的使用方法,当然也也已设置为TCP,或者UDP

返回值SOCKET:返回一个描述符(类似于句柄),一个SOCKET类型的描述符。如果调用失败,就会返回一个INVALID_SOCKET,错误信息可以通过WSAGetLastError()函数返回。

///////////////////////////////////////////////////////////////

int bind(  SOCKET s,     //[in] Descriptor identifying an unbound socket.    指定要bind的套接字                 

             const struct sockaddr FAR *name,  //[in] Address to assign to the socket from theSOCKADDR structure制定了该套接字的本地地址信息,是指向sockaddr结构体的指针变量,由于该地址结构是为所有的地址家族准备的,这个结构可能随所用的网络协议不同而不同,所以要用第三个参数指定该地址结构的长度。

               int namelen      //第二个参数的长度,可以用sizeof来获得。            

              );

 

struct sockaddr{

  u_short sa_family;  //指向该地址家族,这里必须设置为AF_INET

  char sa_data[14];  //要求一块内存分配区,起到占位作用,该区域中指定与协议相关的具体地址信息。在TCP/IP中,我们可以 用sockaddr_in来代替sockaddr,以方便我们填写地址信息

};

struct sockaddr_in{

       short     sin_family;//addr family    AF_INET

      unsigned short sin_port; // PORT

       struct in_addr sin_addr; //ip ADDRS  一般设置为INADDR_ANY,如果不这么设置,需要使用inet_addr()函数。

       char      sin_zero[8];  //一个填充数字,使得 sockaddr与sockaddr_in长度一样

       

};

 

在上面结构体里,除了sin_family可以不使用网络字节序,其他的都需要转换,需用用到htonl与htons函数。hontl 传入与返回的是u_long,htons传入与返回的是u_short,所以转换端口是honts(6000),因为端口是两个字节。

 

           ///////////////////////////////////////////////////////

 

int listen(

            SOCKET s, //创建的socket描述符

             int backlog //可以监听的客户端的数字

 );

SOCKET accept(

     SOCKET s,  //处于监听状态的套接字,就是服务器的套接字

     struct sockaddr FAR* addr,  //这是一个buffer,保存了客户端的IP信息和PORT信息,在使用之前,必须先声明一个地址对象,SOCKADDR_IN addrClient;

     int FAR * addrlen   //第二参数的长度    int len = sizeof (addrClient);

);

accept会返回一个新的socket套接字描述符,这个新的套接字就可以和服务器的socket进行通信了。

/////////////////////////////////////////////////////////////

int send(

               SOCKET s, //accept新建立的socket

               const char FAR* buf, // buffer,保存了想要传输的数据

               int len, //传输数据的长度

              int flag  //设定flag 可以影响send函数的调用形式。一般设置为0

);

 send(sockConn,sendBuf,strlen(sendBuf)+1,0);strlen(sendBuf)+1是因为我们想要传输的string最后有一个“\0“字符,在接收端,可以给字符串加一个结尾符号。

 

sprintf()函数,可以把特定的数据格式化到一个buffer中间去。 sprintf(sendBuf,"Welcome %s to http://www.sina.com", inet_ntoa(addrClient.sin_addr));

将Welcome %s to http://www.sina.com"格式化到sendBuf中间去,但是地址是string.IP地址嘛。所以门后面必须加addrClient.sin_addr...inet_ntoa()函数将addrClient.sin_addr

转换为点分十进制的字符串。

////////////////////////////////////////////////////////////

int recv(  SOCKET s,         char FAR *buf,    int len,          int flags       );

第一个参数是建立连接后的套接字  第二个参数是一个接收数据的buffer,第三个参数数据的长度,,第四个是flags,通常设置为0.

 

closesocket(sockConn); //释放accept建立的socket,释放资源。就是在数据传输后。。。而WSACleanUp()是终止winsock库的使用,这之前需要释放服务器的socket。

 

在包含了Winsock2.h头文件之后,还需要连接一个库文件。

下面是介绍怎么连接库文件。

project->setings->link  然后再  Object/Library modules:框中,选到最后,添加 ws2_32.lib  ->OK

 

服务器的代码

 

//Service
#include <IOSTREAM>
#include <Winsock2.h>
#include <stdio.h>
using namespace  std;

void main()
{
 //load socket lib. Use WSAStartup()
 WORD wVersionRequested;
 WSADATA wsaData;
 int err;
 
 wVersionRequested = MAKEWORD( 2, 2 );
 
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( err != 0 ) {
 
  return;
 }
 

 
 if ( LOBYTE( wsaData.wVersion ) != 2 ||
        HIBYTE( wsaData.wVersion ) != 2 ) {
 /
  WSACleanup( );
  return;
 }
 
 /* The WinSock DLL is acceptable. Proceed. */

 //create socket.use socket();
 SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,0);
 if (sockSrv == INVALID_SOCKET)
 {

  cout<<"socket create error!"<<endl;
        int errCode = WSAGetLastError();
  cout<<errCode<<endl;
  WSACleanup( );
  return;
 }
 //bind the socket to local addr. use bind();
 //fist,set the IP and PORT.
 SOCKADDR_IN addrSrv;
 addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);// any IP addr.
 addrSrv.sin_family = AF_INET;
 addrSrv.sin_port = htons(6000);
// addrSrv.sin_zero = NULL;
 //second,use bind();
     bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
 //listen();
     if ( 0==listen(sockSrv,5))
     {
   printf("socket listening...\n");
     }
   


 //accept()

 SOCKADDR_IN addrClient;
 int len = sizeof(addrClient);
 while (1)
 {  
  SOCKET  sockConn = accept(sockSrv,(SOCKADDR*)&addrClient,&len);

  char sendBuf[100];
  sprintf(sendBuf,"Welcome %s to http://www.sina.com", inet_ntoa(addrClient.sin_addr));
  send(sockConn,sendBuf,strlen(sendBuf)+1,0);//strlen(sendBuf)+1,because,when we got the string
  

  char recvBuf[100];
  recv(sockConn,recvBuf,100,0);
  printf("%s\n",recvBuf);
  closesocket(sockConn);        
 }
}

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值