UDP客户端,也就是首先主动发送数据的一方,也就是发起服务请求的一方。
UDP服务器,也就是首先等待接收数据,并对接收的数据进行处理,返回计算结果的一方,也就是提供服务的一方。
在下面实验中使用到的函数
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
int send(int s, const void *msg, size_t len, int flags);
in_addr_t inet_addr(const char *cp);
uint16_t htons(uint16_t hostshort);
udp客户端测试
测试代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%s:%d -- "format"\n" \
,__FILE__,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endifint main(int argc, char *argv[])
{int client_socket;client_socket = socket(AF_INET,SOCK_DGRAM,0);DEBUG_INFO("client_socket = %d",client_socket);struct sockaddr_in server_addr;server_addr.sin_port = htons(6600);server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr("192.168.0.5");char buf[100];snprintf(buf,sizeof(buf),"time=%d",time(NULL));DEBUG_INFO("server_addr %s:%d on socket %d\n",inet_ntoa(server_addr.sin_addr),htons(server_addr.sin_port),client_socket);socklen_t addrlen = sizeof(server_addr);int send_len = sendto(client_socket,buf,strlen(buf),0,&server_addr,sizeof(server_addr));memset(buf,0,sizeof(buf));DEBUG_INFO("sendto %s:%d \n",inet_ntoa(server_addr.sin_addr),htons(server_addr.sin_port));int read_len = recvfrom(client_socket,buf,sizeof(buf),0,&server_addr,&addrlen);DEBUG_INFO("recvfrom %s:%d -- %s\n",inet_ntoa(server_addr.sin_addr),htons(server_addr.sin_port),buf);sleep(5);return 0;
}
UDP接收端(服务器)
执行代码:
实验解析
UDP客户端向192.168.0.5:6600 地址发送时间值,UDP接受端,也就是调试助手,返回一个字符串。然后UDP客户端等待5秒退出。这可以用来实现一个NTP服务器
UDP服务端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <fcntl.h>// #define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%s:%d -- "format"\n" \
,__FILE__,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endif#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%s:%d -- "format"\n" \
,__FILE__,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endifint main(int argc, char *argv[])
{int server_socket;int res = 0;int on = 1;server_socket = socket(AF_INET,SOCK_DGRAM,0);DEBUG_INFO("server_socket = %d",server_socket);if(setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,&on,sizeof(on)) < 0){perror("setsockopt");exit(-1);}struct sockaddr_in server_addr;server_addr.sin_port = htons(6600);server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr("192.168.0.11");res = bind(server_socket,(const struct sockaddr *)&server_addr,sizeof(server_addr));if(res == -1){perror("bind");return -1;}int len = 0;char buf[100];socklen_t addrlen;struct sockaddr_in client_server;while(1){memset(buf,0,sizeof(buf));memcpy(&client_server,&server_addr,sizeof(client_server));addrlen = sizeof(client_server);recvfrom(server_socket,buf,sizeof(buf),0,&client_server,&addrlen);DEBUG_INFO("buf = %s",buf);DEBUG_INFO("recfrom %s:%d -- %s\n",inet_ntoa(client_server.sin_addr),htons(client_server.sin_port),buf);addrlen = sizeof(client_server);sendto(server_socket,buf,strlen(buf),0,&client_server,addrlen);}sleep(5);return 0;
}
修改客户端中的IP地址为192.168.0.11,也就是虚拟机的IP地址
实验解析:
服务器等待接收数据。客户端向服务端发送数据,服务器端接收数据后,将数据原样返回。然后服务端继续等待接收数据。客户端发送完毕后等待接收,接收到数据后睡眠5秒退出程序。
使用connect的客户端
虽然UDP是无连接的,但是connect函数依然有效,它高速操作系统,默认从哪里接收数据,默认向谁发送数据。这样就可以使用read,wirte、send、recv这四个没有指定地址的函数来收发数据了。
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%s:%d -- "format"\n" \
,__FILE__,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endifint main(int argc, char *argv[])
{int client_socket;int res = 0;client_socket = socket(AF_INET,SOCK_DGRAM,0);DEBUG_INFO("client_socket = %d",client_socket);struct sockaddr_in remote_addr;remote_addr.sin_port = htons(6600);remote_addr.sin_family = AF_INET;remote_addr.sin_addr.s_addr = inet_addr("192.168.0.11");char buf[100];snprintf(buf,sizeof(buf),"time=%d",time(NULL));DEBUG_INFO("remote_addr %s:%d on socket %d\n",inet_ntoa(remote_addr.sin_addr),htons(remote_addr.sin_port),client_socket);res = connect(client_socket,&remote_addr,sizeof(remote_addr));int send_len = send(client_socket,buf,strlen(buf),0);memset(buf,0,sizeof(buf));int read_len = recv(client_socket,buf,sizeof(buf),0);DEBUG_INFO("recv %s:%d -- %s\n",inet_ntoa(remote_addr.sin_addr),htons(remote_addr.sin_port),buf);sleep(5);return 0;
}
测试结果:
既是客户端和也是服务端
如果一个客户端,偶尔也要提供服务的话,那么它绑定(bind)一个明确的本地端口是有必要的,这样别人就知道应该如何主动向它发起通讯了。下面是使用bind绑定本地端口的客户端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%s:%d -- "format"\n" \
,__FILE__,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endifint main(int argc, char *argv[])
{int client_socket;int res = 0;client_socket = socket(AF_INET,SOCK_DGRAM,0);DEBUG_INFO("client_socket = %d",client_socket);struct sockaddr_in remote_addr;remote_addr.sin_port = htons(6600);remote_addr.sin_family = AF_INET;remote_addr.sin_addr.s_addr = inet_addr("192.168.0.11");DEBUG_INFO("INADDR_ANY = %d",INADDR_ANY);struct sockaddr_in local_addr;local_addr.sin_port = htons(6700);local_addr.sin_family = AF_INET;local_addr.sin_addr.s_addr = INADDR_ANY;//inet_addr("192.168.0.11");res = bind(client_socket,(const struct sockaddr *)&local_addr,sizeof(local_addr));if(res == -1){perror("bind");return -1;}char buf[100];snprintf(buf,sizeof(buf),"time=%d",time(NULL));DEBUG_INFO("remote_addr %s:%d on socket %d\n",inet_ntoa(remote_addr.sin_addr),htons(remote_addr.sin_port),client_socket);res = connect(client_socket,&remote_addr,sizeof(remote_addr));int send_len = send(client_socket,buf,strlen(buf),0);memset(buf,0,sizeof(buf));int read_len = recv(client_socket,buf,sizeof(buf),0);DEBUG_INFO("recv %s:%d -- %s\n",inet_ntoa(remote_addr.sin_addr),htons(remote_addr.sin_port),buf);sleep(5);return 0;
}
执行结果:
实验解析:
服务端使用前面的测试代码。这样通讯两端的IP和PORT就都是已知的了,这样比较适合新手,特别是两边都是经验不足的开发人员。防止思维混乱。