Windows程序设计__孙鑫C++Lesson14《网络编程》

Windows程序设计__孙鑫C++Lesson14《网络编程》
本节要点:
1.网络协议参考模型简介
2.套接字简介
3.网络字节顺序
4.客户机/服务器模式简介
5.Windows Sockets的实现
6.Windows网络编程函数准备
7.基于TCP的套接字编程
8.基于UDP的套接字编程
//*************************************************************************************
1.网络协议参考模型简介
OSI七层参考模型 TCP/IP的四层模型  这部分内容涉及理论知识比较丰富,请参见谢希仁《计算机网络》.
这里仅把帮助理解的图列在下面:

2.套接字简介
套接字存在于通信区域中。通信区域也叫地址族,它是一个抽象的概念,主要用于将通过套接字通信的进程的公有特性综合结合在一起。
套接字通常只与同一区域的套接字交换数据(也有可能跨区域通信,但这只在执行了某种转换进程后才能实现)。Windows Sockets只支持一个通信区域:网际域(AF_INET),这个域被使用网际协议簇通信的进程使用。
3.网络字节顺序
不同的计算机存放多字节值得顺序不同,有的机器在起始地址存放低位字节(低位先存),有的机器在起始地址存放高位字节(高位先存)。基于Intel的cpu,即我们常用的pc机采用的是低位先存。为保证数据的正确性,在网络协议中需要使用指定的网络字节顺序。TCP/IP协议使用16位整数和32位整数的高位先存格式。
4.客户机/服务器模式简介  详细内容请参见谢希仁《计算机网络》.
这里介绍如下图所示:

5.Windows Sockets的实现
(1)Windows Sockets是从伯克利套接字扩展而来,以动态链接库的形式提供给我们使用。Windows Sockets扩充主要是提供了一些异步函数,并增加了符合Windows消息驱动特性的网络事件异步选择机制。
(2)套接字的类型
流式套接字(SOCK_STREAM)提供面向连接、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收,基于TCP协议实现的。
数据报式套接字(SOCK_DGRAM)提供无连接服务。数据包以独立包形式发送,不提供无错保证,数据可能丢失或者重复,并且接收顺序混乱,基于UDP协议实现的。
原始套接字(SOCK_RAM) 这里不做介绍。
6.Windows网络编程函数准备
(1)
int WSAStartup(
  WORD wVersionRequested,//Windows Sockets版本信息 高字节指定最低版本,
                         //低位字节表示主版本 
  LPWSADATA lpWSAData  //
);
该函数加载了Ws2_32.dll动态链接库,每次成功调用后应用程序必须在使用完后调用WSACleanup 释放Ws2_32.dll的资源,终止其使用.

(2)SOCKET socket(
  int af,       //指定地址族 对于TCP/IP协议的套接字
  int type,     //指定Socket类型
  int protocol  //是与特定的地址家族相关的协议 指定为0那么他就会根据
                 地址格式和套接字类别,自动为你选择一个合适的协议
);
创建一个和指定服务提供者绑定的套接字。
(3)int bind(
  SOCKET s,     //待绑定的套接字                    
  const struct sockaddr FAR *name, //指定了该套接字的本地地址信息 指向sockaddr指针
  int namelen                       //指定第二个参数的长度
);
用来绑定一个本地地址和套接字。
struct sockaddr {
  u_short    sa_family;
  char       sa_data[14];
};  
在TCP/IP协议中,我们可以用SOCKADDR_IN结构来替换sockaddr,以方便我们填写地址信息。
struct sockaddr_in {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
};
(4)inet_addr  将包含一个IPv4地址的字符串转换为IN_ADDR结构的合适的地址
   inet_ntoa  将一个IPv4地址转换为形如a.b.c.d形式的字符串
   htonl      将一个 u_long结构的32位主机字节序的数转换为TCP/IP网络字节序
   htons      将一个 u_short结构的16位主机字节序的数转换为TCP/IP网络字节序
(5)TCP发送数据
int send(
  SOCKET s,             
  const char FAR *buf, 
  int len,              
  int flags             
);
TCP接受数据
int recv(
  SOCKET s,      
  char FAR *buf, 
  int len,       
  int flags      
);
(6)UDP发送数据
int sendto(
  SOCKET s,                       
  const char FAR *buf,           
  int len,                        
  int flags,                      
  const struct sockaddr FAR *to, 
  int tolen                       
);
UDP接受数据
int recvfrom(
  SOCKET s,                  
  char FAR* buf,             
  int len,                   
  int flags,                 
  struct sockaddr FAR *from, 
  int FAR *fromlen           
);
7.基于TCP的套接字编程
注意:添加两个程序到一个工程,通过选中组建来切换工程。先启动服务器端,后启动客户端。
TCP客户端和服务器端的编写过程如下图所示:

