《VC++深入详解》学习笔记 第十四章 网络编程

1.协议:为进行网络中的数据交换而建立的规则、标准或约定(语义+语法+规则)

1)协议分层,各层之间单项依赖,下层向上层提供服务,对等层之间虚拟通信,实际通信在最底层完成

2)应用层:远程登陆协议Telnet,文件传输协议FTP,超文本传输协议HTTP、域名服务DNS、简单邮件传输协议SMTP、邮局协议POP3

3)传输层:传输控制协议TCP(面向连接,三次握手,数据确认和数据重传机制)

用户数据报协议UDP(无连接,实时性较高)

4)网络层:网际协议IPInternet互联网控制报文协议ICMPInternet组管理协议IGMP

5)数据封装:协议头

6)端口:为了标识通信实体中进行通信的进程(应用程序),端口是一种抽象的软件结构(包括一些数据结构I/O缓冲区)应用程序通过系统调用与某端口建立连接后,传输层传给该端口的数据都被相应的进程所接受,相应进程发给传输层的数据都通过该端口输出

           我们在编写网络应用程序时,要为程序指定1024以上的端口号,因为1024以下的端口号保留给预定义的服务,如:http使用80端口

2.套接字(socket

1)套接字存在于通讯区域中,通信区域也叫地址族,它是一个抽象的概念,主要用于将通过套接字通信的进程的共有特征综合在一起。Windows Sockets只支持一个通信区域:网际层(AF_INET),这个域被使用网际协议簇通信的进程使用

2)网络字节顺序:基于IntelCPU,即我们常用的PC机采用的是低位先存,为保证数据的正确性,在网络协议中需要指定网络字节顺序,TCP/IP协议使用16位或32位整数的最高位先存格式

3)客户机/服务器模式:网间通信是异步的,相互通信的进程间既不存在父子关系,又不存在共享内存缓冲区,需要一种机制为希望通行的进程间建立联系,为二者的数据交换提供同步,这就是基于客户机/服务器模式的TCP/IP

3.Windows Sockets的实现

