从上面图片可以看出,在udp编程模型中,服务器端不需要设置监听的套接字,客户端中也不需要建立连接的过程。
客户端的模型中,也可以在创建socket之后调用connect函数,不过此时udp的connect作用和tcp中的connect不一样,udp中的connect只是将sockaddr信息设置到socket中去,后面调用sendto和recvfrom的函数可以改为tcp中的send和recv。
详情参见下面代码:
//客户端代码
#include <stdlib.h>
#include <unistd.h>
#include <resolv.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
#include <string.h>
#include <signal.h>
const int MAXBUF=1000;
void sig_handler(int signo)
{
if(signo == SIGINT)
{
printf("server close\n");
exit(1);
}
}
int main()
{
// 第一个参数选用AF_INET表示选用ipv4,第二个参数选用SOCK_DGRAM(udp)
int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in dst;
dst.sin_family = AF_INET;
dst.sin_port = htons(10567);
dst.sin_addr.s_addr = htonl(0x7f000001);
int ret = 0;
// 将sockaddr信息设置到socketfd中
ret = connect(socketfd, (struct sockaddr*)&dst, sizeof(sockaddr_in));
if(ret != 0)
{
printf("connect failed\n");
close(socketfd);
socketfd = -1;
}
char buf[MAXBUF] = {0};
int i = 0;
{
memset(buf, 0, sizeof(buf));
sprintf(buf, "i = %d, socket = %d, time = %lu", i, socketfd, clock());
printf("send buffer = [%s]\n", buf);
send(socketfd, buf, strlen(buf), 0);
memset(buf, 0, sizeof(buf));
ret = recv(socketfd, buf, sizeof(buf), 0);
if(ret == 0)
{
printf("error find\n");
//break;
}
else
{
printf("recv len = %d, i = %d, socket = %d,\n", ret, i, socketfd);
printf("recv buffer = [%s]\n", buf);
}
}
close(socketfd);
socketfd = -1;
return 0;
}
服务器端代码:
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
const unsigned int MAXBUF = 1000;
int listenFd = -1;
// 信号处理函数
void sig_handler(int signo)
{
// ctrl+c信号
if(signo == SIGINT)
{
printf("server close\n");
close(listenFd);
listenFd = -1;
exit(1);
}
//子进程结束信号
if(signo == SIGCHLD)
{
printf("child process exit\n");
wait(0);
}
}
// 输出sockaddr信息
void out_addr(struct sockaddr_in *addr)
{
if(addr == NULL)
{
printf("invalid param addr\n");
return;
}
switch(addr->sin_family)
{
case AF_INET:
printf("AF_INET\n");
break;
case AF_INET6:
printf("AF_INET6\n");
break;
default:
printf("unknown\n");
break;
}
char ip[20] = {0};
memset(ip, 0, sizeof(ip));
//将ip地址转换成主机点分十进制
inet_ntop(addr->sin_family, &addr->sin_addr, ip, sizeof(ip));
printf("port = %d, ip = %s\n", ntohs(addr->sin_port), ip);
}
void do_service(int sockfd)
{
struct sockaddr_in clientAddr;
socklen_t len = sizeof(clientAddr);
char buffer[MAXBUF] = {0};
memset(buffer, 0, sizeof(buffer));
if(recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&clientAddr, &len) < 0)
{
printf("recv failed\n");
return;
}
else
{
out_addr(&clientAddr);
printf("client send = [%s]\n", buffer);
memset(buffer, 0, sizeof(buffer));
long int t = time(0);
char *ptr = ctime(&t);
size_t size = strlen(ptr) * sizeof(char);
if(sendto(sockfd, ptr, size, 0, (struct sockaddr*)&clientAddr, len) < 0)
{
printf("send failed [%s]\n", buffer);
}
else
{
}
}
}
int main()
{
if(signal(SIGINT, sig_handler) == SIG_ERR)
{
perror("signal error\n");
exit(1);
}
listenFd = socket(AF_INET, SOCK_DGRAM, 0);
if(listenFd <= 0)
{
printf("create tcp socket failed\n");
return -1;
}
int ret = 0;
int opt = 1;
// 设置地址重用
if((ret = setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) < 0)
{
perror("setsockopt error\n");
exit(1);
}
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(10567);
local.sin_addr.s_addr = INADDR_ANY;// htonl(0x7f000001);
ret = bind(listenFd, (struct sockaddr*)&local, sizeof(sockaddr));
if(ret != 0)
{
printf("bind ret = %d\n", ret);
return -2;
}
sockaddr_in src;
unsigned int srcLength = sizeof(sockaddr_in);
char buffer[MAXBUF] = {0};
while(1)
{
do_service(listenFd);
}
close(listenFd);
listenFd = -1;
return 0;
}