

/**實踐: 實現一個基於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;
}
}