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, ...)
#endif
int 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, ...)
#endif
int 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, ...)
#endif
int 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, ...)
#endif
int 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就都是已知的了,这样比较适合新手,特别是两边都是经验不足的开发人员。防止思维混乱。