网络基础(将TCP服务端程序部署在云服务器)

曼切斯特编码

  • 为了解决通信双方时序同步问题而诞生
  • 原理是通过在时钟周期边缘的电位变化为依据代表0,1

网络的七层模型

  • 应用层:HTTP协议等
  • 表示层:规范数据格式
  • 会话层:建立、管理、终止会话
  • 传输层:TCP,UDP等
  • 网络层:IPV4,V6等
  • 数据链路层:定义单个链路上如何传输
  • 物理层:规范传输介质的特性,比如帧和电流脉冲等

套接字

  • 端口号拼接到IP地址上就是套接字,即形如(主机IP地址:端口号)
  • 为了支持开发面向应用的通信系统,各个系统基于TCP和UDP的应用程序开发接口,该接口通常以一组函数出现,也称为套接字Socket
  • 套接字的一般用法,图中少最后的closesocket

 

Socket实验例程

github地址

server.cpp

#include <iostream>
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h> //互联网地址族头文件。地址族 简单来说就是底
						//层是使用的哪种通信协议来递交数据的,如 AF_INET
						// 用的是 TCP/IPv4;AF_INET6使用的是 TCP/IPv6;
						//而 AF_LOCAL 或者 AF_UNIX 则指的是本地通信
						//(即本次通信是在当前主机上的进程间的通信),一般
						// 用绝对路径的形式来指明 
#include <arpa/inet.h>	//信息转换,将客户端信息,转换为字符串信息。
#include <unistd.h>		//是 C 和 C++ 程序设计语言中提供对 POSIX 
						//(可移植操作系统接口)操作系统 API 的访问功能的头文件的名称。


using namespace std;


#define NUM_THREADS 2


const int BUF_SIZE = 1024;


void *recv(void *arg)
{
	int fd = *((int*)arg);
	char buffer[BUF_SIZE] = {};
	while(1)
	{
		memset(buffer,0x00,BUF_SIZE);
		int ret = recv(fd,buffer,BUF_SIZE,0);
		if (ret == 0)
		{
			cout << "client has been closed" << endl;
			break;
		}
		// char *ip = inet_ntoa(client.sin_addr);//将网络地址转换成“.”点隔的字符串格
	    cout << "client: " << buffer << endl;
	}
	close(fd);
}

int main()
{
	/*+++++++++++++++++++++++++++++++++++++++++++++++
	函数原型											+
	int socket(int domain, int type, int protocol); +
	domain 用于选择网络通信的域,选择通信协议族,通信协议族	+
	在文件socket.h中定义								+
	type 用于选择套接字类型							+
	protocol 制定某个协议的特定类型,即type类型中的某个类	+
	型。通常某协议中只有一种特定类型,这样protocol参数仅能	+
	设置为0;但是有些协议有多种特定的类型,就需要设置这个参	+
	数来选择特定的类型。								+
	++++++++++++++++++++++++++++++++++++++++++++++++*/
    
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd == -1)
    {
    	cout << "socket 创建失败" << endl;
    	exit(-1);
    }


    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    int res = bind(server_fd,(struct sockaddr*)&addr,sizeof(addr));
    if (res == -1)
    {
    	cout << "bind 失败" << endl;
    	exit(-1);
    }


    listen(server_fd,30);
    cout << "等待客户端的连接" << endl;


    pthread_t tids[NUM_THREADS];
    

    while (1)
    {
		struct sockaddr_in client;
		socklen_t len = sizeof(client);
		int conn_fd = accept(server_fd, (struct sockaddr*)&client, &len);
	    if (conn_fd == -1)
	    {
	    	cout << "accept 错误\n" <<endl;
	    	exit(-1);
	    }

	    
	    int ret = pthread_create(&tids[0],NULL,recv,(void *)&conn_fd);
	    if (ret != 0)
	    {
	    	cout << "接收线程创建失败" << endl;
	    }
	   
	    while(1)
	    {
	    	char buffer[BUF_SIZE] = {};
			cin >> buffer;
			int ret = send(conn_fd,buffer,BUF_SIZE,0);
			if (ret == -1)
			{
				break;
			}
	    }
	}

	close(server_fd);
    pthread_exit(NULL);	
    return 0;
}

