UDP:
用户数据报协议
特点:
是无连接的协议,不可靠,数据漏包,传输速度快
用途:
(1)发送小尺寸数据:比如DNS服务器进行IP地址查询
(2)在接收数据,给出应答比较困难的网络中使用UDP,比如:一些无线网络
(3)QQ,飞秋,MSN,等即时通信工具的音视频数据传输和视频会议等相关的应用
(4)流媒体、IPTV等网络多媒体服务中
(5)广播和组播通信
UDP服务器与客户端设计思路
UDP与TCP的区别
(1)socket的第二个参数
TCP:流式套接字 SOCK_STREAM
UDP:数据报套接字 SOCK_DGRAM
(2)流程不一样,UDP不需要listen,accept,connect
(3)收发数据不一样,UDP:recvfrom,sendto
示例代码1
服务器、客户端互发信息
//服务器
#include <stdio.h>
#include <error.h>
#include <errno.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
int main(int argc, char const *argv[])
{
//1、创建通信套接字 //域 数据报套接字
int s_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (s_socket == -1)
{
perror("socket");
return -1;
}
//套接字设置端口重用
int reuse = -1;
if(setsockopt(s_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(reuse)) < 0)
{
perror("setsockopt error");
return -1;
}
//2、初始化地址结构体
struct sockaddr_in s_addr;
memset(&s_addr, 0, sizeof(s_addr));
s_addr.sin_family = AF_INET; //地址族
s_addr.sin_port = 55628; //端口号
// s_addr.sin_addr.s_addr = inet_addr("192.168.1.235");
s_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//3、绑定服务器本机IP地址结构体和套接字
if(bind(s_socket, (struct sockaddr *)&s_addr, sizeof(struct sockaddr)))
{
perror("bind");
return -1;
}
printf("服务器地址绑定成功\n");
//4、数据接收
char r_buf[512];
char w_buf[512];
//定义一个结构体用来存放客户端的地址
struct sockaddr_in c_addr;
int len = sizeof(c_addr);
while(1)
{
memset(r_buf, 0, sizeof(r_buf));
memset(w_buf, 0, sizeof(w_buf));
memset(&c_addr, 0, sizeof(c_addr));
recvfrom(s_socket, r_buf, sizeof(r_buf), 0, (struct sockaddr *)&c_addr, &len);
printf("buf:%s\n", r_buf);
printf("[%s][%d]\n", inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port));
scanf("%[^\n]", w_buf);
getchar();
sendto(s_socket, w_buf, sizeof(w_buf), 0, (struct sockaddr *)&c_addr, sizeof(c_addr));
}
//5、关闭套接字
close(s_socket);
return 0;
}
复制代码
//客户端
#include <stdio.h>
#include <error.h>
#include <errno.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
int main(int argc, char const *argv[])
{
//1、创建通信套接字 //域 数据报套接字
int c_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (c_socket == -1)
{
perror("socket");
return -1;
}
//套接字设置端口重用
int reuse = -1;
if(setsockopt(c_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(reuse)) < 0)
{
perror("setsockopt error");
return -1;
}
//2、初始化地址结构体
struct sockaddr_in c_addr;
memset(&c_addr, 0, sizeof(c_addr));
c_addr.sin_family = AF_INET; //地址族
c_addr.sin_port = 55628; //端口号
c_addr.sin_addr.s_addr = inet_addr("192.168.1.11");
//3、绑定服务器本机IP地址结构体和套接字(可选)
// if(bind(c_socket, (struct sockaddr *)&c_addr, sizeof(struct sockaddr)))
// {
// perror("bind");
// return -1;
// }
// printf("服务器地址绑定成功\n");
//4、数据发送
char w_buf[512];
char r_buf[512];
//定义一个服务器的地址结构体,用来存储IP地址和端口号
struct sockaddr_in s_addr;
int len = sizeof(s_addr);
while(1)
{
memset(w_buf, 0, sizeof(w_buf));
memset(r_buf, 0, sizeof(r_buf));
scanf("%[^\n]", w_buf);
getchar();
sendto(c_socket, w_buf, sizeof(w_buf), 0, ( struct sockaddr *)&c_addr, sizeof(c_addr));
recvfrom(c_socket, r_buf, sizeof(r_buf), 0, (struct sockaddr *)&s_addr, &len);
printf("buf:%s\n", r_buf);
printf("[%s][%d]\n", inet_ntoa(s_addr.sin_addr), ntohs(s_addr.sin_port));
}
//5、关闭套接字
close(c_socket);
return 0;
}
复制代码
运行结果
示例代码2
设计一个UDP服务器,实现:
(1)初始化绑定服务器IP,端口号,套接字
(2)循环接收消息
1、客户端上线消息,显示当前上线的客户端IP和端口号,并记录
2、客户端聊天信息,直接转发给对方客户端
设计一个UDP客户端程序,实现:
(1)上线要发送一条上线消息
(2)线程1:发送消息(聊天消息标记,对方IP,消息内容)
(3)线程2:接收消息,显示
//服务器
#include <stdio.h>
#include <error.h>
#include <errno.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
//设计一个存放地址信息的链表节点结构体
typedef struct node
{
struct sockaddr_in client_addr;
struct node *next;
}Node, *List;
//消息结构体
struct MSG
{
int flag; //1表示上线消息,2表示聊天信息
char to_IP[20]; //表示消息发送给那个用户的IP
char from_IP[20]; //表示消息来自那个用户IP
char text[1024]; //消息内容
};
int main(int argc, char const *argv[])
{
//初始化一个带头结点的空链表
List caddr_list = malloc(sizeof(Node));
//1、创建通信套接字 //域 数据报套接字
int s_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (s_socket == -1)
{
perror("socket");
return -1;
}
//套接字设置端口重用
int reuse = -1;
if(setsockopt(s_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(reuse)) < 0)
{
perror("setsockopt error");
return -1;
}
//2、初始化地址结构体
struct sockaddr_in s_addr;
memset(&s_addr, 0, sizeof(s_addr));
s_addr.sin_family = AF_INET; //地址族
s_addr.sin_port = 55558; //端口号
// s_addr.sin_addr.s_addr = inet_addr("192.168.1.235");
s_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//3、绑定服务器本机IP地址结构体和套接字
if(bind(s_socket, (struct sockaddr *)&s_addr, sizeof(struct sockaddr)))
{
perror("bind");
return -1;
}
printf("服务器地址绑定成功\n");
//4、数据接收
struct MSG rmsg;
//定义一个结构体用来存放客户端的地址
struct sockaddr_in c_addr;
int len = sizeof(c_addr);
while(1)
{
bzero(&rmsg, sizeof(rmsg));
recvfrom(s_socket, &rmsg, sizeof(rmsg), 0, (struct sockaddr *)&c_addr, &len);
//客户端上线信息
if (rmsg.flag == 1)
{
//打印用户上线
printf("用户【%s:%d】上线\n", inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port));
//将用户添加到链表里面
List new = malloc(sizeof(Node));
if (new == NULL)
{
printf("新节点创建失败\n");
continue;
}
new->client_addr = c_addr;
new->next = NULL;
//将这个节点插入到链表中
new->next = caddr_list->next;
caddr_list->next = new;
printf("新用户信息已记录\n");
}
//发送信息
else
if (rmsg.flag == 2)//客户端聊天信息
{
List p = caddr_list;
while(p->next != NULL)
{
p = p->next;
if (strcmp(inet_ntoa(p->client_addr.sin_addr), rmsg.to_IP) == 0)
{
strcpy(rmsg.from_IP, inet_ntoa(c_addr.sin_addr));
sendto(s_socket, &rmsg, sizeof(rmsg), 0,
(struct sockaddr *)&(p->client_addr),
sizeof(struct sockaddr_in));
}
}
}
// else
// if (rmsg.flag == 3)//离线
// {
// //找到消息的源地址,从链表中删除,打印xxx下线
// }
}
//5、关闭套接字
close(s_socket);
return 0;
}
复制代码
//客户端
#include <stdio.h>
#include <error.h>
#include <errno.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
//消息结构体
struct MSG
{
int flag; //1表示上线消息,2表示聊天信息
char to_IP[20]; //表示消息发送给那个用户的IP
char from_IP[20]; //表示消息来自那个用户IP
char text[1024]; //消息内容
};
void *recv_msg(void *arg)
{
int c_socket = *(int *)arg;
//定义一个服务器的地址结构体,用来存储IP地址和端口号
struct sockaddr_in s_addr;
int len = sizeof(s_addr);
struct MSG rmsg;
while(1)
{
memset(&s_addr, 0, sizeof(s_addr));
memset(&rmsg, 0, sizeof(rmsg));
recvfrom(c_socket, &rmsg, sizeof(rmsg), 0, (struct sockaddr *)&s_addr, &len);
printf("-------------------------------------------\n");
printf("来自【%s】的消息:%s\n", rmsg.from_IP, rmsg.text);
printf("-------------------------------------------\n");
}
}
int main(int argc, char const *argv[])
{
//1、创建通信套接字 //域 数据报套接字
int c_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (c_socket == -1)
{
perror("socket");
return -1;
}
//套接字设置端口重用
int reuse = -1;
if(setsockopt(c_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(reuse)) < 0)
{
perror("setsockopt error");
return -1;
}
//2、初始化地址结构体
struct sockaddr_in c_addr;
memset(&c_addr, 0, sizeof(c_addr));
c_addr.sin_family = AF_INET; //地址族
c_addr.sin_port = 55558; //端口号
c_addr.sin_addr.s_addr = inet_addr("192.168.1.235");
//先发一条上线消息
struct MSG one_msg = {1, "", "", ""};
sendto(c_socket, &one_msg, sizeof(one_msg), 0, ( struct sockaddr *)&c_addr, sizeof(c_addr));
//4、数据发送
struct MSG smsg;
pthread_t pid;
pthread_create(&pid, NULL, recv_msg, (void *)&c_socket);
while(1)
{
memset(&smsg, 0, sizeof(smsg));
smsg.flag = 2;
scanf("%s%s", smsg.to_IP, smsg.text);
getchar();
sendto(c_socket, &smsg, sizeof(smsg), 0, ( struct sockaddr *)&c_addr, sizeof(c_addr));
printf("发送成功\n");
}
//5、关闭套接字
close(c_socket);
return 0;
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END