曼切斯特编码
- 为了解决通信双方时序同步问题而诞生
- 原理是通过在时钟周期边缘的电位变化为依据代表0,1
网络的七层模型
- 应用层:HTTP协议等
- 表示层:规范数据格式
- 会话层:建立、管理、终止会话
- 传输层:TCP,UDP等
- 网络层:IPV4,V6等
- 数据链路层:定义单个链路上如何传输
- 物理层:规范传输介质的特性,比如帧和电流脉冲等
套接字
- 端口号拼接到IP地址上就是套接字,即形如(主机IP地址:端口号)
- 为了支持开发面向应用的通信系统,各个系统基于TCP和UDP的应用程序开发接口,该接口通常以一组函数出现,也称为套接字Socket
- 套接字的一般用法,图中少最后的closesocket
Socket实验例程
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;
}
实验过程
不懂的名词请看涉及知识的解释
- 编写CS模型
- 通过scp命令将server端程序上传至云服务器
- 修改云服务器中server程序中IP地址为0.0.0.0,修改本地的client端的IP地址为云服务器的公网地址
- 在云服务器的安全组——>配置规则——>快速创建规则中打开端口
- 分别执行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进程。