client.cpp

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>


using namespace std;


#define NUM_THREAD 2


const int BUF_SIZE = 1024;


void *recv(void *arg)
{
	int fd = *((int*)arg);
	char buffer[BUF_SIZE] = {};
	while(1)
	{
		memset(buffer,0x00,BUF_SIZE);
		int ret = recv(fd,buffer,BUF_SIZE,0);	
		if (ret < 0)
		{
			cout << "找不到服务器";
			break;
		}	
		cout << "server:" << buffer << endl; 
	}
	close(fd);
}

int main()
{
	int client_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (client_fd == -1)
	{
		cout << "socket 创建失败" << endl;
		exit(-1);
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(8080);
	addr.sin_addr.s_addr = inet_addr("123.57.78.143");
	


	int res = connect(client_fd,(struct sockaddr*)&addr,sizeof(addr));
	if (res == -1)
	{
		cout << "连接失败" << endl;
		exit(-1);
	}
	cout << "建立连接" << endl;
	

	pthread_t tids[NUM_THREAD];
	int ret = pthread_create(&tids[0],NULL,recv,(void *)&client_fd);
	if (ret != 0)
    {
    	cout << "接收线程创建失败" << endl;
    }


	while (1)
	{
	    char buffer[BUF_SIZE] = {};
		cin >> buffer;
		send(client_fd,buffer,BUF_SIZE,0);	
		// cout << "已发送" << endl;
	}


	pthread_exit(NULL);
	return 0;
}

实验过程

不懂的名词请看涉及知识的解释

  1. 编写CS模型
  2. 通过scp命令将server端程序上传至云服务器
  3. 修改云服务器中server程序中IP地址为0.0.0.0,修改本地的client端的IP地址为云服务器的公网地址
  4. 在云服务器的安全组——>配置规则——>快速创建规则中打开端口
  5. 分别执行server和client

涉及知识

SCP命令

  • Linux scp 命令用于 Linux 之间复制文件和目录。
  • scp 是 secure copy 的缩写, scp 是 linux 系统下基于 ssh 登陆进行安全的远程文件拷贝命令

SSH协议

  • SSH是secure shell的缩写
  • SSH 是建立在应用层基础上的安全协议
  • SSH 是专为远程登录会话和其他网络服务保障安全的协议,防止传输中信息泄露,使用ras加密算法

RAS加密算法

  • 一种非对称加密算法
  • 建立在对极大整数的因式分解计算基础上

举例说明ssh远程登录命令

ssh username@hostname

使用此命令进行远程登录的时候,host会询问是否接受它提供的公钥(你试验的时候可能默认接受了,所以一般都不提示)。当我们本地接受后,会将登录信息经过公钥加密(比如本地ip和远程服务器的登录密码等)然后传回host,host再用自己的私钥恢复真实信息。因为在非对称加密算法中,公钥是公开的,即使有人知道公钥,但是没有私钥也不能截取信息。

 

为什么是0.0.0.0/0

0.0.0.0/0代表允许所有IP进行访问

什么是公网IP

公网IP就是提供给外界识别的IP地址,相对的还有私有IP,就是局域网内部使用的IP。两者所在的范围不一样,私有IP就好比是自己小区内部里送东西时的门牌号,公网IP就是寄快递时要填的省市区。而不同的小区之前用来送东西的门牌号可能是一样的,比如都是1栋1单元101,而省市区是唯一的。那么有了公网IP你就可以在网上找到我的主机,然后在通过程序里的端口号和服务端程序进行通信。其实本质上就是层级关系和标识问题。

端口号代表什么

通过公网IP找到了服务器,但是作为服务器,他可能同时运行了多个server服务程序,也就是很多client都在连接服务器,为了使他们连接之后通信不发生混乱,就需要使用端口号来一一对应。注意与进程号PID的区别,PID才是进程的唯一标识,用来其他程序对该进程进行操作,比如kill进程。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章