1. 知识点
1.1 TCP和UDP优缺点
1.2 UDP通信流程
1.2.1 服务端
-
创建udp套接字
-
初始化服务端网络地址结构
-
绑定服务端网络地址
4.创建结构体用来存储客户端网络地址结构
- 接收客户数据
1.2.2 客户端
-
创建udp套接字
-
初始化服务器网络地址结构
-
客户端先发送数据
2. 函数接口参考course-1
3. udp通信的接收和发送
udp_server.c
cpp
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
//udp通信
//服务端 和客户端 接收发送信息
int main(int argc,char** argv)
{
//1. 创建udp套接字
int socket_fd = socket(AF_INET,SOCK_DGRAM,0);
if(socket_fd == -1){
printf("创建套接字失败\n");
return -1;
}
//2. 初始化服务端网络地址结构 //4.创建结构体cliaddr用来存储客户端网络地址结构
struct sockaddr_in cliaddr,seraddr;//cliaddr客户 seraddr服务
//初始化服务端地址
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(atoi(argv[2]));//设置端口 //主机字节序的短整型数据
seraddr.sin_addr.s_addr = inet_addr(argv[1]);//将将点分十进制转转为无符号的32位网络地址
socklen_t seraddr_len = sizeof(seraddr);
socklen_t cliaddr_len = sizeof(cliaddr);
//3. 绑定服务端网络地址
int bind_ok = bind(socket_fd,(struct sockaddr*)&seraddr,seraddr_len);
if(bind_ok==-1){
perror("bind failed");
}
//监听,可有可无
listen(socket_fd,4);
char sbuf[128];//发送
char rbuf[128];//接收
while(1){
memset(sbuf,0,sizeof(sbuf));
memset(rbuf,0,sizeof(rbuf));
//5. 接收客户数据,这里先接收客户发送的消息,第五个参数可以得到客户的地址
recvfrom(socket_fd,rbuf,sizeof(rbuf),0,(struct sockaddr *)&cliaddr,&cliaddr_len);
printf("server接收数据:%s\n",rbuf);
//发送,发送个客户,因为我们recvfrom函数接收已经得到了客户地址了
fgets(sbuf,sizeof(sbuf),stdin);
sendto(socket_fd,sbuf,sizeof(sbuf),0,(struct sockaddr *)&cliaddr,cliaddr_len);
printf("server发送数据:%s\n",sbuf);
//退出
if(strcmp("bye\n",sbuf)==0){
break;
}
//退出
if(strcmp("bye\n",rbuf)==0){
break;
}
}
close(socket_fd);
return 0;
}
udp_client.c
cpp
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
//udp通信
//客户端 和 服务端 发送接收信息
int main(int argc,char** argv)
{
//1. 创建udp套接字
int socket_fd = socket(AF_INET,SOCK_DGRAM,0);
if(socket_fd == -1){
printf("创建套接字失败\n");
return -1;
}
//2. 初始化服务器网络地址结构
struct sockaddr_in seraddr;//cliaddr客户 seraddr服务
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(atoi(argv[2]));//设置端口 //主机字节序的短整型数据
seraddr.sin_addr.s_addr = inet_addr(argv[1]);//将将点分十进制转转为无符号的32位网络地址
socklen_t seraddr_len = sizeof(seraddr);
//这个绑定可有可无
// struct sockaddr_in cliaddr;
// socklen_t cliaddr_len = sizeof(cliaddr);
// int bind_ok = bind(socket_fd,(struct sockaddr*)&cliaddr,cliaddr_len);
// if(bind_ok==-1){
// perror("bind failed");
// }
//监听,可有可无
listen(socket_fd,4);
char sbuf[128];//发送
char rbuf[128];//接收
while(1){
memset(sbuf,0,sizeof(sbuf));
memset(rbuf,0,sizeof(rbuf));
//3. 客户端先发送数据,发给服务端,因为上面我们初始化了服务器地址结构
fgets(sbuf,sizeof(sbuf),stdin);
sendto(socket_fd,sbuf,sizeof(sbuf),0,(struct sockaddr *)&seraddr,seraddr_len);
printf("client发送数据:%s\n",sbuf);
//接收服务端数据
recvfrom(socket_fd,rbuf,sizeof(rbuf),0,(struct sockaddr *)&seraddr,&seraddr_len);
printf("client接收数据:%s\n",rbuf);
//退出
if(strcmp("bye\n",sbuf)==0){
break;
}
//退出
if(strcmp("bye\n",rbuf)==0){
break;
}
}
close(socket_fd);
return 0;
}
4. 可以将 通信的双发 接收和发送 写在一起
whole_udp.c
cpp
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
int sock_fd = -1;
void *recv_addr2(void *arg)
{
struct sockaddr outside_addr = *(struct sockaddr *)arg;
socklen_t addr2_len = sizeof(outside_addr);
char sbuf[128];
while(1)
{
memset(sbuf,0,sizeof(sbuf));
fgets(sbuf,sizeof(sbuf),stdin);
sendto(sock_fd,sbuf,sizeof(sbuf),0,(struct sockaddr *)&outside_addr,addr2_len);
}
}
int main(int argc, char **argv)
{
//创建udp套接字
sock_fd = socket(AF_INET,SOCK_DGRAM,0);
//定义结构体
struct sockaddr_in local_addr, outside_addr;
//自己本地的地址信息
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(atoi(argv[2]));
local_addr.sin_addr.s_addr = inet_addr(argv[1]);
//计算本地大小
socklen_t local_addr_len = sizeof(local_addr);
//绑定本地
bind(sock_fd,(struct sockaddr *)&local_addr,local_addr_len);
//外地地址信息
outside_addr.sin_family = AF_INET;
outside_addr.sin_port = htons(atoi(argv[4]));
outside_addr.sin_addr.s_addr = inet_addr(argv[3]);
//计算外地大小
socklen_t outside_addr_len = sizeof(outside_addr);
//监听 ,可有可无
listen(sock_fd,4);
//创建线程
pthread_t tid;
//传参把外地地址信息传到线程函数中
pthread_create(&tid,NULL,recv_addr2,(void *)&outside_addr);
char rbuf[128];//接收
while(1)
{
memset(rbuf,0,sizeof(rbuf));
//接收,接收外地信息
recvfrom(sock_fd,rbuf,sizeof(rbuf),0,(struct sockaddr *)&outside_addr,&outside_addr_len);
//将是一个用于将 IPv4 地址从二进制形式转换为点分十进制字符串形式的C库函数。
char *ip = inet_ntoa(outside_addr.sin_addr);//ip
unsigned short port = ntohs(outside_addr.sin_port);//端口号
printf("[来自%s %u outside_addr]:%s\n",ip,port,rbuf);
}
}