Windows/Linux下Socket網絡通信UDP傳輸Client/Server端的C/C++實現詳解

前言

在我的上兩篇博文中已詳盡解析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得到源服務器地址,就無法發出反饋信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章