典型UDP客户端和服务端结构与代码调用顺序:
recvfrom和sendto函数:
验证接收到的响应:
对于UDP,其不像TCP是基于有连接的,这样就有一个问题,sendto之后,recvfrom回来的数据可能不是从sendto目的地址回复的消息,这样就需要验证接收到的响应,提供如下方法供参考:
UDP的connet函数:
思考一个问题,如果服务器没启动,客户端sendto发送一个数据,结果会是什么呢?
结论是,sendto成功返回,如果客户端还同时调用了recvfrom,则将永远堵塞在recvfrom函数(当然可以设超时),同时通过tcpdump还可以看到,服务端返回icmp port unreachable错误消息,但是这个消息并没有通过sendto和recvfrom函数返回给用户进程,换句话说,用户并不知道服务端返回了icmp错误。怎么办呢,udp的connet函数可以搞定这些。
对于udp socket调用connect,称之为已连接UDP socket,其余未连接UDP socket区别如下:
例子:
下面给出 已连接UDP套接字的客户端代码 和 通过采用select的TCP和UDP混合的服务端代码。
udp client:
/* sockclnt.c*/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> /*for struct sockaddr_in*/ #define SVR_IP "127.0.0.1" #define SVR_PORT 1234 #define BUFSIZE 255 int main() { int ret = 0; int sockfd = 0; struct sockaddr_in svr_addr; memset(&svr_addr, 0, sizeof(struct sockaddr_in)); // create client socket fd sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { perror("socket failed"); exit(1); } // connet to server svr_addr.sin_family = AF_INET; svr_addr.sin_port = htons(SVR_PORT); svr_addr.sin_addr.s_addr = inet_addr(SVR_IP); ret = connect(sockfd, (struct sockaddr *)&svr_addr, sizeof(struct sockaddr_in)); if (ret == -1) { perror("connect failed"); exit(1); } int i = 0; for (i = 0; i < 150; i++) { char * msg = "udp hello server...."; printf("client send msg: %s\n", msg); if((send(sockfd, msg, strlen(msg), 0))<0) { perror("send()"); } char recv_buf[BUFSIZE] = {0}; int recv_len = recv(sockfd, recv_buf, sizeof(recv_buf), 0); printf("client recv msg: %s%d\n", recv_buf, recv_len); sleep(1); } close(sockfd); }
tcp client:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> /*for struct sockaddr_in*/ #define SVR_IP "127.0.0.1" #define SVR_PORT 1234 #define BUFSIZE 255 int main() { int ret = 0; int sockfd = 0; struct sockaddr_in svr_addr; memset(&svr_addr, 0, sizeof(struct sockaddr_in)); // create client socket fd sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror("socket failed"); exit(1); } // connet to server svr_addr.sin_family = AF_INET; svr_addr.sin_port = htons(SVR_PORT); svr_addr.sin_addr.s_addr = inet_addr(SVR_IP); ret = connect(sockfd, (struct sockaddr *)&svr_addr, sizeof(struct sockaddr_in)); if (ret == -1) { perror("connect failed"); exit(1); } int i = 0; for (i = 0; i < 100; i++) { char * msg = "tcp hello server...."; printf("client send msg: %s\n", msg); if((send(sockfd, msg, strlen(msg), 0))<0) { perror("send()"); } char recv_buf[BUFSIZE] = {0}; int recv_len = recv(sockfd, recv_buf, sizeof(recv_buf), 0); printf("client recv msg: %s%d\n", recv_buf, recv_len); sleep(2); } close(sockfd); }
udp tcp select server
/****************************************************************************** Copyright (C), 2001-2011, DCN Co., Ltd. ****************************************************************************** File Name : main.c Version : Initial Draft Author : Dong Shen Created : 2012/9/19 Last Modified : Description : transcode main ******************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/queue.h> #define MYPORT 1234 // the port users will be connecting to #define BACKLOG 512 // how many pending connections queue will hold #define BUF_SIZE 1024 #define MAX_PATH_LEN 255 #ifndef bool #define bool int #endif #define FALSE 0 #define TRUE 1 typedef struct fd_a_t { int fd_a; bool need_write; char * ret_buf; int ret_buf_len; TAILQ_ENTRY(fd_a_t) fd_a_node; }fd_a_t; typedef TAILQ_HEAD(fd_a_list_t_, fd_a_t) fd_a_list_t; void free_node(fd_a_t * cur_fd_node) { if (NULL == cur_fd_node) { return; } if (cur_fd_node->ret_buf != NULL) { free(cur_fd_node->ret_buf); cur_fd_node->ret_buf = NULL; } free(cur_fd_node); cur_fd_node = NULL; return; } int main(void) { int ret = 0; int listen_fd = 0; // listen on listen_fd int new_fd = 0; // new connection on new_fd int udp_fd = 0; // udp socket fd struct sockaddr_in server_addr; // server address information struct sockaddr_in client_addr; // connector's address information memset(&server_addr, 0, sizeof(struct sockaddr_in)); memset(&client_addr, 0, sizeof(struct sockaddr_in)); // create tcp listen socket, bind, listen listen_fd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == listen_fd) { perror("socket"); return -1; } int yes = 1; ret = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); if (-1 == ret) { perror("setsockopt"); return -1; } server_addr.sin_family = AF_INET; // host byte order server_addr.sin_port = htons(MYPORT); // short, network byte order server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // automatically fill with my IP memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero)); ret = bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (-1 == ret) { perror("bind"); return -1; } ret = listen(listen_fd, BACKLOG); if (-1 == ret) { perror("listen"); return -1; } udp_fd = socket(AF_INET, SOCK_DGRAM, 0); if (-1 == udp_fd) { perror("socket"); return -1; } bzero(&server_addr, sizeof(struct sockaddr_in)); server_addr.sin_family = AF_INET; // host byte order server_addr.sin_port = htons(MYPORT); // short, network byte order server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // automatically fill with my IP memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero)); ret = bind(udp_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (-1 == ret) { perror("bind"); return -1; } int maxsock = 0; int conn_amount = 0; fd_a_list_t fd_a_hd; TAILQ_INIT(&fd_a_hd); fd_set read_fdsr; fd_set write_fdsr; // timeout setting // struct timeval tv; while (1) { // tv.tv_sec = 30; // tv.tv_usec = 0; // initialize file descriptor set FD_ZERO(&read_fdsr); FD_ZERO(&write_fdsr); FD_SET(listen_fd, &read_fdsr); FD_SET(udp_fd, &read_fdsr); maxsock = listen_fd > udp_fd ? listen_fd : udp_fd; fd_a_t * cur_fd_node = TAILQ_FIRST(&fd_a_hd); while (cur_fd_node != NULL) { FD_SET(cur_fd_node->fd_a, &read_fdsr); FD_SET(cur_fd_node->fd_a, &write_fdsr); if (cur_fd_node->fd_a > maxsock) { maxsock = cur_fd_node->fd_a; } cur_fd_node = TAILQ_NEXT(cur_fd_node, fd_a_node); } // select read write socket fd, read_fdsr. ret = select(maxsock + 1, &read_fdsr, &write_fdsr, NULL, NULL); if (ret < 0) { perror("select"); break; } else if (ret == 0) { printf("select timeout\n"); continue; } // check whether a new connection comes if (FD_ISSET(listen_fd, &read_fdsr)) { int sin_size = sizeof(client_addr); new_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &sin_size); if (new_fd <= 0) { perror("accept"); continue; } // add to fd queue if (conn_amount < BACKLOG) { // create new fd node, insert into fd list fd_a_t * new_fd_a_node = calloc(1, sizeof(fd_a_t)); new_fd_a_node->fd_a = new_fd; TAILQ_INSERT_TAIL(&fd_a_hd, new_fd_a_node, fd_a_node); conn_amount++; printf("new connection client[%d] %s:%d\n", new_fd, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); } else { printf("max connections arrive, exit\n"); send(new_fd, "bye", 4, 0); close(new_fd); continue; } } if (FD_ISSET(udp_fd, &read_fdsr)) { char msg[BUF_SIZE] = {0}; struct sockaddr_in cliaddr; socklen_t len = sizeof(struct sockaddr_in); ssize_t n = recvfrom(udp_fd, msg, BUF_SIZE, 0, (struct sockaddr *)&cliaddr, &len); printf("udp recv: %s\n", msg); printf("udp send: %s\n", msg); sendto(udp_fd, msg, n, 0, (struct sockaddr *)&cliaddr, len); } // check every fd in the set cur_fd_node = TAILQ_FIRST(&fd_a_hd); while(cur_fd_node != NULL) { if (FD_ISSET(cur_fd_node->fd_a, &read_fdsr)) { char recv_buf[BUF_SIZE] = {0}; int ret_len = recv(cur_fd_node->fd_a, recv_buf, sizeof(recv_buf), 0); if (ret_len <= 0) // client close { printf("client[%d] close\n", cur_fd_node->fd_a); // close socket, clear fd_set close(cur_fd_node->fd_a); // remove fd_node from fd_list fd_a_t * next_fd_node = TAILQ_NEXT(cur_fd_node, fd_a_node); TAILQ_REMOVE(&fd_a_hd, cur_fd_node, fd_a_node); free_node(cur_fd_node); cur_fd_node = next_fd_node; conn_amount--; continue; } else // receive data { printf("tcp recv: %s\n", recv_buf); cur_fd_node->need_write = TRUE; cur_fd_node->ret_buf_len = ret_len; cur_fd_node->ret_buf = calloc(1, strlen(recv_buf) + 1); strcpy(cur_fd_node->ret_buf, recv_buf); } } if (FD_ISSET(cur_fd_node->fd_a, &write_fdsr)) { if (TRUE == cur_fd_node->need_write) { printf("tcp send %s\n", cur_fd_node->ret_buf); send(cur_fd_node->fd_a, cur_fd_node->ret_buf, cur_fd_node->ret_buf_len, 0); cur_fd_node->need_write = FALSE; } } cur_fd_node = TAILQ_NEXT(cur_fd_node, fd_a_node); } } fd_a_t * cur_fd_node = NULL; while ((cur_fd_node = TAILQ_FIRST(&fd_a_hd)) != NULL) { printf("client[%d] close\n", cur_fd_node->fd_a); // close socket, clear fd_set close(cur_fd_node->fd_a); // remove fd_node from fd_list TAILQ_REMOVE(&fd_a_hd, cur_fd_node, fd_a_node); free_node(cur_fd_node); conn_amount--; } return 1; }