曼切斯特編碼
- 爲了解決通信雙方時序同步問題而誕生
- 原理是通過在時鐘週期邊緣的電位變化爲依據代表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進程。