Windows编程(9)|SDK网络编程

一. IP地址

  1.IP地址是一个逻辑地址,32位,4个字节组成,常用点分十进制表示

   2.网络中每台主机都有个IP地址,用来标识一台主机,具有全球唯一性

   3.网络中的主机之间要进行交流,需要的是IP地址来确认

 

二.网络协议

   是计算机之间数据交换的一种规则和标准,如果没有协议,数据是无法进行交换,正如两个人之间的交流,要使用同一种语言才能进行交流,计算机也是一样

   1.OSI七层参考模型

Windows编程(9)|SDK网络编程_第1张图片

 

       在通信实体之间的对等层之间是不能通信的,并且一个通信实体上各层之间有严格的单向依赖,上层使用下层提供的服务,下层为上层提供服务,

而实体之间的实际通信是由最底层的物理层使用物理介质传输数据

 Windows编程(9)|SDK网络编程_第2张图片

 

 

 

(1).应用层:负责程序和网络服务的接口工作,使用报文的数据单元,处理的地址是进程的标识和端口号,使用到的协议有远程登录协议Telnet、文件传输协议FTP、

    超文本传输协议HTTP、域名服务DNS、简单邮件传输协议SMTP、邮局协议POP3.

(2).表示层:定义数据的格式和加密数据,

(3).会话层:主要组织.协商和管理通信的应用程序进程之间的会话,对进程之间的消息进行管理,为会话的实体建立连接和维持联系状态

(4).传输层:在连接的实体之间建立传输链路,有两种协议可选择,

      传输控制协议TCP: 面向连接的可靠的传输协议。

      用户数据报协议UDP: 是无连接的,不可靠的传输协议。

  处理的地址是进程标识.TCP端口和UDP端

(5).网络层:使用IP地址寻址,通过路由选择算法为数据分组通过通信子网选择最适当的路径,并提供网络的互联和拥塞功能,数据单元是分组数据,处理地址是IP地址,

    使用的协议为网际协议IP、Internet互联网控制报文协议ICMP、Internet组管理协议IGMP.

(6).数据链路层:在相邻的节点间的链路上,进行以”帧”为单位的无差错的传输数据

(7).物理层:物理设备之间的通信

 

2.数据的封装

要从一台主机上发送数据到另外一台数据上,首先要进行打包封装,也就是在数据前面加上特定的协议头部,

Windows编程(9)|SDK网络编程_第3张图片

 

 

 3.TCP/IP模型

    (1).有4层:应用层,传输层,网络层,网络接口

 

Windows编程(9)|SDK网络编程_第4张图片

 

         (2).TCP/IP模型与OSI七层模型

Windows编程(9)|SDK网络编程_第5张图片

 

  4.端口

     传输层提供了进程的通信能力,为了标识实体中通信的进程,TCP/IP采用了协议端口,简称端口,程序与某个端口绑定后,传输层的数据的接收和发送都经过这个端口,

端口使用一个16位的数字来表示,它的范围是0~65535,1024以下的端口号保留给预定义的服务

 

三.客户机/服务器模式(C/S)编程

  1. Socket(套接字)是一种网络编程接口,用于描述IP地址和端口,有了套接字后,可以很方便的访问TCP/IP协议,使得开发网络程序更加的容易,

       流式套接字(SOCK_STREAM):提供面向连接、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收。

      数据报式套接字(SOCK_DGRAM): 提供无连接服务。数据包以独立包形式发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。

      原始套接字(SOCK_RAW)。

  2.服务器

首先服务器方要先启动

     ①打开一个通信通道并告知本地主机,它愿意在某一地址和端口上接收客户请求。

     ②等待客户请求到达该端口。

     ③接收到重复服务请求,处理该请求并发送应答信号。接收到并发服务请求,要激活一个新的进程(或线程)来处理这个客户请求。新进程(或线程) 处理此客户请求,

         并不需要对其它请求作出应答。服务完成后,关闭此新进程与客户的通信链路,并终止。

    ④返回第二步,等待另一客户请求。

    ⑤关闭服务器。

   3.客户机

   ①打开一个通信通道,并连接到服务器所在主机的特定端口。

   ②向服务器发服务请求报文,等待并接收应答;继续提出请求。

   ③请求结束后关闭通信通道并终止。

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

