目录
国际网络通信协议标准:
1.OSI协议:
2.TCP/IP协议模型:
应用层 :
传输层:
网络层: IPV4协议 IP地址
IP地址的划分: 公有地址 私有地址
MAC地址:
端口号:
UDP编程
1.套接字:
2.流程
3.函数接口
(1) socket
(2)sendto
(3) inet_addr
(4) htons
(5) bind
(6)recvfrom
主机作为发送端
主机作为接收端
网络:
协议:通信双方约定的一套标准
国际网络通信协议标准:
1.OSI协议:
应用层 发送的数据内容
表示层 数据是否加密
会话层 是否建立会话连接
传输层 数据传输的方式
网络层 数据的路由
数据链路层 局域网内部通信
物理层 物理介质的连接
2.TCP/IP协议模型:
应用层 发送的数据内容
传输层 数据传输的方式
网络层 数据由一台主机到达另一台主机
网络接口层 物理介质连接
应用层 :
FTP 文件传输协议
TFTP 简单文件传输协议
HTTP 超文本传输协议
HTTPS 安全超文本传输协议
SMTP 简单邮件传输协议
TELNET 网络终端登录协议
DNS 域名系统
..
传输层:
TCP 传输控制协议
UDP 用户数据报协议
UDP:不安全、不可靠的传输方式
UDP机制简单
UDP占用的资源开销比较小
TCP:安全、可靠的传输方式
TCP机制复杂
TCP占用的资源开销比较大
三次握手建立连接,确认双方能够通信
通信过程中保障数据传输的完整性
四次挥手断开连接,确保数据传输的完整
网络层:
IPV4协议
IP地址
管理员IP地址形式:192.168.0.167
内存IP地址形式: 11000000.10101000.00000000.10100111
IP地址 = 网络位 + 主机位
网络位:IP地址所属的网段(局域网的编号)
主机位:局域网中的第几台主机
网段号:网络位不变,主机位全为0
广播号:网络位不变, 主机位全为1
子网掩码:每个IP地址都会搭配一个子网掩码,用来区分IP地址的网络位及主机位
子网掩码展开成二进制,1对应的部分就是IP地址的网络位,0对应的部分就是IP地址的主机位
192.168.0.167
255.255.255.0
11000000.10101000.00000000.10100111
11111111.11111111.11111111.00000000
网段号192.168.0.0
广播号192.168.0.255
IP地址的划分:
公有地址
私有地址
A类:1.0.0.0 ~ 126.255.255.255
子网掩码:255.0.0.0
管理超大规模型网络
私有地址:10.0.0.0 ~ 10.255.255.255
B类:128.0.0.0 ~ 191.255.255.255
子网掩码:255.255.0.0
管理大中规模型网络
私有地址:172.16.0.0 - 172.31.255.255
C类:192.0.0.0 ~ 223.255.255.255
子网掩码:255.255.255.0
管理中小规模型网络
私有地址:192.168.0.0 ~ 192.168.255.255
D类:224.0.0.0 ~ 239.255.255.255
用于组播:255.255.255.0
E类:240.0.0.0 ~ 255.255.255.255
用于实验和研究:255.255.255.0
MAC地址:
设备自带网卡的地址(该地址是唯一的)
端口号:
找到同一台主机不同的应用程序
UDP编程
1.套接字:
实现Linux系统下的网络通信
套接字:一次通信对象的抽象
2.流程
发送端流程:1.创建套接字
2.发送信息
3.关闭套接字
接收端流程: 1.创建套接字
2.绑定IP和Port
3.接收信息
4.关闭套接字
3.函数接口
(1) socket
socket
int socket(int domain, int type, int protocol);
功能:
创建套接字
参数:
domain: AF_INET 表示IPV4协议
type:套接字类型
SOCK_STREAM:流式套接字
SOCK_DGRAM:数据报套接字
SOCK_RAW:原始套接字
protocol:
TCP和UDP协议:0
返回值:
成功返回用来通信的文件描述符
失败返回-1
(2)sendto
sendto
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
功能:
发送信息
参数:
sockfd:套接字文件描述符
buf:发送数据空间首地址
len:发送数据长度
flags:发送属性 默认为0
dest_addr:目标地址存放空间首地址
addrlen:目的地址的长度struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
返回值:
成功返回发送字节数
失败返回-1如果sendto对应的套接字没有绑定端口,则sendto绑定一个随机端口完成发送功能
(3) inet_addr
inet_addr
in_addr_t inet_addr(const char *cp);
功能:
将字符串的IP地址转换为32位的地址类型
(4) htons
htons
uint16_t htons(uint16_t hostshort);
功能:
将本地字节序(小端)转换成网络大端字节序
(5) bind
bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:
将套接字与IP地址和端口进行绑定
参数:
addr:绑定地址结构体空间首地址
addrlen:绑定地址空间大小
返回值:
成功返回0
失败返回-1
注意:
只能绑定自己的IP地址
(6)recvfrom
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
功能:
接收信息
参数:
sockfd:套接字文件描述符
buf:接收数据空间首地址
len:接收数据长度
flags:接收的属性 默认为0
src_addr:存放发送方地址空间的地址
addrlen: 要接收的发送方地址的长度
返回值:
成功返回实际接收字节数
失败返回-1
练习一:
主机作为发送端
#include "../head.h"
//主机作为发送方
int main()
{
int sockfd=0;
ssize_t nsize=0;
struct sockaddr_in recvaddr;
struct sockaddr_in sendaddr;
//创建用来通信的套接字
sockfd=socket(AF_INET,SOCK_DGRAM,0);//AF_INET:IPV4协议族,SOCK_DGRAM:UDP数据报套接字
if(sockfd==-1)
{
perror("failed to socket");
return -1;
}
//将发送端套接字与IP地址和端口号绑定
sendaddr.sin_family=AF_INET;
sendaddr.sin_port=htons(30000);
sendaddr.sin_addr.s_addr=inet_addr("192.168.0.185");
bind(sockfd,(struct sockaddr *)&sendaddr,sizeof(sendaddr));
//为目的地址赋值
recvaddr.sin_family=AF_INET;//协议族为IPV4
recvaddr.sin_port=htons(8080);//端口号( htons() 将本地字节序(小端)转换为网络字节序(大端))(发送给wltszs4.3.29网络调试助手)
recvaddr.sin_addr.s_addr=inet_addr("192.168.0.135");//IP地址( inet_addr() 将字符串类型转换为二进制地址类型)
//向目的地址发送数据
nsize=sendto(sockfd,"66666666666",12,0,(struct sockaddr *)&recvaddr,sizeof(recvaddr));//注意强制类型转换
if(nsize==-1)
{
perror("failed to sendto");
return 0;
}
printf("发送成功\n");
//关闭套接字
close(sockfd);
return 0;
}
主机作为接收端
#include "../head.h"
int main()
{
int sockfd=0;
int ret=0;
char tmpbuff[200]={0};
ssize_t nsize=0;
struct sockaddr_in recvaddr;
//创建套接字
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd==-1)
{
perror("failed to socket");
return -1;
}
//将套接字和IP地址与端口号绑定
recvaddr.sin_family=AF_INET;
recvaddr.sin_port=htons(20000);
recvaddr.sin_addr.s_addr=inet_addr("192.168.0.185");
ret=bind(sockfd,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
if(ret==-1)
{
perror("failed to bind");
return 0;
}
//接受数据
nsize=recvfrom(sockfd,tmpbuff,sizeof(tmpbuff),0,NULL,NULL);
if(nsize==-1)
{
perror("failed to recvfrom");
return 0;
}
//打印数据
printf("接收到字节数:%ld,内容为:%s\n",nsize,tmpbuff);
//关闭套接字
close(sockfd);
return 0;
}
练习二:利用UDP编程发送文件
发送端send.c
#include "../head.h"
int main()
{
int sockfd;
int ret=0;
FILE *fp=NULL;
struct sockaddr_in recvaddr;
char readbuff[1024]={0};
size_t size=0;
ssize_t nsize=0;
//创建套接字
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd==-1)
{
perror("failed to socket");
return -1;
}
//为目的地址赋值
recvaddr.sin_family=AF_INET;
recvaddr.sin_port=htons(30000);
recvaddr.sin_addr.s_addr=inet_addr("192.168.0.187");
//输入要发送的文件名
printf("请输入要发送的文件名:");
fgets(readbuff,sizeof(readbuff),stdin);
readbuff[strlen(readbuff)-1]='\0';
//打开该文件
fp=fopen(readbuff,"r");
if(fp==NULL)
{
perror("failed to fopen send.txt");
return -1;
}
//发送文件名
nsize=sendto(sockfd,readbuff,strlen(readbuff)+1,0,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
if(nsize==-1)
{
perror("failed to sendto");
return -1;
}
//发送文件内容
while(1)
{
size=fread(readbuff,1,sizeof(readbuff),fp);
if(size<=0)
{
break;
}
nsize=sendto(sockfd,readbuff,size,0,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
if(nsize==-1)
{
perror("failed to sendto");
return -1;
}
}
//发送文件结尾关闭标志
sprintf(readbuff,".quit");
nsize=sendto(sockfd,readbuff,size,0,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
if(nsize==-1)
{
perror("failed to sendto");
return -1;
}
//关闭套接字和文件
close(sockfd);
fclose(fp);
return 0;
}
接收文件端
#include "../head.h"
int main()
{
int sockfd;
int ret=0;
FILE *fp=NULL;
char filename[100]={0};
char tmpbuff[1024]={0};
struct sockaddr_in recvaddr;
char readbuff[1024]={0};
size_t size=0;
ssize_t nsize=0;
//创建套接字
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd==-1)
{
perror("failed to socket");
return -1;
}
//绑定接收IP地址和套接字,端口号
recvaddr.sin_family=AF_INET;
recvaddr.sin_port=htons(30000);
recvaddr.sin_addr.s_addr=inet_addr("192.168.0.187");
bind(sockfd,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
//接收文件名
nsize=recvfrom(sockfd,filename,sizeof(filename),0,NULL,NULL);
if(nsize==-1)
{
perror("failed to recvfrom");
return -1;
}
//创建文件
fp=fopen(filename,"w");
if(fp==NULL)
{
perror("failed to fopen");
return -1;
}
//接收文件内容
while (1)
{
nsize=recvfrom(sockfd,tmpbuff,sizeof(tmpbuff),0,NULL,NULL);
if(nsize==0)
{
break;
}
if(!strcmp(tmpbuff,".quit"))
{
break;
}
fwrite(tmpbuff,nsize,1,fp);
}
//关闭套接字和文件
close(sockfd);
fclose(fp);
return 0;
}
练习三:利用UDP编程实现聊天功能
1.(进程实现)
send.c
#include "../head.h"
int main()
{
pid_t pid;
struct sockaddr_in sendaddr;
struct sockaddr_in recvaddr;
char tmpbuff[100]={0};
ssize_t size_send;
ssize_t size_recv;
int sockfd=0;
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd==-1)
{
perror("failed to socket");
return -1;
}
recvaddr.sin_family=AF_INET;
recvaddr.sin_port=htons(30000);
recvaddr.sin_addr.s_addr=inet_addr("192.168.0.187");
//第一次发送是为了建立连接,数据可随机发送,不打印
size_send = sendto(sockfd,"hello",6,0,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
if(size_send==-1)
{
perror("failed to sendto");
return -1;
}
pid=fork();
if(pid==-1)
{
perror("failed to fork");
return -1;
}
if(pid==0)
{
while (1)
{
memset(tmpbuff,0,sizeof(tmpbuff));
fgets(tmpbuff,sizeof(tmpbuff),stdin);
tmpbuff[strlen(tmpbuff)-1]='\0';
size_send=sendto(sockfd,tmpbuff,strlen(tmpbuff),0,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
if(size_send==-1)
{
perror("failed to sendto");
return -1;
}
if(!strcmp(tmpbuff,".quit"))
{
break;
}
}
kill(getppid(),SIGKILL);
}
else if(pid>0)
{
while(1)
{
memset(tmpbuff,0,sizeof(tmpbuff));
size_recv = recvfrom(sockfd,tmpbuff,sizeof(tmpbuff),0,NULL,NULL);
if(size_recv==-1)
{
perror("failed to recvfrom");
return -1;
}
if(!strcmp(tmpbuff,".quit"))
{
break;
}
printf("RECV:%s\n",tmpbuff);
}
kill(pid,SIGKILL);
}
close(sockfd);
return 0;
}
recv.c
#include "../head.h"
int main()
{
pid_t pid;
struct sockaddr_in sendaddr;
struct sockaddr_in recvaddr;
size_t size_send;
size_t size_recv;
char tmpbuff[100]={0};
socklen_t addrlen=sizeof(sendaddr);
int ret=0;
int sockfd=0;
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd==-1)
{
perror("failed to socket");
return -1;
}
recvaddr.sin_family=AF_INET;
recvaddr.sin_port=htons(30000);
recvaddr.sin_addr.s_addr=inet_addr("192.168.0.187");
ret=bind(sockfd,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
if(ret==-1)
{
perror("failed to bind");
return -1;
}
//第一次接收是为了建立连接,数据不打印
size_recv = recvfrom(sockfd,tmpbuff,sizeof(tmpbuff),0,(struct sockaddr *)&sendaddr,&addrlen);
if(size_recv==-1)
{
perror("failed to recvfrom");
return -1;
}
pid=fork();
if(pid==-1)
{
perror("failed to fork");
return -1;
}
if(pid==0)
{
while(1)
{
memset(tmpbuff,0,sizeof(tmpbuff));
size_recv = recvfrom(sockfd,tmpbuff,sizeof(tmpbuff),0,NULL,NULL);
if(size_recv==-1)
{
perror("failed to sendto");
return -1;
}
if(!strcmp(tmpbuff,".quit"))
{
break;
}
printf("RECV:%s\n",tmpbuff);
}
kill(getppid(),SIGKILL);
}
else if(pid>0)
{
while (1)
{
memset(tmpbuff,0,sizeof(tmpbuff));
fgets(tmpbuff,sizeof(tmpbuff),stdin);
tmpbuff[strlen(tmpbuff)-1]='\0';
size_send=sendto(sockfd,tmpbuff,strlen(tmpbuff),0,(struct sockaddr *)&sendaddr,sizeof(sendaddr));
if(size_send==-1)
{
perror("failed to sendto");
return -1;
}
if(!strcmp(tmpbuff,".quit"))
{
break;
}
}
kill(pid,SIGKILL);
}
close(sockfd);
return 0;
}
2.(线程实现)
send.c
#include "../head.h"
int sockfd = 0;
pthread_t tid1;
pthread_t tid2;
struct sockaddr_in recvaddr;
void *thread1(void *arg)
{
char tmpbuff[1024] = {0};
ssize_t nsize = 0;
while (1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
fgets(tmpbuff, sizeof(tmpbuff), stdin);
tmpbuff[strlen(tmpbuff)-1] = '\0';
nsize = sendto(sockfd, tmpbuff, strlen(tmpbuff), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
if (-1 == nsize)
{
perror("fail to sendto");
return NULL;
}
if (!strcmp(tmpbuff, ".quit"))
{
break;
}
}
pthread_cancel(tid2);
return NULL;
}
void *thread2(void *arg)
{
char tmpbuff[1024] = {0};
ssize_t nsize = 0;
while (1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
nsize = recvfrom(sockfd, tmpbuff, sizeof(tmpbuff), 0, NULL, NULL);
if (-1 == nsize)
{
perror("fail to recvfrom");
return NULL;
}
if (!strcmp(tmpbuff, ".quit"))
{
break;
}
printf("RECV:%s\n", tmpbuff);
}
pthread_cancel(tid1);
return NULL;
}
int main(void)
{
//1.创建套接字
char tmpbuff[1024] = {"hello"};
ssize_t nsize = 0;
recvaddr.sin_family = AF_INET;
recvaddr.sin_port = htons(RECV_PORT);
recvaddr.sin_addr.s_addr = inet_addr(RECV_IP);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd)
{
perror("fail to socket");
return -1;
}
//2.发送一次
nsize = sendto(sockfd, tmpbuff, strlen(tmpbuff), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
if (-1 == nsize)
{
perror("fail to sendto");
return -1;
}
//3.创建两个线程
pthread_create(&tid1, NULL, thread1, NULL);
pthread_create(&tid2, NULL, thread2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
close(sockfd);
return 0;
}
recv.c
#include "../head.h"
int sockfd = 0;
pthread_t tid1;
pthread_t tid2;
struct sockaddr_in recvaddr;
struct sockaddr_in sendaddr;
void *thread1(void *arg)
{
char tmpbuff[1024] = {0};
ssize_t nsize = 0;
while (1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
fgets(tmpbuff, sizeof(tmpbuff), stdin);
tmpbuff[strlen(tmpbuff)-1] = '\0';
nsize = sendto(sockfd, tmpbuff, strlen(tmpbuff), 0, (struct sockaddr *)&sendaddr, sizeof(sendaddr));
if (-1 == nsize)
{
perror("fail to sendto");
return NULL;
}
if (!strcmp(tmpbuff, ".quit"))
{
break;
}
}
pthread_cancel(tid2);
return NULL;
}
void *thread2(void *arg)
{
char tmpbuff[1024] = {0};
ssize_t nsize = 0;
while (1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
nsize = recvfrom(sockfd, tmpbuff, sizeof(tmpbuff), 0, NULL, NULL);
if (-1 == nsize)
{
perror("fail to recvfrom");
return NULL;
}
if (!strcmp(tmpbuff, ".quit"))
{
break;
}
printf("RECV:%s\n", tmpbuff);
}
pthread_cancel(tid1);
return NULL;
}
int main(void)
{
//1.创建套接字
char tmpbuff[1024] = {"hello"};
ssize_t nsize = 0;
int ret = 0;
socklen_t addrlen = sizeof(sendaddr);
recvaddr.sin_family = AF_INET;
recvaddr.sin_port = htons(RECV_PORT);
recvaddr.sin_addr.s_addr = inet_addr(RECV_IP);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd)
{
perror("fail to socket");
return -1;
}
ret = bind(sockfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
if (-1 == ret)
{
perror("fail to bind");
return -1;
}
//2.接收一次
nsize = recvfrom(sockfd, tmpbuff, sizeof(tmpbuff), 0, (struct sockaddr *)&sendaddr, &addrlen);
if (-1 == nsize)
{
perror("fail to recvfrom");
return -1;
}
//3.创建两个线程
pthread_create(&tid1, NULL, thread1, NULL);
pthread_create(&tid2, NULL, thread2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
close(sockfd);
return 0;
}