1)套接字类型

     1.流式套接字(SOCK_STREAM):提供面向连接的、可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接受,流式套接字实际上是基于TCP协议实现的

     2.数据报套接字(SOCK_DGRAM):提供无连接服务,数据包以独立包形式发送,不提供无错保证,数据可能丢失或重复,并且接受顺序混乱,基于UDP协议实现

     3.原始套接字(SOCK_RAW

2)基于TCP(面向连接)的socket编程

     服务器端:

     1.创建套接字(socket

     2.将套接字绑定到一个本地地址和端口上(bind

     3.将套接字设为监听模式,准备接受客户请求(listen

     4.等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept

     5.用返回的套接字和客户端进行通信(send/recv

     6.返回,等待另一个客户请求

     7.关闭套接字

     客户端:

     1.创建套接字(socket

     2.向服务器发送连接请求(connect

     3.和服务器端进行通信(send/recv

     4.关闭套接字

3)基于UDP(面向无连接)的socket编程(对于UDP的套接字程序来说,它的服务器端和客户端这种概念不是很强化(接收端—发送端))

     接收端

     1.创建套接字(socket

     2.将套接字绑定到一个本地地址和端口上(bind

     3.等待接收数据(recvfrom

     4.关闭套接字

     客户端

     1.创建套接字(socket

     2.向服务器发送数据(sendto

     3.关闭套接字

4.相关函数

1WSAStartup函数:加载套接字库,进行套接字库的版本协商(确定使用的版本)

    函数原型:int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);

//第一个参数指定准备加载的Winsock库的版本

// lpWSAData返回值,指向WSADATA结构的指针,库版本有关的信息填充在这个结构中

         对于每一个WSAStartup函数的成功调用(即成功加载WinSock动态库后)在最后都应调用一个WSACleanUp调用,以便释放为该应用程序分配的资源,终止对WinSock动态库的使用

2socket函数:创建套接字

    函数原型:SOCKET socket(int af,int type,int protocol);

//第一个参数指定地址族,对于TCP/IP协议的套接字,只能是AF_INET(或PF_INET

//第二个参数指定Socket类型(SOCK_STREAMSOCK_DGRAM

//第三个参数是与特定的地址家族相关的协议,若为0,那么系统会自动选择

3bind函数:将套接字绑定到本地的某个地址和端口上

    函数原型:int bind(SOCKET s,const struct sockaddr FAR* name,int namelen);

//第一个参数是要绑定的套接字

//第二个参数是该套接字的本地地址信息,这是一个指向sockaddr结构的指针变量,由于该地址结构是为所有地址家族准备的,这个结构可能会随所使用的网络协议不同而不同

//第三个参数指定该地址结构的长度

        sockaddr结构:struct sockaddr{

                       u_short sa_family;//地址家族(TCP/IP协议必须设置为AF_INET

                       char sa_data[14];//要求一块内存分配区,起到占位作用,对于不同的协议家族,用不同的结构来替换sockaddr(除了sa_family外,sockaddr_in是按网络字节顺序表示的)

                                  }

        sockaddr_in结构替换sockaddr

         struct sockaddr_in{

              short sin_family;//地址族(IPAF_INET

              unsigned short sin_port;//端口

              struct in_addr sin_addr;//主机IP地址

              char sin_zero[8];//填充数(使sockaddr_in结构和sockaddr结构的长度一样)

};

         sin_addr成员的类型in_addr结构:

         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;

4)将IP地址指定为INADDR_ANY,允许套接字向任何分配给本机的IP地址发送或接受数据

     inet_addr函数:指定某一特定IP

     函数原型:unsigned long inet_addr(const char FAR* cp);//参数是一个字符串,该字符串指定了以点分十进制格式表示的IP地址(eg:192.168.1.1),该函数会返回一个适合分配给S_addru_long类型的数值

     inet_ntoa函数:与inet_addr作用相反

      函数原型:char FAR* inet_ntoa(struct in_addr in);

5listen函数:将指定的套接字设置为监听模式

      函数原型:int listen(SOCKET s,int backlog);

//第一个参数是套接字描述符

//第二个参数是等待连接队列的最大长度,如果设置为SOMAXCONN,那么下层的服务提供者将负责将这个套接字设置为最大的合理值

6accept函数:接受客户端发送的连接请求

      函数原型:SOCKET accept(SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);

//第一个参数是套接字描述符,该套接字已经通过listen函数将其设置为监听状态

//第二个参数是指向一个缓冲区的指针,该缓冲区用来接收连接实体的地址,也就是当客户端向服务器发起连接,服务器接受这个连接时,保存客户端的IP地址和端口信息

//第三个参数是一个指向整型的指针,返回包含地址信息的长度

7send函数:通过一个已建立连接的套接字发送数据

      函数原型:int send(SOCKET s,const char FAR* buf,int len,int flags);

//第一个参数是一个已建立连接的套接字,第二个参数指向存放将要传递的数据的缓冲区,//第三个参数是缓冲区的长度,第四个参数设定的值影响函数的行为,一般为0

8recv函数:从一个已连接的套接字接受数据

      函数原型:int recv(SOCKET s,char FAR* buf,int len,int flags);

//套接字,接受数据的缓冲区,缓冲区长度,影响函数行为

9connect函数:与一个特定的套接字建立连接

      函数原型:int connect(SOCKET s,const struct sockaddr FAR* name,int namelen);

//套接字,连接的服务器端地址信息,指定服务器端地址的长度

10recvfrom函数:接受一个数据报信息并保存源地址

      函数原型:int recvfrom(

SOCKET s,//准备接受数据的套接字

char FAR* buf,//指向缓冲区的指针,用来接受数据

int len,//缓冲区的长度

int flags,//影响函数行为

struct sockaddr FAR* from,//指向地址结构体的指针,主要用来接受发送数据方的地址信息

int FAR* fromlen//整型指针,并且它是一个in/out类型的参数,表明在调用前需要给它指定一个初始值,函数会通过这个参数返回地址结构的大小

);

11sendto函数:向一个特定的目的方发送数据

     函数原型:int sendto(SOCKET s,const char FAR* buf,int len,int flags,const struct sockaddr FAR* to,int tolen);

//套接字;缓冲区指针,存放要发送的数据;缓冲区中数据长度;影响函数调用行为;可选//指针,指定目标套接字的地址;上个参数中指定地址的长度

12htonshtonl函数(主机字节顺序转为TCP/IP网络字节顺序)

      u_short htons(u_short hostshort);

      u_long htonl(u_long hostlong);

5.基于TCP的网络应用程序的编写(先导入ws2_32.lib

1)服务器程序

#include<Winsock2.h>

#include<stdio.h>

 

int main()

{  

         

          WORD wVersionRequested; //保存WinSock库的版本号

          wVersionRequested = MAKEWORD(1,1);//调用MAKEWORD宏创建一个包含了请求版本号的WORD

 

          WSADATA wsaData;

   

          //保存WSAStartup函数返回值,若不为0则退出

int err = WSAStartup(wVersionRequested,&wsaData); //加载套接字库

          if(err != 0)

          {

                   return 1;

          }

 

    //判断wsaData.wVersion的低字节和高字节是否都为1,如果不是我们请求的值,退出

    if( (LOBYTE(wsaData.wVersion) != 1) || (HIBYTE(wsaData.wVersion) != 1))

          {

                   WSACleanup();

                   return 1;

          }

 

 

          //创建用于监听的套接字

          SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,0);

   

          //地址

          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));

 

          //将套接字设为监听模式,准备接收客户请求

          listen(sockSrv,5);

   

//记录客户地址

          SOCKADDR_IN addrClient;

          int len = sizeof(SOCKADDR);

 

          while(1)

          {

                   //等待客户请求到来

                   SOCKET sockConn = accept(sockSrv,(SOCKADDR*)&addrClient,&len);//该函数返回一个新的对应于此次连接的套接字

 

                   //发送数据

                   char sendBuf[100];

                   sprintf(sendBuf,"Welcome %s to Here",inet_ntoa(addrClient.sin_addr));

                   send(sockConn,sendBuf,strlen(sendBuf) + 1,0);//用已经建立连接的套接字发送

//strlen不算\0,所以要加1

 

                   //接收数据

                   char recvBuf[100];

                   recv(sockConn,recvBuf,100,0);//用已经建立连接的套接字接收

 

                   //打印接收的数据

                   printf("%s\n",recvBuf);

 

                   closesocket(sockConn);

          }

 

          return 0;

}

2)客户端程序

#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 1;

          }

 

if( (LOBYTE(wsaData.wVersion) != 1) || (HIBYTE(wsaData.wVersion) != 1))

          {

                   WSACleanup();

                   return 1;

          }

 

 

          //创建套接字

          SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);

 

          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(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

 

          //接收数据

          char recvBuf[100];

          recv(sockClient,recvBuf,100,0);

          printf("%s\n",recvBuf);

 

          //发送数据

          send(sockClient,"lisi",strlen("lisi") + 1,0);

 

          //关闭套接字

          closesocket(sockClient);

   

          //关闭套接字库

          WSACleanup();

 

          return 0;

}

6. 基于UDP的网络应用程序的编写(先导入ws2_32.lib

     1)服务器端

#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 1;

         }

 

    if( (LOBYTE(wsaData.wVersion) != 1) || (HIBYTE(wsaData.wVersion) != 1))

         {

                   WSACleanup();

                   return 1;

         }

   

         //创建套接字

         SOCKET sockSrv = socket(AF_INET,SOCK_DGRAM,0);

 

         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,len);

                            printf("Chat end!\n");

                            break;

                   }

                   sprintf(tempBuf,"%s say:%s",inet_ntoa(addrClient.sin_addr),recvBuf);

                   printf("%s\n",tempBuf);

                   //发送消息

                   printf("input\n");

                   gets(sendBuf);

                   sendto(sockSrv,sendBuf,strlen(sendBuf) + 1,0,(SOCKADDR*)&addrClient,len);

         }

         //关闭套接字

         closesocket(sockSrv);

         WSACleanup();

         return 0;

}

     2)客户端

#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 1;

         }

 

    if( (LOBYTE(wsaData.wVersion) != 1) || (HIBYTE(wsaData.wVersion) != 1))

         {

                   WSACleanup();

                   return 1;

         }

   

         //创建套接字

         SOCKET sockClient = socket(AF_INET,SOCK_DGRAM,0);

 

         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);

 

         char recvBuf[100];

         char sendBuf[100];

         char tempBuf[200];

 

         int len = sizeof(SOCKADDR);

 

         while(1)

         {

                   //发送数据

                   printf("Input\n");

                   gets(sendBuf);

                   sendto(sockClient,sendBuf,strlen(sendBuf) + 1,0,(SOCKADDR*)&addrSrv,len);

                   //等待并接收数据

                   recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrSrv,&len);

                   if('q' == recvBuf[0])

                   {

                            sendto(sockClient,"q",strlen("q") + 1,0,(SOCKADDR*)&addrSrv,len);

                            printf("Chat end!\n");

                            break;

                   }

                   sprintf(tempBuf,"%s say:%s",inet_ntoa(addrSrv.sin_addr),recvBuf);

                   printf("%s\n",tempBuf);

         }

         //关闭套接字

         closesocket(sockClient);

         WSACleanup();

         return 0;

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值