Windows编程(9)|SDK网络编程_第6张图片

Windows编程(9)|SDK网络编程_第7张图片

 

5. 基于UDP(面向无连接)的socket编程

 

Windows编程(9)|SDK网络编程_第8张图片

 

6.TCP编程实现

  服务端:

   1、初始化winsocket 库  //任何网络应用程序必须做的一步 :WSAStartup();

   2、创建套接  socket();

   3、绑定本机的某个ip地址和端口上    bind()

   4、监听   listen()

   5、接受用户的请求   accept();   //阻塞的函数,如果没有客户请求,将等待

   6、通讯   send(),recv()

  客户端:

    1、初始化winsocket 库  //任何网络应用程序必须做的一步    WSAStartup();

   2、创建套接字    socket();

   3、连接服务端  connect();

 

 

TCP服务端代码......

//基于TCP网络编程,服务端
//VC6.0环境
//1. 加载套接字库
//使用函数int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);
//第一参数是window套接字版本,可以使用MAKEWORD(x,y)获取版本
//第二参数是WSADATA结构体指针,用于接收加载的套接字信息

//2. 创建套接字,使用函数SOCKET socket(int af,int type,int protocol  );
//第一参数是地址簇,对于TCP/IP协议,只能是AF_INET或PF_INET
//第二参数是指定Socket类型,SOCK_STREAM指定产生流式套接字,SOCK_DGRAM产生数据报套接字
//第三参数是与特定的地址家族相关的协议,如果是0,系统自动选择合适协议

//3. 创建套接字后要绑定到一个IP地址和一个端口上
//函数:int bind(SOCKET s,const struct sockaddr FAR *name,int namelen);
//第一参数是要绑定的套接字,
//第二参数是套接字的本地地址信息,指向sockaddr结构的指针,可以使用sockaddr_in结构的指针
//指针代替,是按照网络字节顺序表示
//第三参数是指该地址结构长度

/*sockaddr_in结构体
struct sockaddr_in{ 
short sin_family;   //地址簇,对于IP地址,一直是AF_INET
unsigned short sin_port; //给套接字的端口号
struct?? in_addr sin_addr; //in_addr结构体,套接字的IP地址
char sin_zero[8]; //使得sockaddr和sockaddr_in结构长度一样
};
***********************************************************/
/*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;
};

  值得说明的两点:
  1.将IP地址指定为INADDR_ANY,表示套接字可以向分配给本地多个的IP地址发送数据和接收数据,
  如果只想使用一个IP地址,可以使用inet_addr()函数将一个点分十进制的IP地址转为u_long型,
  而使用inet_ntoa()函数会完成相反的转换
  2.sockaddr(sockaddr_in)是按照网络字节顺序表示,所以要进行相应的字节顺序转换
  htonl()函数是将一个u_long型转换为TCP/IP网络字节顺序
  htons()函数是将一个u_short型转换为TCP/IP网络字节顺序
*/

//4.监听int listen(SOCKET s, int backlog );
//第一参数是套接字
//第二参数是等待连接队列连接请求的个数,使用SOMAXCONN表示系统选择合适的个数

//5.接受连接请求
//SOCKET accept(SOCKET s,struct sockaddr FAR *addr,	int FAR *addrle);
//第一参数是套接字
//第二参数是sockaddr结构体,用于保存连接的客户的地址信息
//第三参数是用于保存结构体大小,要初始化
//连接成功后返回一个套接字,可以使用这个套接字与连接的这个客户端通信

//6.发送消息
//int send(SOCKET s,const char FAR *buf,int len,int flags);
//第一参数是已经于客户端连接了的套接字
//第二参数是发送的数据
//第三参数是数据长度
//第四参数是调用执行方式,一般为0

//7.接受数据
//int recv(SOCKET s,char FAR *buf, int len,int flags);
//用法和send()函数差不多



#include <WinSock2.h>
#include <iostream>
using namespace std;

#pragma comment(lib,"Ws2_32.lib") //连接库文件