//实验代码如下
//*************************************************************************************
//TcpSrv.cpp
#include <Winsock2.h>
#include <stdio.h>
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD( 1, 1 );
//加载Ws2_32.dll
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
           return;
}

if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1) {
  WSACleanup( );
  return;
}

SOCKET SocketSrv=socket(AF_INET,SOCK_STREAM,0 );//step1 建立套接字
    SOCKADDR_IN  AddrSrv;
    AddrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//网络字节序
    AddrSrv.sin_family=AF_INET;
AddrSrv.sin_port=htons(6000);//端口号两个字节
bind(SocketSrv,(SOCKADDR *)&AddrSrv,sizeof(SOCKADDR));//step2 绑定到套接字
listen(SocketSrv,5);//step3 监听连接请求 5为队列长度
    SOCKADDR_IN AddrClient;
    int len=sizeof(SOCKADDR);//必须赋初值
//服务器端程序循环运行
while(1)
{
      SOCKET  ScoketConn=accept(SocketSrv, (SOCKADDR*)&AddrClient, &len);//step4 接受请求 AddrClient接收连接请求方的信息
      char SendBuf[100];
   sprintf(SendBuf,"Wellcome! Connect from %s Success!",inet_ntoa(AddrClient.sin_addr));
   send(ScoketConn,SendBuf,strlen(SendBuf)+1,0);//step5 发送数据用连接的套接字 不能用处于监听状态的套接字
   char RecvBuf[100];
      recv(ScoketConn,RecvBuf,100,0);//step5 接收数据
      printf("%s\n",RecvBuf);
   closesocket(ScoketConn);
}
//实际上还需做以下操作 但上述是死循环因此没有执行下述操作
    closesocket(SocketSrv);//关闭套接字
    WSACleanup();//终止库引用
}
//*************************************************************************************
//TcpClient.cpp
#include <Winsock2.h>
#include <stdio.h>
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD( 1, 1 );

err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
  return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1) {
  WSACleanup( );
  return;
}
SOCKET SocketClient=socket(AF_INET,SOCK_STREAM,0 );//step1 建立套接字
SOCKADDR_IN AddrSrv;
        AddrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//本地回路地址
AddrSrv.sin_family=AF_INET;
AddrSrv.sin_port=htons(6000);
connect(SocketClient,(SOCKADDR *)&AddrSrv,sizeof(SOCKADDR));//step2 连接请求
char RecvBuf[100];
recv(SocketClient,RecvBuf,100,0);//接收服务器数据
printf("%s\n",RecvBuf);
char sendbuf[100];
sprintf(sendbuf,"this is from %s","liming");
send(SocketClient,sendbuf,strlen(sendbuf)+1,0);//发送数据
closesocket(SocketClient);//关闭套接字 释放资源
WSACleanup();//终止套接字库的引用
}
//*************************************************************************************
启动程序后(注意先启动服务器程序,后启动客户端程序)运行效果:


//*************************************************************************************
8.基于UDP的套接字编程
UDP服务端和客户端编写过程如下图所示:


 

//实验代码如下
//*************************************************************************************
//NetSrv.cpp
#include <Winsock2.h>
#include <stdio.h>
void main()
{  
//加载dll文件
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
  return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1) {
  WSACleanup( );
  return;
}
SOCKET SockSrv=socket(AF_INET,SOCK_DGRAM,0);//创建基于UDP的套接字
SOCKADDR_IN AddrSrv;
    AddrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
    AddrSrv.sin_family=AF_INET;
AddrSrv.sin_port=htons(6000);
    bind(SockSrv,(SOCKADDR *)&AddrSrv,sizeof(SOCKADDR));//绑定套接字
