網(wǎng)絡(luò)編程
TCP/IP四層模型:(背)
```
1葛峻、網(wǎng)絡(luò)接口/鏈路層 以太網(wǎng)楨協(xié)議 ARP
2缩焦、網(wǎng)絡(luò)層 IP ICMP IGMP
3、傳輸層 TCP UDP
4驯嘱、應(yīng)用層 http ftp nfs ssh telnet
```
網(wǎng)絡(luò)通信過程(掌握)
結(jié)論:
數(shù)據(jù)在沒有封裝之前是不能再網(wǎng)絡(luò)中傳輸?shù)摹?
應(yīng)用層開始封裝(比如想發(fā)送文件,先封裝一層ftp協(xié)議)近范,
然后 ---> {傳輸層(tcp) ---->網(wǎng)絡(luò)層 (ip) ----->鏈路層 }(屬于內(nèi)核層
不用自己封裝的)
接收數(shù)據(jù)就是相反的過程脊髓,一層一層的解協(xié)議。
各層的協(xié)議介紹
網(wǎng)絡(luò)接口/鏈路層 以太網(wǎng)楨協(xié)議 ARP(了解)
路由器/交換機 形成一個網(wǎng)絡(luò)顽决,那么兩個路由之間的傳輸就是依靠以太網(wǎng)幀協(xié)議的。
以太網(wǎng)楨協(xié)議:根據(jù)Mac地址崇摄,完成數(shù)據(jù)包傳輸擎值。
ARP請求,根據(jù)IP地址獲取MAC地址
image.png
網(wǎng)絡(luò)層 ip協(xié)議
image.png
IP地址可以在網(wǎng)絡(luò)環(huán)境中標識唯一的一臺主機逐抑。
傳輸層 Udp /Tcp
端口可以確定是哪一臺主機上的哪一個進程
IP地址+端口號:在網(wǎng)絡(luò)環(huán)境中鸠儿,唯一的標識一個進程。
UDP
image.png
TCP
image.png
Socket編程
相關(guān)函數(shù):
inet_addr()函數(shù):
in_addr_t inet_addr(const char *cp);
作用:將點分制的ip地址轉(zhuǎn)換成 整形ip地址(網(wǎng)絡(luò)字節(jié)序)
點分制ip地址("192.168.1.252"), 占用內(nèi)存空間是多少? 14byte
為了減少存儲空間(用一個4字節(jié)整數(shù)存ip地址 C0 A8 1 FC)
練習:
int main()
{
int x = inet_addr("192.168.1.252");
//將點分制ip地址轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序的ip地址
printf("x is %x\n", x);
//FC01A8C0(正好和ip地址相反) 字節(jié)序
}
計算機采用小端對齊:高位存在高地址厕氨,低位存在低地址
如:
int a = 0x12345678 进每,12存在高地址了 ,因此是小端對齊
網(wǎng)絡(luò)字節(jié)序:大端對齊
htonl 本地字節(jié)序轉(zhuǎn)成網(wǎng)絡(luò)字節(jié)序(IP)
htons 本地字節(jié)序轉(zhuǎn)成網(wǎng)絡(luò)字節(jié)序(Port)
ntohl
ntohs
inet_ntoa (作用: 將網(wǎng)絡(luò)字節(jié)序的ip地址轉(zhuǎn)換成點分制ip地址)
UDP通信
image.png
練習
send.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
int main(int argc,char *argv[]){
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == sockfd){
perror("socket error!");
exit(1);
}
struct sockaddr_in youaddr;
youaddr.sin_family = AF_INET;
youaddr.sin_port = htons(4444);
youaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
char buff[100] = "hello";
while(1){
gets(buff);
int size = sendto(sockfd, buff, strlen(buff)+1, 0,
(struct sockaddr *)&youaddr, sizeof(youaddr));
memset(buff,0,sizeof(buff));
recvfrom(sockfd, buff, sizeof(buff), 0,(struct sockaddr *)&youaddr,
sizeof(youaddr));
write(STDOUT_FILENO,buff,strlen(buff)+1);
}
close(sockfd);
return 0;
}
Recv.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
int main(int argc,char *argv[]){
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == sockfd){
perror("socket error!");
exit(1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(4444);//自己的端口號
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//addr.sin_addr.s_addr = htonl(INADDR_ANY);
int size= bind(sockfd, (struct sockaddr *)&addr,
sizeof(addr));
char buff[100];
int len = 16;
struct sockaddr_in youaddr;
while(1){
recvfrom(sockfd, buff, sizeof(buff), 0,(struct sockaddr *)&youaddr, &len);
write(STDOUT_FILENO,buff,strlen(buff)+1);
memset(buff,0,sizeof(buff));
gets(buff);
sendto(sockfd, buff, strlen(buff)+1, 0,
(struct sockaddr *)&youaddr, sizeof(youaddr));
printf("****%s*****\n",buff);
}
printf("\nIP = %s port = %d\n",inet_ntoa(youaddr.sin_addr.s_addr),
ntohs(youaddr.sin_port));
while(1);
close(sockfd);
return 0;
}
練習:兩個進程命斧,每個都能接收田晚,發(fā)送(雙人upd聊天程序,循環(huán)收發(fā))
(注意:在一臺電腦上国葬,兩個進程分別要用兩個端口號贤徒, 1 發(fā)送端口號 對應(yīng) 2 接收端口號)
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>
#include <stdio.h>
struct sockaddr_in my_addr;
struct sockaddr_in to_addr;
int fd;
void *send_fun(void *p)
{
char buf[100] = { 0 };
while(1)
{
gets(buf);
sendto(fd, buf, 100, 0, (struct sockaddr*)&to_addr, 16);
}
}
int main(int argc, char *argv[])
{
pthread_t id;
fd = socket(AF_INET, SOCK_DGRAM, 0);
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[3])); //argv[3] 自己的端口號 33333
my_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY 能自動獲取ip地址
bind(fd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
to_addr.sin_family = AF_INET;
to_addr.sin_port = htons(atoi(argv[2])); //argv[2]對方的端口號 44444
to_addr.sin_addr.s_addr = inet_addr(argv[1]); //argv[1]對方的ip地址127.0.0.1
pthread_create(&id, NULL, send_fun,NULL); //創(chuàng)建線程時芹壕,系統(tǒng)會分配一個id, 放到變量id中
while(1)
{
char buf[100];
int len = 16;
recvfrom(fd, buf, 100, 0, (struct sockaddr *)&to_addr, &len);
printf("from %d:%s\n", ntohs(to_addr.sin_port), buf);
}
}
三次握手
SYN標志位 建立連接
ACK標志位 做應(yīng)答
image.png
注意:其實是不可能發(fā)一條才收一條,都是批量發(fā)送批量處理的接奈,效率高些
image.png
四次揮手 原因:半關(guān)閉
image.png