int main()
{
	WSADATA  WSAD;//WSADATA结构体
	SOCKET   SvrSocket;//套接字
    


	//加载套接字库
      if(WSAStartup(MAKEWORD(2,2),&WSAD))
	{
		cout << "加载套接字库失败" << endl;
		return 0;
	}
	
    //创建套接字,如果失败的话返回INVALID_SOCKET值
      SvrSocket = socket(AF_INET,SOCK_STREAM,0);
	
	if (INVALID_SOCKET == SvrSocket )
	{  
		cout << "创建套接字失败" << endl;
		WSACleanup();
		return 0;
	}

	//绑定套接字
	SOCKADDR_IN  sockAddr;
	sockAddr.sin_port = htons(6000);
	sockAddr.sin_addr.S_un.S_addr =ADDR_ANY;
	sockAddr.sin_family = AF_INET;
	
	bind(SvrSocket,(SOCKADDR*)&sockAddr,sizeof(SOCKADDR));
	
        //监听
        listen(SvrSocket,5);//只可以连接最前连接的5个连接请求
   
	cout << "服务器开始监听...." << endl;
	
	//等待客户并连接
	SOCKADDR_IN  ClientAddr;
	int  ClientAddrLeng = sizeof(SOCKADDR);//赋初始值
	while (TRUE)
	{
          SOCKET Socket1 = accept(SvrSocket,(SOCKADDR*)&ClientAddr,&ClientAddrLeng);
	  
          if (Socket1 != INVALID_SOCKET)
          {
		  cout << inet_ntoa(ClientAddr.sin_addr) << "连接成功" << endl;
          }

	  //发送数据
	  char  buff[100];
	  sprintf(buff,"欢迎 %s 的到来",inet_ntoa(ClientAddr.sin_addr));
	  send(Socket1,buff,sizeof(buff),0);
	  //send(Socket1,buff,strlen(buff)+1,0); 


	  //接受数据
	  char ReBuff[100];
	  recv(Socket1,ReBuff,sizeof(ReBuff),0);
	  cout <<inet_ntoa(ClientAddr.sin_addr)
		   << "说:" <<ReBuff << endl;
          closesocket(Socket1);
	  
	}
   
	closesocket(SvrSocket);
	WSACleanup();
	return 0;
}


TCP客户端代码....

 

//基于TCP网络编程,客户端
//VC6.0环境

//1、初始化winsocket 库,任何网络应用程序必须做的一步
//WSAStartup();
//2、创建套接字
//socket();
//3、连接服务端
//connect();
//int connect(SOCKET s,const struct sockaddr FAR *name,int namelen );
//第一参数是客户端的套接字
//第二参数是服务端得地址信息
//第三参数地址结构体长度

#include <WinSock2.h>
#include <iostream>
using namespace std;

#pragma comment(lib,"Ws2_32.lib") //连接库文件

int main()
{
	WSADATA  WSAD;//WSADATA结构体
	SOCKET   ClientSock;//套接字
    
	
	
	//加载套接字库
       if(WSAStartup(MAKEWORD(2,2),&WSAD))
	{
		cout << "加载套接字库失败" << endl;
		return 0;
	}
  
	//创建套接字
        ClientSock = socket(AF_INET,SOCK_STREAM,0);

	if (INVALID_SOCKET == ClientSock)
	{
        cout << "创建套接字失败" << endl;
		WSACleanup();
		return 0;
	}

	//连接服务器
	int err;
	SOCKADDR_IN  ServerAddr; //服务端地址结构体
        ServerAddr.sin_family = AF_INET;
	ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	//服务端得IP地址,因为服务端在本机上
	ServerAddr.sin_port = htons(6000); //要和服务端绑定的端口号一样

	err= connect(ClientSock,(SOCKADDR*)&ServerAddr,sizeof(SOCKADDR));
       //连接成功的话则返回0值
	if (err)
	{
		cout << "连接服务器失败" << endl;
		return 0;
	}
	else
	{
		cout << "连接服务器成功" << endl;
	}

       //接收消息和发送消息
	char ReBuff[100];
	recv(ClientSock,ReBuff,sizeof(ReBuff),0);
	cout << "服务器消息: "<<ReBuff << endl;

	send(ClientSock,"我是客户端...",sizeof("我是客户端...")+1,0);

	closesocket(ClientSock);
	WSACleanup();

    return 0;
}


 

Windows编程(9)|SDK网络编程_第9张图片

 

 