char RecvBuf[100];
char SendBuf[100];
char TempBuf[200];
SOCKADDR_IN AddrClient;
int len=sizeof(SOCKADDR);//监听请求
while(1)
{
  recvfrom(SockSrv,RecvBuf,100,0,(SOCKADDR *)&AddrClient,&len);//接受数据
     if( 'q'==RecvBuf[0] )//对方是否想要对出聊天
  {
   sendto(SockSrv,"q",strlen("q")+1,0,(SOCKADDR *)&AddrClient,sizeof(SOCKADDR));//回发一个'q'终止聊天
      printf("chat end!\n");
   break;//终止循环
  }
       sprintf(TempBuf,"Message from %s is :%s \n",inet_ntoa(AddrClient.sin_addr),RecvBuf);
    puts(TempBuf);//显示接受数据
    printf("please reply:\n");
    gets(SendBuf);
    sendto(SockSrv,SendBuf,strlen(SendBuf)+1,0,(SOCKADDR *)&AddrClient,sizeof(SOCKADDR));//发送数据
}
//资源释放处理
closesocket(SockSrv);
WSACleanup();
}
//*************************************************************************************
//NetClient.cpp
#include <Winsock2.h>
#include <stdio.h>
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD( 1, 1 );

err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
  return;
}

if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1) {
  WSACleanup( );
  return;
}
SOCKET SockClient=socket(AF_INET,SOCK_DGRAM,0);//创建基于UDP的套接字
SOCKADDR_IN AddrClient;
        AddrClient.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
        AddrClient.sin_family=AF_INET;
AddrClient.sin_port=htons(6000);
char RecvBuf[100];
char SendBuf[100];
char TempBuf[200];
SOCKADDR_IN AddrSrv;
int len=sizeof(SOCKADDR);
while(1)
{
    printf("plase input Message\n");
    gets(SendBuf);
    sendto(SockClient,SendBuf,strlen(SendBuf)+1,0,(SOCKADDR *)&AddrClient,sizeof(SOCKADDR));//发送数据
    recvfrom(SockClient,RecvBuf,100,0,(SOCKADDR *)&AddrSrv,&len);
    if('q'==RecvBuf[0])//是否终止聊天
    {
     sendto(SockClient,"q",strlen("q")+1,0,(SOCKADDR *)&AddrClient,sizeof(SOCKADDR));
     printf("chat end!\n");
        break;
    }
    sprintf(TempBuf,"Message From %s is : %s\n",inet_ntoa(AddrSrv.sin_addr),RecvBuf);
    puts(TempBuf);
}
//资源释放处理
closesocket(SockClient);
WSACleanup();
}
//*************************************************************************************
启动程序后(注意先启动服务器程序,后启动客户端程序)运行效果:


//*************************************************************************************
本节小结"
1.认识TCP协议和UDP协议的区别
2.认识Windows sockets套接字
3.掌握简单的基于TCP、基于UDP的服务器端和客户端的程序编写
对于这几个函数中的一些参数特别是如AddrClient.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");这种语句中的数据结构,用起来并不是很方便,还有待理解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
孙鑫vc是一种特殊的混合编程语言,它结合了C语言和Verilog语言的特点。在深入详解孙鑫vc代码之前,我们先了解一下它的一些特性。 首先,孙鑫vc具有高度的可定制性。用户可以根据自己的需求选择C语言和Verilog语言中的特性来编写代码。这种灵活性使得孙鑫vc可以适用于不同的应用领域。 其次,孙鑫vc支持并行计算。它提供了一种简单而有效的方式来利用硬件资源进行并行计算,提高程序的执行效率。 另外,孙鑫vc还具有强大的调试功能。它能够在运行时对代码进行监控和调试,帮助开发者快速定位问题并进行修复。 深入详解孙鑫vc代码包括以下几个方面: 首先,我们可以从代码的结构和组织方式入手。孙鑫vc代码一般由多个模块组成,每个模块包含了各自的功能和接口。 其次,我们需要了解代码中使用的变量和数据类型。在孙鑫vc中,可以使用C语言和Verilog语言中的数据类型,如整型、浮点型等。了解这些数据类型的使用方法和限制对理解代码非常重要。 然后,我们需要分析代码中的控制流和算法。这包括了代码中的条件语句、循环语句等,以及算法的实现细节。通过对控制流和算法的分析,我们可以更好地理解代码的逻辑和实现原理。 最后,我们还需要关注代码中的接口和数据传输方式。在孙鑫vc中,模块之间通过接口进行数据的传递和交互。了解接口的定义和使用方式对于理解代码的功能和模块之间的关系非常重要。 综上所述,深入详解孙鑫vc代码需要从代码的结构和组织方式、变量和数据类型、控制流和算法、接口和数据传输方式等多个方面进行分析和理解。通过对这些方面的研究,我们可以更好地理解孙鑫vc代码,并且能够对代码进行修改和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值