可以用来:数据传输、数据共享
应用层 | 实际收发的数据 |
表示层 | 发送的数据是否加密 |
会话层 | 是否建立会话连接 |
传输层 | 数据传输的方式(数据包,流式) |
网络层 | 数据的路由(如何从一个局域网到达另一个局域网) |
数据链路层 | 局域网下如何通信 |
物理层 | 物理介质的连接 |
应用层 | 传输的数据 |
传输层 | 传输的方式 |
网络层 | 数据如何从一个台主机到达另一台主机 |
网络接口层 | 物理介质的连接 |
例如有:HTTP 超文本传输协议
HTTPS
FTP 文件传输协议
TFTP 简单文本传输协议
SMTP 邮件传输协议
MQTT
TELNET
...
UDP:用户数据报协议
特点:1. 实现机制简单
2. 资源开销小
3. 不安全不可靠
TCP:传输控制协议
特点:1. 实现机制复杂
2. 资源开销大
3. 安全可靠
IPv4
IP地址:唯一网络中一台主机的标号
IP地址:网络位 + 主机位
子网掩码:用来标识IP地址的网络位和主机位
子网掩码是1的部分表示IP地址的网络位
子网掩码是0的部分表示IP地址的主机位
网段号:网络位不变,主机位全位0,表示网段号
广播地址:网络位不变,主机位全为1,表示广播地址
IP地址类型:
A类:
1.0.0.0 - 126.255.255.255
子网掩码:255.0.0.0
管理超大规模网络
私有IP地址:10.0.0.0 - 10.255.255.255
B类:
128.0.0.0 - 191.255.255.255
子网掩码:255.255.0.0
管理大中规模型网络
私有IP地址:172.16.0.0 - 172.31.255.255
C类:
192.0.0.0 - 223.255.255.255
子网掩码:255.255.255.0
管理中小规模型网络
私有IP地址:192.168.0.0 - 192.168.255.255
D类:
224.0.0.0 - 239.0.0.0
用于组播
E类:
240.0.0.0 - 255.255.255.255
用于实验
socket套接字(全双工)编程:
发端:socket -> sendto -> close
收端:socket -> bind -> recvfrom -> close
int socket(int domain, int type, int protocol);
功能:创建一个用来通信的文件描述符
参数:
domain:使用的协议族 AF_INET(IPv4协议族)
type:套接字类型
SOCK_STREAM:流式套接字
SOCK_DGRAM:数据报套接字
SOCK_RAW:原始套接字
protocol:协议
默认为0;
返回值:
成功返回文件描述符
失败返回-1
- 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
in_addr_t inet_addr(const char *cp);
功能:将字符串IP地址转换为内存中的IP地址
uint16_t htons(uint16_t hostshort);
功能:将本地字节序转换为网络的大端字节序
1. 编写程序实现从终端接收字符串发送给windows软件调试助手,并接收软件助手的回复,显示在终端屏幕上
- #include "head.h"
-
- int main(void)
- {
- int sockfd = 0;
- ssize_t nsize = 0;
- char tmpbuff[1024] = {0};
- struct sockaddr_in recvaddr;
-
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if(sockfd == -1)
- {
- perror("fail to socket");
- return -1;
- }
-
- gets(tmpbuff);
-
-
- recvaddr.sin_family = AF_INET;
- recvaddr.sin_port = htons(50000);
- recvaddr.sin_addr.s_addr = inet_addr("192.168.1.162");
-
- bind(sockfd, (struct sockaddr *)&recvaddr, sizeof(&recvaddr));
-
- nsize = sendto(sockfd, tmpbuff, strlen(tmpbuff), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
- if(nsize == -1)
- {
- perror("fail to sendto");
- return -1;
- }
-
- printf("成功发送 %ld 字节!\n", nsize);
-
-
- memset(tmpbuff, 0, sizeof(tmpbuff));
- nsize = recvfrom(sockfd, tmpbuff, sizeof(tmpbuff), 0, (struct sockaddr *)&recvaddr, (socklen_t *)sizeof(&recvaddr));
- printf("%s\n",tmpbuff);
-
- close(sockfd);
-
- return 0;
- }
- ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
- struct sockaddr *src_addr, socklen_t *addrlen);
功能:从套接字中接收数据
参数:
sockfd:套接字文件描述符
buf:存放数据空间首地址
flags:属性,默认为0
src_addr:存放IP地址信息的空间首地址
addlen:存放接收到IP地址大小空间的首地址
返回值:
成功返回实际接收字节数
失败返回-1
点击“虚拟机”
点击“设置”
点击“网络适配器”
选择“桥接模式”
点击“确定”
点击“编辑”
点击“虚拟网络编辑器”
点击“更改设置”
sudo /etc/init.d/networking restart
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:在套接字上绑定一个IP地址和端口号
参数:
sockfd:套接字文件描述符
addr:绑定IP地址空间首地址
addrlen:绑定IP地址的长度
返回值:
成功返回0
失败返回-1
1. UDP是无连接,发端退出,收端没有任何影响
2. UDP发送数据上限,最好不要超过1500个字节
3. UDP是不安全不可靠的,连续且快速的传输数据容易产生数据丢失
可以通过wireshark抓包工具来查看收发的数据
操作流程:
1. 打开wireshark:
sudo wireshark
2. 选择抓取数据包的网卡:any
3. 执行通信的代码
4. 停止通信
5. 设定过滤条件
ip.addr == IP地址 :通过IP地址查找
udp :通过传输方式udp查找
tcp :通过传输方式tcp查找
udp.port == 端口号:通过端口号查找
源端口号(2个字节)
目的端口号(2个字节)
长度(2个字节)
检验和(2个字节)
要求在不同主机中编写两个程序,实现全双工聊天功能
1. 进入软件后接收当前用户的昵称
2. 显示的格式为对方用户昵称 (对方IP:对方端口) > 接收到的内容
3. 用户输入“.quit”退出聊天
4. 网络通信时收发结构体
- struct person
- {
- char name[32];
- char text[512];
- };
- #include "head.h"
-
- int sockfd = 0;
- ssize_t nsize = 0;
- struct sockaddr_in tmpaddr;
- struct sockaddr_in sendaddr;
- socklen_t addrlen = sizeof(tmpaddr);
-
- struct person
- {
- char name[32];
- char text[512];
- };
-
- pthread_t tid_recv;
- pthread_t tid_send;
-
- void *RecvInfo(void *arg)
- {
- struct person user;
- while(1)
- {
- memset(&user, 0, sizeof(user));
- nsize = recvfrom(sockfd, &user, sizeof(user), 0, (struct sockaddr *)&tmpaddr, &addrlen);
- if(nsize == -1)
- {
- perror("fail to recvfrom");
- return NULL;
- }
- printf("%s %s : %d > %s\n", user.name, inet_ntoa(tmpaddr.sin_addr), ntohs(tmpaddr.sin_port), user.text);
- if(!strcmp(user.text, ".quit"))
- {
- break;
- }
- }
- pthread_cancel(tid_send);
- return NULL;
- }
-
- void *SendInfo(void *arg)
- {
- struct person user;
- while(1)
- {
- memset(&user, 0, sizeof(user));
- scanf("%s", user.name);
- scanf("%s", user.text);
- nsize = sendto(sockfd, &user, sizeof(user), 0, (struct sockaddr *)&sendaddr, sizeof(sendaddr));
- if(nsize == -1)
- {
- perror("fail to sendto");
- return NULL;
- }
- printf("success send %ld byte\n", nsize);
- if(!strcmp(user.text, ".quit"))
- {
- break;
- }
- }
- pthread_cancel(tid_recv);
-
- return NULL;
- }
-
- int main(void)
- {
- struct sockaddr_in recvaddr;
-
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if(sockfd == -1)
- {
- perror("fail to socket");
- return -1;
- }
-
- recvaddr.sin_family = AF_INET;
- recvaddr.sin_port = htons(30000);
- recvaddr.sin_addr.s_addr = inet_addr("192.168.1.153");
- bind(sockfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
-
- sendaddr.sin_family = AF_INET;
- sendaddr.sin_port = htons(30000);
- sendaddr.sin_addr.s_addr = inet_addr("192.168.1.152");
-
- pthread_create(&tid_recv, NULL, RecvInfo, NULL);
- pthread_create(&tid_send, NULL, SendInfo, NULL);
-
- pthread_join(tid_recv, NULL);
- pthread_join(tid_send, NULL);
-
-
- close(sockfd);
-
- return 0;
-
- }
题目:基于UDP实现直播间聊天的功能:
需求:
软件划分为用户客户端和主播服务端两个软件client.c和server.c
用户客户端负责:
1.接收用户的昵称
2.接收用户输入的信息,能够将信息发送给服务端
3.接收服务端回复的数据信息,并完成显示
主播服务端负责:
1.对所有加入直播间的用户的IP地址和端口实现管理(加入、退出)
2.当有新的客户端加入时,能够向所有客户端提示:"欢迎 XXX 用户进入直播间"
3.当有客户端退出时,能够向所有客户端提示:"XXX 离开直播间"
4.能够实现客户端聊天内容的转发,当某个客户端发送聊天信息时,能够将该信息转给除了该用户之外聊天室内所有其余客户端用户
client.c
- #include "head.h"
-
- int sockfd = 0;
- char name[32];
- struct sockaddr_in recvaddr;
- pthread_t tid_send;
- pthread_t tid_recv;
-
- void *SendMsg(void *arg)
- {
- struct msgbuf sendmsg;
- ssize_t nsize = 0;
- while(1)
- {
- memset(&sendmsg, 0, sizeof(sendmsg));
- sendmsg.type = USER_TYPE_CHAT;
- sprintf(sendmsg.name, "%s", name);
- gets(sendmsg.text);
- if(strcmp(sendmsg.text,".quit") == 0)
- {
- sendmsg.type = USER_TYPE_OUT;
- }
- nsize = sendto(sockfd, &sendmsg, sizeof(sendmsg), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
- if(nsize == -1)
- {
- perror("fail to sendto");
- return NULL;
- }
- if(sendmsg.type == USER_TYPE_OUT)
- {
- break;
- }
- }
- pthread_cancel(tid_recv);
-
- return NULL;
- }
-
- void *RecvMsg(void *arg)
- {
- struct msgbuf recvmsg;
- ssize_t nsize = 0;
-
- while(1)
- {
- nsize = recvfrom(sockfd, &recvmsg, sizeof(recvmsg), 0, NULL, NULL);
- if(nsize == -1)
- {
- perror("fail to recvfrom");
- return NULL;
- }
- if(recvmsg.type == USER_TYPE_CHAT)
- {
- printf("%s>%s\n", recvmsg.name, recvmsg.text);
- }
- if(recvmsg.type == USER_TYPE_OUT)
- {
- break;
- }
- }
-
- return NULL;
- }
-
- int main(void)
- {
- ssize_t nsize = 0;
- struct msgbuf sendmsg;
-
-
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if(sockfd == -1)
- {
- perror("fail to socket");
- return -1;
- }
-
- printf("请输入你的名字:\n");
- gets(name);
-
- memset(&sendmsg, 0, sizeof(sendmsg));
- sendmsg.type = USER_TYPE_INT;
- sprintf(sendmsg.name, "%s", name);
-
- recvaddr.sin_family = AF_INET;
- recvaddr.sin_port = htons(SERVER_PORT);
- recvaddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
- nsize = sendto(sockfd, &sendmsg, sizeof(sendmsg), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
- if(nsize == -1)
- {
- perror("fail to sendto");
- return -1;
- }
-
- pthread_create(&tid_send, NULL, SendMsg, NULL);
- pthread_create(&tid_recv, NULL, RecvMsg, NULL);
-
- pthread_join(tid_send, NULL);
- pthread_join(tid_recv, NULL);
-
- close(sockfd);
- }
server.c
- #include "head.h"
-
- int main(void)
- {
- int sockfd = 0;
- ssize_t nsize = 0;
- ssize_t size = 0;
- struct sockaddr_in serveraddr;
-
- struct address useraddr[100];
- struct sockaddr_in userinfo;
- socklen_t addrlen = 0;
- addrlen = sizeof(userinfo);
-
- struct msgbuf recvuser;
- int i = 0;
-
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if(sockfd == -1)
- {
- perror("fail to socket");
- return -1;
- }
-
- serveraddr.sin_family = AF_INET;
- serveraddr.sin_port = htons(SERVER_PORT);
- serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
- bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
-
- memset(useraddr, 0, sizeof(useraddr));
-
- while(1)
- {
- memset(&recvuser, 0, sizeof(recvuser));
- memset(&userinfo, 0, sizeof(userinfo));
- nsize = recvfrom(sockfd, &recvuser, sizeof(recvuser), 0, (struct sockaddr *)&userinfo, &addrlen);
- if(nsize == -1)
- {
- return -1;
- }
- if(recvuser.type == USER_TYPE_INT)
- {
- for(i = 0; i < 100; i++)
- {
- if(useraddr[i].mark == 1)
- {
- continue;
- }
- else if(useraddr[i].mark == 0)
- {
- useraddr[i].mark = 1;
- useraddr[i].cltaddr.sin_family = AF_INET;
- useraddr[i].cltaddr.sin_port = userinfo.sin_port;
- useraddr[i].cltaddr.sin_addr.s_addr = userinfo.sin_addr.s_addr;
- printf("欢迎用户:%s来到直播间!\n", recvuser.name);
- break;
- }
- }
- }
- else if(recvuser.type == USER_TYPE_OUT)
- {
- for(i = 0; i < 100; i++)
- {
- if(memcmp(&useraddr[i].cltaddr, &userinfo, sizeof(userinfo)) == 0)
- {
- useraddr[i].mark = 0;
- printf("用户:%s离开直播间!\n", recvuser.name);
- }
- }
- }
- else if(recvuser.type == USER_TYPE_CHAT)
- {
- printf("%s(%s:%d)>%s\n", recvuser.name, inet_ntoa(userinfo.sin_addr), ntohs(userinfo.sin_port), recvuser.text);
- for(i = 0; i < 100; i++)
- {
- if(useraddr[i].mark != 0)
- {
- size = sendto(sockfd, &recvuser, sizeof(recvuser), 0, (struct sockaddr *)&useraddr[i].cltaddr, sizeof(useraddr[i].cltaddr));
- if(size == -1)
- {
- perror("fail to sendto");
- return -1;
- }
- }
- }
- }
- }
- close(sockfd);
-
- return 0;
- }
在这里head.h中定义了两个结构体,已经定义了客户发过来的状态
- #ifndef _HEAD_H_
- #define _HEAD_H_
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- struct msgbuf
- {
- int type;
- char name[32];
- char text[512];
- };
-
- struct address
- {
- struct sockaddr_in cltaddr;
- int mark;
- };
-
- #define USER_TYPE_INT 100
- #define USER_TYPE_OUT 200
- #define USER_TYPE_CHAT 300
-
- #define SERVER_ADDR "192.168.1.162"
- #define SERVER_PORT 5000
-
- #endif