/**實踐: 實現一個基於UDP的echo回聲server/client**/ //server端代碼 void echoServer(int sockfd); int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) err_exit("socket error"); struct sockaddr_in servAddr; servAddr.sin_family = AF_INET; servAddr.sin_addr.s_addr = htonl(INADDR_ANY); servAddr.sin_port = htons(8001); if (bind(sockfd, (const struct sockaddr *)&servAddr, sizeof(servAddr)) == -1) err_exit("bind error"); echoServer(sockfd); } void echoServer(int sockfd) { char buf[BUFSIZ]; ssize_t recvBytes = 0; struct sockaddr_in clientAddr; socklen_t addrLen; while (true) { memset(buf, 0, sizeof(buf)); addrLen = sizeof(clientAddr); memset(&clientAddr, 0, addrLen); recvBytes = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&clientAddr, &addrLen); //如果recvBytes=0, 並不代表對端連接關閉, 因為UDP是無連接的 if (recvBytes < 0) { if (errno == EINTR) continue; else err_exit("recvfrom error"); } cout << buf ; if (sendto(sockfd, buf, recvBytes, 0, (const struct sockaddr *)&clientAddr, addrLen) == -1) err_exit("sendto error"); } }
/**client端代碼**/ void echoClient(int sockfd); int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) err_exit("socket error"); echoClient(sockfd); cout << "Client exiting..." << endl; } void echoClient(int sockfd) { struct sockaddr_in servAddr; servAddr.sin_family = AF_INET; servAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); servAddr.sin_port = htons(8001); char buf[BUFSIZ] = {0}; while (fgets(buf, sizeof(buf), stdin) != NULL) { if (sendto(sockfd, buf, strlen(buf), 0, (const struct sockaddr *)&servAddr, sizeof(servAddr)) == -1) err_exit("sendto error"); memset(buf, 0, sizeof(buf)); int recvBytes = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL); if (recvBytes == -1) { if (errno == EINTR) continue; else err_exit("recvfrom error"); } cout << buf ; memset(buf, 0, sizeof(buf)); } }UDP協議並不是像TCP一樣是一對一的通信,UDP可以實現廣播通信,並且由於是無連接的,只要知道對等方地址(ip和port) 都可以主動發數據。關閉server,再連接上,還可以進行通信。
總結:UDP客戶或服務進程,僅在使用自己的UDP套接字與確定的唯一對端進行通信時,才會調用connect,當然,一般UDP客戶端會用connect多一點。同時如果采用connect,其性能也會得到提升,因為對於sendto來講,其發送數據前和後,需要連接套接字、斷開套接字,如果connect之後,這兩步就省下了。
7.UDP外出接口的確定:
假設客戶端有多個IP地址,由connect /sendto 函數提供的遠程地址的參數,系統會選擇一個合適的出口,比如Server的IP是192.168.2.10, 而客戶端現在的IP有 192.168.1.32 和 192.168.2.75 那麼會自動選擇192.168.2.75 這個IP出去。(具體算法先略過...)
最後附上數據報截斷的一個示例:
int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) err_exit("socket error"); struct sockaddr_in servAddr; servAddr.sin_family = AF_INET; servAddr.sin_addr.s_addr = htonl(INADDR_ANY); servAddr.sin_port = htons(8001); if (bind(sockfd, (const struct sockaddr *)&servAddr, sizeof(servAddr)) == -1) err_exit("bind error"); //給自己發送數據 if (sendto(sockfd, "ABCDE", 5, 0, (const struct sockaddr *)&servAddr, sizeof(servAddr)) == -1) err_exit("sendto error"); for (int i = 0; i < 5; ++i) { char ch; int recvBytes = recvfrom(sockfd, &ch, 1, MSG_DONTWAIT, NULL, NULL); if (recvBytes == -1) { if (errno == EINTR) continue; else if (errno == EAGAIN) err_exit("recvfrom error"); } else cout << "char = " << ch << ", recvBytes = " << recvBytes << endl; } }