大部分的准备工作的代码都合之前TCP时候的一样,我们只需要在关键部分进行一下修改即可。
首先是服务端的程序:
创建套接字—-绑定地址—-进行通讯
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUFF_SIZE 30
void error_handling(char *message);
int main (int argc ,char *argv[]){
//客户端和服务端的socket描述符
int serv_sock;
//客户端和服务端的地址
struct sockaddr_in serv_addr;
struct sockaddr_in clent_addr;
//客户端地址大小
socklen_t client_addr_size;
//要发送的消息
char messages [BUFF_SIZE];
//字符串长度
int str_len = 0;
if(argc!=2){
printf("Usage : %s <port> \n",argv[0]);
exit(1);
}
//在创建socket的时候需要指定连接方式为面向消息的
serv_sock = socket(PF_INET,SOCK_DGRAM,0);
//进行检查
if(serv_sock == -1){
error_handling("UDP socket creation error");
}
//
memset(&serv_addr,0,sizeof(serv_addr));
//为serve_addr结构体设置使用协议,IP地址,端口号
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
//进行地址绑定
if(bind(serv_sock,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) == -1){
error_handling("bind() error");
}
while(1){
client_addr_size = sizeof(clent_addr);
str_len = recvfrom(serv_sock,messages,BUFF_SIZE,0,(struct sockaddr *) &clent_addr,&client_addr_size);
sendto(serv_sock,messages,str_len,0,(struct sockaddr *) &clent_addr,client_addr_size);
}
close(serv_sock);
return 0;
}
void error_handling(char * message){
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
然后是客户端程序:
创建套接字—-进行通信
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUFF_SIZE 30
void error_handling(char *messages);
int main (int argc ,char * argv[]){
//本地的socket
int sock;
//要传输的消息
char message[BUFF_SIZE];
//保存消息的长度
int str_len;
//socket地址的长度
struct sockaddr_in server_addr;
struct sockaddr_in from_addr;
socklen_t addr_size;
//检查参数的个数
if(argc != 3){
printf("Usage : %s <port> \n",argv[0]);
exit(1);
}
//创建客户端的socket,并指定使用协议,连接方式。
sock = socket(PF_INET,SOCK_DGRAM,0);
//初始化server_addr所申请的内存空间
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
server_addr.sin_port = htons(atoi(argv[2]));
while(1){
fputs("Insert message ( q to quit ):",stdout);
fgets(message,sizeof(message),stdin);
//接收到结束输入的字符
if(!strcmp(message,"q\n") || !strcmp(message,"Q\n")){
break;
}
sendto(sock,message,strlen(message),0,(struct sockaddr *) &server_addr,sizeof(server_addr));
addr_size = sizeof(from_addr);
str_len = recvfrom(sock,message,BUFF_SIZE,0,(struct sockaddr *)&from_addr,&addr_size);
message[str_len] = 0;
printf("Message from server : %s\n",message);
}
close(sock);
return 0;
}
void error_handling(char * message){
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
至此我们就完成了一个基于UDP的服务端/客户端程序,我们可以对其进行编译运行,查看一下效果。
注意点:
我们在编写UDP的客户端时并没有直接给他指定IP地址和端口号,但是在运行时没有任何的错误。这是因为本应该在sendto()函数之前进行地址绑定的,也就是调用bind()函数,但是我们在书写时省略了,当调用sendto函数时系统发现尚未分配地址信息,就会随机的为其分配一个IP地址和端口号(IP地址用主机的IP地址,端口号使用尚未使用的端口号),所以无需进行额外的配置。
已连接套接字和未连接套接字:
在每次调用sendto()函数的时候都会重复一下的三个步骤:1. 向UDP套接字注册目标IP地址和端口号;2. 传输数据 3.清除UDP套接字中注册的目标地址信息。
这个机制很好的保证了UDP套接字的重复使用,使得只需要创建一个套接字便可以和许多的主机进行通讯。但是也有其缺点,就是当只是进行点对点的通讯时每次都需要经历一次创建过程,但是地址信息并没有发生变化,所以是无效的。为此产生了一个新的UDP套接字,他和一般的套接字唯一的区别在于他的目标信息是不发生变化的,这样就可以避免了多次无效的创建。
创建方法:
//创建客户端的socket,并指定使用协议,连接方式。
sock = socket(PF_INET,SOCK_DGRAM,0);
//初始化server_addr所申请的内存空间
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
server_addr.sin_port = htons(atoi(argv[2]));
//添加上connect()函数,使其成为已连接的套接字
connect(sock,(struct sockaddr *) &server_addr,sizeof(server_addr));