網絡基礎(將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進程。

 

 

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