操作系统与网络 2019-3-15

1.网络

1.1 在昨天的解决方案中新建一个 Server 用做接收客户端的消息

1.加载库->创建套接字->绑定本机->传输数据->关闭套接字->卸载库;
2.创建套接字,我们仍然使用 AF_INET ,基于数据报文的 UDP 协议;
3.绑定,我们仍然需要向 Client 里的绑定一样,绑定自身的 IP 以及 端口 ;
4.传输数据,我们使用 recvfrom 来接收数据,该函数与 sendto 基本类似;
5.关闭套接字,卸载库;

#include <iostream>
using namespace std;

#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

int main()
{
	// 1.加载库(这一段代码仍然是help文档当中的)
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(2, 2);
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) 
	{

		printf("WSAStartup failed with error: %d\n", err);
		return 1;
	}
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) 
	{
		printf("Could not find a usable version of Winsock.dll\n");
		WSACleanup();
		return 1;
	}
	else
		printf("The Winsock 2.2 dll was found okay\n");

	// 2.创建套接字
	SOCKET server_socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(server_socket == INVALID_SOCKET)
	{
		cout << "error code: " << WSAGetLastError() << endl;
		WSACleanup();
		return -1;
	}

	// 3.绑定本机信息(绑定本地信息时,若要与他人通信,则需要让他人知道自己的端口号)
	sockaddr_in server_addr;
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(4568);
	server_addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.25");
	if(::bind(server_socket, (const sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR)
	{
		cout << "error code: " << WSAGetLastError() << endl;
		::closesocket(server_socket);
		server_socket = 0;
		WSACleanup();
		return -1;
	}

	// 4.收数据
	sockaddr_in client_addr;
	int recv_addr_size = sizeof(client_addr);
	char szBuffer[1024] = {0};
	int recv_flag = ::recvfrom(server_socket, szBuffer, 1024, 0, (sockaddr*)&client_addr, &recv_addr_size);
	if(recv_flag > 0)
	{
		cout << szBuffer << endl;
	}
	
	// 5.关闭套接字
	::closesocket(server_socket);
	server_socket = 0;

	// 6.卸载库
	WSACleanup();

	system("pause");
	return 0;
}

1.2 当我们完成 Client 与 Server 的编写之后,我们运行两个项目,在 Client 的cmd窗口上输入一串字符,就可以在 Server 的cmd窗口上看到我们在 Client 上发送的字符了。

1.3 将 Server 接收的数据来自哪个 IP 及 端口 进行打印

1.我们在 Server 的 recvfrom 函数中有一个用来接收发送数据的地址,我们可以通过这个地址来获取其IP以及端口;
2.在我们获得的地址 client_addr 中,其有一个成员 sin_addr ,其中装的就是发送数据端的IP, sin_port 中装的就是端口号;
3.我们直接打印 client_addr.sin_addr 以及 client_addr.sin_port 发现并不是我们想要的 IP 地址,端口号也不对;
4.这是因为我们在传送数据的过程中是大端传送,而在计算机中保存数据时是小端形式,那么我们收到了 192.168.1.25 之后,就会以十进制的形式进行显示,那么我们得到的当然是错误的值;我们可以通过 inet_ntoa 进行格式之间的转换;通过 ntohs 来将端口号从大端转换到小端;
5.我们在添加一个新功能:让 Client 可以一直发送数据, Server 可以一直收数据,那么我们需要两个循环来完成这两个功能;
Server

#include <iostream>
using namespace std;

#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

int main()
{
	// 1.加载库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(2, 2);
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) 
	{

		printf("WSAStartup failed with error: %d\n", err);
		return 1;
	}
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) 
	{
		printf("Could not find a usable version of Winsock.dll\n");
		WSACleanup();
		return 1;
	}
	else
		printf("The Winsock 2.2 dll was found okay\n");

	// 2.创建套接字
	SOCKET server_socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(server_socket == INVALID_SOCKET)
	{
		cout << "error code: " << WSAGetLastError() << endl;
		WSACleanup();
		return -1;
	}

	// 3.绑定本机信息
	sockaddr_in server_addr;
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(4568);
	server_addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.25");
	if(::bind(server_socket, (const sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR)
	{
		cout << "error code: " << WSAGetLastError() << endl;
		::closesocket(server_socket);
		server_socket = 0;
		WSACleanup();
		return -1;
	}

	while(1)
	{
		// 4.收数据
		sockaddr_in client_addr;
		int recv_addr_size = sizeof(client_addr);
		char szBuffer[1024] = {0};
		char *quit_flag = "q";
		int recv_flag = ::recvfrom(server_socket, szBuffer, 1024, 0, (sockaddr*)&client_addr, &recv_addr_size);
		if(recv_flag > 0)
		{
			cout << inet_ntoa(client_addr.sin_addr) 
				<< "(" << ntohs(client_addr.sin_port) << "):" << szBuffer << endl;
		}
		if(strcmp(szBuffer, quit_flag) == 0)
			break;
	}
	

	// 5.关闭套接字
	::closesocket(server_socket);
	server_socket = 0;

	// 6.卸载库
	WSACleanup();

	system("pause");
	return 0;
}

Client

#include <iostream>
using namespace std;

#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

int main()
{
	// 1.加载库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(2, 2);
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) 
	{
		printf("WSAStartup failed with error: %d\n", err);
		return 1;
	}
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) 
	{
		printf("Could not find a usable version of Winsock.dll\n");
		WSACleanup();
		return 1;
	}
	else
		printf("The Winsock 2.2 dll was found okay\n");

	// 2.创建套接字
	SOCKET socket_client = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(socket_client == INVALID_SOCKET)
	{
		cout << "error code: " << WSAGetLastError() << endl;
		WSACleanup();
		return -1;
	}

	// 3.绑定套接字与自己的端口
	sockaddr_in addr_client;												// 自己的信息
	addr_client.sin_family = AF_INET;										// 自己的IP地址是IPv4 还是IPv6
	addr_client.sin_port = htons(4567);										// 端口号
	addr_client.sin_addr.S_un.S_addr = inet_addr("192.168.1.25");			// 自己的IP地址
	if(SOCKET_ERROR == ::bind(socket_client, (const sockaddr*)&addr_client, sizeof(sockaddr_in)))
	{
		cout << "error code: " << WSAGetLastError() << endl;
		::closesocket(socket_client);
		WSACleanup();
		return -1;
	}

	while (1)
	{
		// 4.传输数据
		char sz_buffer[1024] = {0};
		char *quit_flag = "q";
		cout << inet_ntoa(addr_client.sin_addr) << "(" << ntohs(addr_client.sin_port) <<"):";
		cin >> sz_buffer;
		sockaddr_in addr_server;
		addr_server.sin_family = AF_INET;
		addr_server.sin_port = htons(4568);
		addr_server.sin_addr.S_un.S_addr = inet_addr("192.168.1.25");
		int n_send_size = ::sendto(socket_client, sz_buffer, 1024, 0, (const sockaddr*)&addr_server, sizeof(sockaddr_in));
		if(n_send_size <= 0)
		{
			cout << "error code: " << WSAGetLastError() << endl;
		}
		if(strcmp(sz_buffer, quit_flag) == 0)
			break;
	}
	

	::closesocket(socket_client);
	socket_client = 0;

	// 5.关闭库
	WSACleanup();

	system("pause");
	return 0;
}

