C語言 udp socket 發送和接收不定長結構體

//socket udp 服務端
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    unsigned char szMagicNum[4];
    unsigned int u32MsgIdx;
    unsigned int u16MsgParamLen;
    unsigned char pMsgParam[0]; // 表示不定長
} MSG_INTF_S;

// 使用不定長的結構體發送數據的關鍵在於:
// 不能在結構體中使用指針變量, 改用pMsgParam[0]這種寫法
// 因爲pMsgParam[0]不定長, 所以結構體變量要分配到堆中, 即用malloc或new來分配空間

// 接收的話, 就申請一個大一點的buffer, 一次接收完發送端發過來的不定長數據即可
// 因爲UDP的特點: UDP發一包就是一包, 要麼收到, 要麼收不到一個完整的包

int main(void)
{
    //創建socket對象
    int sockfd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

    //創建網絡通信對象
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(9635);
    //server_addr.sin_addr.s_addr = INADDR_ANY; //綁定在 0.0.0.0 地址
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//綁定在 127.0.0.1 環回地址

    //綁定socket對象與通信鏈接
    int ret = bind(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
    if(ret < 0)
    {
        printf("bind fail\n");
        close(sockfd);
        return -1;
    }

    struct sockaddr_in client_addr;
    socklen_t len = sizeof(client_addr);

    MSG_INTF_S *pstRecvMsg = NULL;
    MSG_INTF_S stSendMsg;

    pstRecvMsg = (MSG_INTF_S *)malloc(10240);

    while(1)
    {
        memset(pstRecvMsg, 0, 10240);
        recvfrom(sockfd, pstRecvMsg, 10240, 0, (struct sockaddr*)&client_addr, &len);

        printf("server: recv a connect: ip:%s, port:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

        printf("server: recv szMagicNum[0]  = 0x%X\n", pstRecvMsg->szMagicNum[0]);
        printf("server: recv szMagicNum[1]  = 0x%X\n", pstRecvMsg->szMagicNum[1]);
        printf("server: recv szMagicNum[2]  = 0x%X\n", pstRecvMsg->szMagicNum[2]);
        printf("server: recv szMagicNum[3]  = 0x%X\n", pstRecvMsg->szMagicNum[3]);
        printf("server: recv u32MsgIdx      = %d\n", pstRecvMsg->u32MsgIdx);
        printf("server: recv u16MsgParamLen = %d\n", pstRecvMsg->u16MsgParamLen);
        printf("server: recv pMsgParam      = %s\n", pstRecvMsg->pMsgParam);

        ///////////////////////////////////////////////////////////////////////////////

        // 發一個消息給客戶端
        memset(&stSendMsg, 0, sizeof(MSG_INTF_S));
        stSendMsg.szMagicNum[0] = 0x11;
        stSendMsg.szMagicNum[1] = 0x22;
        stSendMsg.szMagicNum[2] = 0x33;
        stSendMsg.szMagicNum[3] = 0x44;

        sendto(sockfd, &stSendMsg, sizeof(MSG_INTF_S), 0, (struct sockaddr*)&client_addr, len);
    }

    close(sockfd);
    return 0;
}

 

//socket udp 客戶端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    unsigned char szMagicNum[4];
    unsigned int u32MsgIdx;
    unsigned int u16MsgParamLen;
    unsigned char pMsgParam[0]; // 表示不定長
} MSG_INTF_S;

// 使用不定長的結構體發送數據的關鍵在於:
// 不能在結構體中使用指針變量, 改用pMsgParam[0]這種寫法
// 因爲pMsgParam[0]不定長, 所以結構體變量要分配到堆中, 即用malloc或new來分配空間

// 接收的話, 就申請一個大一點的buffer, 一次接收完發送端發過來的不定長數據即可
// 因爲UDP的特點: UDP發一包就是一包, 要麼收到, 要麼收不到一個完整的包

int main(void)
{
    int buf = 0;
    unsigned int cnt = 0;

    //創建socket對象
    int sockfd=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    //綁定客戶端地址信息
    struct sockaddr_in client_addr;
    client_addr.sin_family = AF_INET;
    client_addr.sin_port = htons(9633);
    //client_addr.sin_addr.s_addr = INADDR_ANY; //綁定在 0.0.0.0 地址
    client_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//綁定在 127.0.0.1 環回地址

    if(bind(sockfd, (struct sockaddr* )&client_addr, sizeof(struct sockaddr_in)) < 0)
    {
        printf("bind failed\n");
        return -1;
    }

    //創建網絡通信對象
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(9635);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    socklen_t len = sizeof(server_addr);

    int TotalDataLen = 0;
    MSG_INTF_S *pstSendMsg = NULL;
    MSG_INTF_S stRecvMsg;

    while(1)
    {
        printf("client: input a number: ");
        scanf("%d", &buf);

        char *msg = (char *)"Too young too simple!!!"; // 準備不定長的數據
        TotalDataLen = sizeof(MSG_INTF_S) + strlen(msg) + 1;
        pstSendMsg = (MSG_INTF_S *)malloc(TotalDataLen); // 開闢一塊空間
        pstSendMsg->szMagicNum[0] = 0x55;
        pstSendMsg->szMagicNum[1] = 0x66;
        pstSendMsg->szMagicNum[2] = 0x77;
        pstSendMsg->szMagicNum[3] = 0x88;
        pstSendMsg->u32MsgIdx = cnt++;
        pstSendMsg->u16MsgParamLen = strlen(msg) + 1;
        memcpy(pstSendMsg->pMsgParam, msg, pstSendMsg->u16MsgParamLen);

        sendto(sockfd, pstSendMsg, TotalDataLen, 0, (struct sockaddr*)&server_addr, len);

        if(pstSendMsg)
        {
            free(pstSendMsg);
            pstSendMsg = NULL;
        }

        ////////////////////////////////////////////////////////////////////////////////

        // 接收服務端返回來的消息, 以此確認服務端已成功收到數據
        memset(&stRecvMsg, 0, sizeof(MSG_INTF_S));
        recvfrom(sockfd, &stRecvMsg, sizeof(MSG_INTF_S), 0, (struct sockaddr*)&server_addr, &len);
        printf("client: recv a connect: ip:%s, port:%d\n", inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));

        if(stRecvMsg.szMagicNum[0] == 0x11 && stRecvMsg.szMagicNum[1] == 0x22
            && stRecvMsg.szMagicNum[2] == 0x33 && stRecvMsg.szMagicNum[3] == 0x44)
        {
            printf("client: server receive success\n\n");
        }
        else
        {
            printf("client: server reveive fail\n\n");
        }
    }

    close(sockfd);
    return 0;
}

 

