网络编程
TCP/IP四层模型:(背)
```
1、网络接口/链路层 以太网桢协议 ARP
2、网络层 IP ICMP IGMP
3、传输层 TCP UDP
4、应用层 http ftp nfs ssh telnet
```
网络通信过程(掌握)
结论:
数据在没有封装之前是不能再网络中传输的。
应用层开始封装(比如想发送文件,先封装一层ftp协议),
然后 ---> {传输层(tcp) ---->网络层 (ip) ----->链路层 }(属于内核层
不用自己封装的)
接收数据就是相反的过程,一层一层的解协议。
各层的协议介绍
网络接口/链路层 以太网桢协议 ARP(了解)
路由器/交换机 形成一个网络,那么两个路由之间的传输就是依靠以太网帧协议的。
以太网桢协议:根据Mac地址,完成数据包传输。
ARP请求,根据IP地址获取MAC地址
网络层 ip协议
IP地址可以在网络环境中标识唯一的一台主机。
传输层 Udp /Tcp
端口可以确定是哪一台主机上的哪一个进程
IP地址+端口号:在网络环境中,唯一的标识一个进程。
UDP
TCP
Socket编程
相关函数:
inet_addr()函数:
in_addr_t inet_addr(const char *cp);
作用:将点分制的ip地址转换成 整形ip地址(网络字节序)
点分制ip地址("192.168.1.252"), 占用内存空间是多少? 14byte
为了减少存储空间(用一个4字节整数存ip地址 C0 A8 1 FC)
练习:
int main()
{
int x = inet_addr("192.168.1.252");
//将点分制ip地址转换成网络字节序的ip地址
printf("x is %x\n", x);
//FC01A8C0(正好和ip地址相反) 字节序
}
计算机采用小端对齐:高位存在高地址,低位存在低地址
如:
int a = 0x12345678 ,12存在高地址了 ,因此是小端对齐
网络字节序:大端对齐
htonl 本地字节序转成网络字节序(IP)
htons 本地字节序转成网络字节序(Port)
ntohl
ntohs
inet_ntoa (作用: 将网络字节序的ip地址转换成点分制ip地址)
UDP通信
练习
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;
}
练习:两个进程,每个都能接收,发送(双人upd聊天程序,循环收发)
(注意:在一台电脑上,两个进程分别要用两个端口号, 1 发送端口号 对应 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); //创建线程时,系统会分配一个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标志位 做应答
注意:其实是不可能发一条才收一条,都是批量发送批量处理的,效率高些