一:客戶端與服務器
//客戶端:client,一般字母c表示
//服務器:server,一般字母s表示 ,所以c/s一般就是 :客戶端/服務器
//客戶端:就是一個程序,
//服務器:也是 一個程序;
//(1.1)解析一個瀏覽器訪問網頁的過程
//(1.2)客戶端服務器角色規律總結
//a)數據通訊總在兩端進行,其中一端叫客戶端,另一端叫服務器端;
//b)總有一方先泛起第一個數據包,這發起第一個數據包的這一端,就叫客戶端【瀏覽器】;被動收到第一個數據包這端,叫服務器端【淘寶服務器】;
//c)連接建立起來,數據雙向流動,這叫 雙工【你可以發數據包給我,我也可以發數據包給你】
//d)既然服務器端是被動接收連接,那麼客戶端必須得能夠找到服務器在哪裏;
//我瀏覽器要訪問淘寶網,我需要知道淘寶服務器的地址【ip地址:192.168.1.100 三個點分隔四個數】,
//以及淘寶服務器的姓名【端口號,這是 一個無符號數字,範圍 0-65535之間的一個數字】
// 淘寶網服務器【nginx服務器】會調用listen()函數來監聽80端口;
//在編寫網絡通訊程序時,你只需要指定淘寶服務器的ip地址和淘寶服務器的端口號,就能夠跟淘寶服務器進行通訊;
//e)epoll
二:網絡模型
(2.1)OSI七層網絡模型:
//物【物理層】 鏈【數據鏈路層】 網【網絡層】 傳【傳輸層】 會【會話層】 表【表示層】 應【應用層】
//OSI(Open System Interconnect):開放式系統互聯;是ISO(國際標準化組織)在1985年研究的網絡互聯模型;
//把一個要發送出去的數據包從裏到外裹了7層,就跟 一個人一樣,穿了7件衣服,一件套一件 ;最終把包裹了7層的數據包發送都網絡上去了;
(2.2)TCP / IP協議四層模型
//Transfer Control Protocol[傳輸控制協議]/Internet Protocol[網際協議];
//tcp/ip實際是 一組 協議的代名詞,而不僅僅是一個協議;
//tcp/ip協議,其實每一層都對應着一些協議;
(2.3)TCP / IP協議的解釋和比喻
//我們把人看成 要發送出去的數據包;人出門上街 ,我們把外邊的街道,就看成網絡,我們人出門上街,就等於把數據包發送到互聯網是上去;
// 人 <=======> 數據包
//街道 <=======> 互聯網
//人上街 <=======> 數據包發送到互聯網上
//人不能光腚上街,人要先穿內衣內褲【TCP】;套一個襯衣襯褲【IP】,套個外衣外褲【以太網幀】,可以出門了;
// TCP 比喻成了 內衣內褲
// IP 比喻成了 襯衣襯褲
// 以太網幀 比喻成了 外衣外褲
//你要發送 abc 這三個字母出去到網絡上;
//加個tcp頭【abc套了個內衣內褲】
//加個IP頭【abc套了個襯衣襯褲】
//加個以太網幀頭/尾【abc套了個外衣外褲】
//加了這三個頭一個尾之後,就認爲這個數據包符合了TCP/IP協議,這個數據包能夠被髮送到網絡上去了【人穿好了衣服可以出門了】;
三:最簡單的客戶端和服務器程序實現代碼
//a)客戶端程序,服務器端程序;只具備演示價值,不具備商業價值。
//b)最終epoll技術實現商用的服務器程序;
//c)《Unix網絡編程》第一卷;
nginx_server.c
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#define SERV_PORT 9000 //本服務器要監聽的端口號,一般1024以下的端口很多都是屬於周知端口,所以我們一般採用1024之後的數字做端口號
int main(int argc, char *const *argv)
{
//這些演示代碼的寫法都是固定套路,一般都這麼寫
//服務器的socket套接字【文件描述符】
int listenfd = socket(AF_INET, SOCK_STREAM, 0); //創建服務器的socket,大家可以暫時不用管這裏的參數是什麼,知道這個函數大概做什麼就行
struct sockaddr_in serv_addr; //服務器的地址結構體
memset(&serv_addr,0,sizeof(serv_addr));
//設置本服務器要監聽的地址和端口,這樣客戶端才能連接到該地址和端口併發送數據
serv_addr.sin_family = AF_INET; //選擇協議族爲IPV4
serv_addr.sin_port = htons(SERV_PORT); //綁定我們自定義的端口號,客戶端程序和我們服務器程序通訊時,就要往這個端口連接和傳送數據
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //監聽本地所有的IP地址;INADDR_ANY表示的是一個服務器上所有的網卡(服務器可能不止一個網卡)多個本地ip地址都進行綁定端口號,進行偵聽。
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));//綁定服務器地址結構體
listen(listenfd, 32); //參數2表示服務器可以積壓的未處理完的連入請求總個數,客戶端來一個未連入的請求,請求數+1,連入請求完成,c/s之間進入正常通訊後,請求數-1
int connfd;
const char *pcontent = "I sent sth to client!"; //指向常量字符串區的指針
for(;;)
{
//卡在這裏,等客戶單連接,客戶端連入後,該函數走下去【注意這裏返回的是一個新的socket——connfd,後續本服務器就用connfd和客戶端之間收發數據,而原有的lisenfd依舊用於繼續監聽其他連接】
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
//發送數據包給客戶端
write(connfd,pcontent,strlen(pcontent)); //注意第一個參數是accept返回的connfd套接字
//只給客戶端發送一個信息,然後直接關閉套接字連接;
close(connfd);
} //end for
close(listenfd); //實際本簡單範例走不到這裏,這句暫時看起來沒啥用
return 0;
}
nginx_client.c
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#define SERV_PORT 9000 //要連接到的服務器端口,服務器必須在這個端口上listen着
int main(int argc, char *const *argv)
{
//這些演示代碼的寫法都是固定套路,一般都這麼寫
int sockfd = socket(AF_INET, SOCK_STREAM, 0); //創建客戶端的socket,大家可以暫時不用管這裏的參數是什麼,知道這個函數大概做什麼就行
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(serv_addr));
//設置要連接到的服務器的信息
serv_addr.sin_family = AF_INET; //選擇協議族爲IPV4
serv_addr.sin_port = htons(SERV_PORT); //連接到的服務器端口,服務器監聽這個地址
//這裏爲了方便演示,要連接的服務器地址固定寫
if(inet_pton(AF_INET,"192.168.1.126",&serv_addr.sin_addr) <= 0) //IP地址轉換函數,把第二個參數對應的ip地址轉換第三個參數裏邊去,固定寫法
{
printf("調用inet_pton()失敗,退出!\n");
exit(1);
}
//連接到服務器
if(connect(sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0)
{
printf("調用connect()失敗,退出!\n");
exit(1);
}
int n;
char recvline[1000 + 1];
while(( n = read(sockfd,recvline,1000)) > 0) //僅供演示,非商用,所以不檢查收到的寬度,實際商業代碼,不可以這麼寫
{
recvline[n] = 0; //實際商業代碼要判斷是否收取完畢等等,所以這個代碼只有學習價值,並無商業價值
printf("收到的內容爲:%s\n",recvline);
}
close(sockfd); //關閉套接字
printf("程序執行完畢,退出!\n");
return 0;
}
//(3.1)套接字socket概念
//套接字(socket):就是個數字,通過調用socket()函數來生成;這個數字具有唯一性;一直給你用,直到你調用close()函數把這個數字關閉;
//文件描述符;一切皆文件,咱們就把socket也看成是文件描述符,我們可以用socket來收發數據;send(),recv();
//(3.2)一個簡單的服務器端通訊程序範例【看調用了哪些函數:面試官可能 會考】
//(3.3)IP地址簡單談
//192.168.1.100[IVP4]:理解成現實社會中的居住地址
//192.168.1.100[IVP4]:第四個版本的IP地址格式;
//發展處了新的IP地址版本【第六版】,IPV6
//我們寫通訊程序代碼時是否需要根據ipv4,ipv6來調整呢?
//a)寫服務器程序,不用考慮ipv4,ipv6的問題,遵照ipv4規則寫就行;
//b)寫客戶端程序,只演示ipv4版本的客戶端範例。
//後續寫項目老師會帶着大家寫同時兼容ipv4,ipv6【協議無關】客戶端程序;
//(3.4)一個簡單的客戶端通訊程序範例
//c/s建立連接時雙方彼此都要有 ip地址 /端口號;
//連接一旦建立起來,那麼雙方的通訊【雙工收發】,就只需要用雙方彼此對應的套接字即可;
//(3.5)客戶端服務器程序綜合演示和調用流程圖
//服務器端程序要先運行;
四:TCP和UDP的區別
//TCP(Transfer Control Protocol):傳輸控制協議
//UDP(User Datagram Protocol):用戶數據報協議
//socket()
//TCP 比喻成 內衣內褲
//UDP 比喻成 內衣內褲
//a)TCP是大品牌內衣內褲,售後質量好;如果被偷取,廠家負責派人幫你找
//b)UDP小品牌內衣內褲,沒有什麼售後服務;
//TCP協議:可靠的面向連接的協議;數據包丟失的話操作系統底層會感知並且幫助你重新發送數據包;
//UDP協議:不可靠的,無連接的協議;
//優缺點:
//a)tcp:可靠協議,必然要耗費更多的系統資源確保數據傳輸的可靠;
//得到好處就是隻要不斷線,傳輸給對方的數據,一定正確的,不丟失,不重複,按順序到達對端;
//b)udp:不可靠協議;發送速度特別快;但無法確保數據可靠性
//各自的用途:
//a)tcp:文件傳輸,收發郵件需要準確率高,但效率可以相對差;一般TCP比UDP用的範圍和場合更廣;
//b)udp:qq聊天信息;DNS。。。。。。估計隨着網絡的發展,網絡性能更好,丟包率更低,那麼udp應用範圍更廣;