demo運行結果:

client: input a number: 4
server: recv a connect: ip:127.0.0.1, port:9633
server: recv szMagicNum[0]  = 0x55
server: recv szMagicNum[1]  = 0x66
server: recv szMagicNum[2]  = 0x77
server: recv szMagicNum[3]  = 0x88
server: recv u32MsgIdx      = 0
server: recv u16MsgParamLen = 24
server: recv pMsgParam      = Too young too simple!!!
client: recv a connect: ip:127.0.0.1, port:9635
client: server receive success

client: input a number: 5
server: recv a connect: ip:127.0.0.1, port:9633
server: recv szMagicNum[0]  = 0x55
server: recv szMagicNum[1]  = 0x66
server: recv szMagicNum[2]  = 0x77
server: recv szMagicNum[3]  = 0x88
server: recv u32MsgIdx      = 1
server: recv u16MsgParamLen = 24
server: recv pMsgParam      = Too young too simple!!!
client: recv a connect: ip:127.0.0.1, port:9635
client: server receive success

client: input a number: 8
server: recv a connect: ip:127.0.0.1, port:9633
server: recv szMagicNum[0]  = 0x55
server: recv szMagicNum[1]  = 0x66
server: recv szMagicNum[2]  = 0x77
server: recv szMagicNum[3]  = 0x88
server: recv u32MsgIdx      = 2
server: recv u16MsgParamLen = 24
server: recv pMsgParam      = Too young too simple!!!
client: recv a connect: ip:127.0.0.1, port:9635
client: server receive success

client: input a number: 99
server: recv a connect: ip:127.0.0.1, port:9633
server: recv szMagicNum[0]  = 0x55
server: recv szMagicNum[1]  = 0x66
server: recv szMagicNum[2]  = 0x77
server: recv szMagicNum[3]  = 0x88
server: recv u32MsgIdx      = 3
server: recv u16MsgParamLen = 24
server: recv pMsgParam      = Too young too simple!!!
client: recv a connect: ip:127.0.0.1, port:9635
client: server receive success
 

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