1.3 端口号种类

  • 1.知名端口号:一般是常用软件将其占用,其他程序不允许占用;范围从0到1023;
  • 2.注册端口号:它们松散地绑定于一些服务;端口号从1025到49151;
  • 3.动态端口号:这些端口号一般不固定分配给某个服务,也就是说许多服务都可以使用这些端口;范围从1024到65535;

1.4 网络层册划分

  • 1.OSI七层模型:开放系统互联参考模型
    自上而下依次是:应用层->表示层->会话层->传输层->网络层->数据链路层->物理层
  • 2.TCP/IP五层模型:应用层->传输层->网络层->数据链路层->物理层
  • 3.TCP/IP四层模型:应用层->传输层->网络层->物理层
  • 4.不管是OSI七层模型还是TCP/IP的四层、五层模型,每一层中都要自己的专属协议,完成自己相应的工作以及与上下层级之间进行沟通。我们从上往下依次进行学习;
    • 1)应用层:为操作系统或网络应用程序提供访问网络服务的接口;
    • 2)表示层:表示层对上层数据或信息进行变换以保证一个主机应用层信息可以被另一个主机的应用程序理解。表示层的数据转换包括数据的加密、压缩、格式转换等;
    • 3)会话层:会话层管理主机之间的会话进程,即负责建立、管理、终止进程之间的会话。会话层还利用在数据中插入校验点来实现数据的同步;
      会话层、表示层和应用层重点 :数据传输基本单位为报文;包含的主要协议包括 FTP(文件传送协议)、Telnet(远程登录协议)、DNS(域名解析协议)、SMTP(邮件传送协议),POP3协议(邮局协议),HTTP协议(Hyper Text Transfer Protocol);
    • 4)传输层:第一个端到端,即主机到主机的层次。传输层负责将上层数据分段并提供端到端的、可靠的或不可靠的传输。此外,传输层还要处理端到端的差错控制和流量控制问题;
      传输层重点 :传输层负责将上层数据分段并提供端到端的、可靠的或不可靠的传输以及端到端的差错控制和流量控制问题;包含的协议主要有TCP协议(Transmission Control Protocol,传输控制协议)、UDP协议(User Datagram Protocol,用户数据报协议);重要的设备是网关;
    • 5)网络层:网络层的目的是实现两个端系统之间的数据透明传送,具体功能包括寻址和路由选择、连接的建立、保持和终止等。它提供的服务使传输层不需要了解网络中的数据传输和交换技术。如果想用尽量少的词来记住网络层,那就是“路径选择、路由及逻辑寻址”;
      网络层的重点 :网络层负责对子网间的数据包进行路由选择;网络层可以实现拥塞控制、网际互连等功能;基本数据单位为IP数据报;包含的协议主要包括 IP 协议(Internet Protocol,因特网互联协议);ICMP协议(Internet Control Message Protocol,因特网控制报文协议);ARP协议(Address Resolution Protocol,地址解析协议);RARP协议(Reverse Address Resolution Protocol,逆地址解析协议);重要设备是路由器;
    • 6)数据链路层:数据链路层在物理层提供的服务的基础上向网络层提供服务,其最基本的服务是将源自网络层来的数据可靠地传输到相邻节点的目标机网络层;该层的作用包括:物理地址寻址、数据的成帧、流量控制、数据的检错、重发等;
      数据链路层的重点 :数据链路层为网络层提供可靠的数据传输;基本数据单位为帧;主要的协议是以太网协议;重要的设备有两个 网桥 和 交换机;
    • 7)物理层:激活、维持、关闭通信端点之间的机械特性、电气特性、功能特性以及过程特性;
      物理层的重点 :该层为上层协议提供了一个传输数据的可靠的物理媒体,简单的说,物理层确保原始的数据可在各种物理媒体上传输;两个重要设备中继器(Repeater,也叫放大器)和集线器;

1.5 数据封装过程

1.上层数据经过传输层加上了 TCP头 ;
2.带有 TCP头 的上层数据经过网络层加上了 IP头 ;
3.带有 IP头 和 TCP头 的上层数据经过数据链路层加上了 LLC头 ;
4.带有 LLC头 、 IP头 、 TCP头 的上层数据经过物理层加上了 MAC头 ;

5.我们发现,数据经过每层都会加上一个头,用来进行加强数据的单一性。

猜你喜欢

转载自blog.csdn.net/weixin_42896619/article/details/88656066