UDP服务端代码....

//基于UDP的网络编程,服务端
//VC6.0
/*UDP中的接收数据函数
int recvfrom(SOCKET s,char FAR* buf,int len,int flags,struct sockaddr FAR *from,int FAR *fromlen);
第一参数是套接字
第二参数是接收数据的空间
第三参数是接收数据的空间大小
第四参数是调用方式,一般为0
第五参数是地址结构体,用于保存发送数据方的地址信息
第六参数数地址结构体大小,必须初始化
*/
#include <WINSOCK2.H>
#include <iostream>
using namespace std;

#pragma comment(lib,"Ws2_32.lib") //连接库文件

int main()
{
    
	WSADATA wsaData;
	//加载成功则会返回0
	if (WSAStartup(MAKEWORD(2,2),&wsaData))
	{
          cout << "加载失败" << endl;
	  return 0;
	}
  
	//创建数据报套接字,UDP的使用数据报套接字
	SOCKET SerSocket = socket(AF_INET,SOCK_DGRAM,0);

    if (SerSocket == INVALID_SOCKET)
    {
		cout << "创建套接字失败" << endl;
		WSACleanup();
		return 0;
    }
	
    //绑定
	SOCKADDR_IN  ServAddr;
	ServAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	ServAddr.sin_family = AF_INET;
	ServAddr.sin_port = htons(6100);

	bind(SerSocket,(SOCKADDR*)&ServAddr,sizeof(SOCKADDR));
   
	//接收数据,注意使用的是 recvfrom()函数
        SOCKADDR_IN  ClientAddr;
	int   AddrLen = sizeof(SOCKADDR);
	char  recvBuff[100];
        memset(recvBuff,0,100);
  
        recvfrom(SerSocket,recvBuff,sizeof(recvBuff),0,(SOCKADDR*)&ClientAddr,&AddrLen);
        cout << recvBuff << endl;

	//服务器端发送数据
        sendto(SerSocket,"hello,这是服务器端发的消息",sizeof("hello,这是服务器端发的消息")+1,0,
		  (SOCKADDR*)&ClientAddr,sizeof(SOCKADDR));

	closesocket(SerSocket);
	WSACleanup();

	return 0;
}

 

UDP客户端代码

//UDP网络编程,客户端

//int sendto(SOCKET s, const char FAR *buf, int len,int flags,const struct sockaddr FAR *to, int tolen  );
//这个函数的用法和recvfrom()函数用法一样

#include <WINSOCK2.H>
#include <iostream>
using namespace std;

#pragma comment(lib,"ws2_32.lib") //连接库文件

int main()
{
    
	WSADATA wsaData;
	//加载成功则会返回0
	if (WSAStartup(MAKEWORD(2,2),&wsaData))
	{
		cout << "加载失败" << endl;
		return 0;
	}
	
	//创建数据报套接字,UDP的使用数据报套接字
	SOCKET ClientSocket = socket(AF_INET,SOCK_DGRAM,0);
	
    if (ClientSocket == INVALID_SOCKET)
    {
		cout << "创建套接字失败" << endl;
		WSACleanup();
		return 0;
    }

	//客户端发送数据
	SOCKADDR_IN ServAddr;//服务器地址信息
	int  Addrlen = sizeof(SOCKADDR);
	ServAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	ServAddr.sin_port = htons(6100);
	ServAddr.sin_family = AF_INET;

	sendto(ClientSocket,"hello,这是客户端发的消息",sizeof("hello,这是客户端发的消息")+1,
		     0,(SOCKADDR*)&ServAddr,sizeof(SOCKADDR));
 
       //客户端接收数据
	char reBuff[100];
	int  len = sizeof(SOCKADDR);
	recvfrom(ClientSocket,reBuff,sizeof(reBuff),0,(SOCKADDR*)&ServAddr,&len);
	cout << reBuff << endl;

	closesocket(ClientSocket);
	WSACleanup();

	return 0;


}

 


 

Windows编程(9)|SDK网络编程_第10张图片

 

 

 这部分的学习参考了孙鑫VC++编程的视频,部分内容来自视频的PPT内容.....

 

 

 

你可能感兴趣的:(编程,windows,网络,socket,struct,服务器)