前言
在我的上兩篇博文中已詳盡解析TCP的Client/Server傳輸。本篇博文記錄Windows的Cient端和Linux服務端的UDP傳輸。
代碼
Client端
#define _CRT_SECURE_NO_WARNINGS
#include <winsock2.h>
#pragma comment(lib, "WS2_32")
#include <iostream>
#include <stdio.h>
#include<time.h>
#define BUF_SIZE 1024
using namespace std;
int udp_client(){
WSADATA wsadata;
int iRet = 0;
// 初始化套接字動態庫
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0){
iRet = WSAGetLastError();
printf("WSAStartup failed !\n");
return -1;
}
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2)
{
printf("Wrong version!\n");
WSACleanup();
return 0;
}
SOCKADDR_IN servAddr;
SOCKET sockClient = socket(AF_INET,
SOCK_DGRAM,
IPPROTO_IP
);
if (sockClient == INVALID_SOCKET) {
cout << "Socket 創建失敗,Exit!";
return 0;
}
// 設置服務器地址
servAddr.sin_family = AF_INET;
servAddr.sin_addr.S_un.S_addr = inet_addr("39.105.16.186");
servAddr.sin_port = htons(6555);
char strSend[BUF_SIZE];
char strRecv[BUF_SIZE];
sprintf(strSend, "Hello World! This is client");
memset(strRecv,0,sizeof(strRecv));
int nServAddLen = sizeof(servAddr);
// 向服務器發送數據
iRet = sendto(sockClient, strSend, BUF_SIZE, 0, (sockaddr *)&servAddr, nServAddLen);
if (iRet == SOCKET_ERROR){
printf("sendto() failed:%d\n", WSAGetLastError());
closesocket(sockClient);
WSACleanup();
return -1;
}
printf("msg has been sent!\n");
// 從服務器接收數據
iRet = recvfrom(sockClient, strRecv, BUF_SIZE, 0, (sockaddr *)&servAddr, &nServAddLen);
if (iRet == SOCKET_ERROR)
{
printf("recvfrom failed:%d\n", WSAGetLastError());
closesocket(sockClient);
WSACleanup();
return -1;
}
printf("Recv From Server:%s\n", strRecv);
if (!closesocket(sockClient))
{
WSAGetLastError();
return 0;
}
if (!WSACleanup())
{
WSAGetLastError();
return 0;
}
cout << "客戶端關閉" << endl;
return 0;
}
int main(){
//tcp_client();
udp_client();
}
Server端
#include <stdio.h>
#include<string.h>
#include<errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define SERVER_PORT 6555
#define BUFF_LEN 1024
void handle_udp_msg(int fd)
{
char buf[BUFF_LEN];
socklen_t len;
int iRet;
struct sockaddr_in clent_addr;
while(1)
{
memset(buf, 0, BUFF_LEN);
len = sizeof(clent_addr);
iRet = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&clent_addr, &len);
if(iRet == -1)
{
printf("recieve data fail!\n");
return;
}
printf("Msg received! client:%s\n",buf);
memset(buf, 0, BUFF_LEN);
sprintf(buf, "I have recieved %d bytes data!\n", iRet);
printf("Answer has been sent:%s\n",buf);
sendto(fd, buf, sizeof(buf), 0, (struct sockaddr*)&clent_addr, len);
}
}
/*
server:
socket-->bind-->recvfrom-->sendto-->close
*/
int main(int argc, char* argv[])
{
int server_fd, ret;
struct sockaddr_in ser_addr;
server_fd = socket(AF_INET, SOCK_DGRAM, 0); //AF_INET:IPV4;SOCK_DGRAM:UDP
if(server_fd < 0)
{
printf("create socket fail!\n");
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
return -1;
}
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
ser_addr.sin_port = htons(SERVER_PORT);
ret = bind(server_fd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
if(ret < 0)
{
printf("socket bind fail!\n");
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
return -1;
}
printf("======waiting for client's request======\n");
handle_udp_msg(server_fd);
close(server_fd);
return 0;
}
重要知識點
https://linux.die.net/man/2/recv
再上個可以看文檔的地址。
sendto/recvfrom
細枝末節在前兩篇博文已全部分析詳盡。UDP相比於TCP最大的區別有二:
一是不需要連接,所以加快了傳輸速度,但是也加大了丟包的可能,
二是使用sendto/recvfrom。這是因爲在沒有連接的狀態下,如果不指定地址,無法發送數據。recvfrom和recv是差不多的,相比於recv,recvfrom可以接收發送地址等額外數據。在服務器端,如果沒有用recvfrom得到源服務器地址,就無法發出反饋信息。