C:你如何通过套接字读取和解包消息?

Posted

技术标签:

【中文标题】C:你如何通过套接字读取和解包消息?【英文标题】:C: How do you read and unpack a message over a socket? 【发布时间】:2016-03-19 18:52:25 【问题描述】:

我正在尝试通过套接字接收更新的消息并解压缩 数据。我想使用 update_client 函数来更新服务器的 客户的内部代表协调并响应 具有所需值的客户端。

我想我应该使用 recv 将完整的消息(长度为 UPDATE_CMD_LEN)读入缓冲区。

然后解压具有以下格式消息的数据:

      U<id><fields of gpscoords>

字符“U”(表示更新),后跟客户端 ID(无符号字符) 然后是网络格式的gpscoord结构。

这是一个粗略的想法,但我不知道如何:

[1] 解包 ID

[2] 用int update_client(unsigned char id, gpscoords *pos解包坐标 它采用 ID 和指向位置结构的指针,错误返回 -1,成功返回 0

然后服务器应该使用 sendto 函数响应客户端并回复 NO_RES 表示错误,SUCC_RES 表示成功。

void do_update(int sockfd, struct sockaddr_in *clientaddr) 
    char msg[UPDATE_CMD_LEN];

    recv(sockfd, msg, UPDATE_CMD_LEN, flags);

    fprintf(stderr, "do_update not implemented!\n");
    exit(-1);

client.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

#include "gps.h"

#define CMD_LEN  10
#define LINE_LEN NAME_LEN + 15

#define FIREFOX "/usr/bin/firefox"


void make_route_url(char *url, gpscoords *pos, gpscoords *target) 
    char pos_sign_n;
    char pos_sign_e;
    char tar_sign_n;
    char tar_sign_e;

    pos_sign_n = pos->north ? '+' : '-';
    pos_sign_e = pos->east ? '+' : '-';
    tar_sign_n = target->north ? '+' : '-';
    tar_sign_e = target->east ? '+' : '-';

    sprintf(url,
            "http://map.project-osrm.org/?z=13&loc=%c%u.%u,%c%u.%u&loc=%c%u.%u,%c%u.%u",
            pos_sign_n, pos->lat, pos->lat_frac,
            pos_sign_e, pos->lon, pos->lon_frac,
            tar_sign_n, target->lat, target->lat_frac,
            tar_sign_e, target->lon, target->lon_frac);


void show_route(gpscoords *pos, gpscoords *target) 

    // char url[75];

    fprintf(stderr, "show_route not implemented!\n");
    exit(-1);




int run_client(char *name, char *serverip) 
    char *cmdline;
    size_t line_len;
    char cmd[11];
    gpsinfo info;
    gpscoords pos, targetpos;
    char targetname[NAME_LEN];

    bzero(targetname, NAME_LEN);
    bzero(&info, sizeof(info));
    bzero(&pos, sizeof(pos));
    bzero(&targetpos, sizeof(targetpos));

    if (register_client(serverip, name, &info) == -1) 
        perror("Error registering client.");
        return -1;
    

    find_self(&pos);
    if (update_position(&info, &pos) == -1) 
        printf("Failed to send initial position to server.\n");
        exit(-1);
    

    for (;;) 
        printf("Command: ");
        line_len = 0;
        cmdline = NULL;
        getline(&cmdline, &line_len, stdin);

        if (strncmp(cmdline, "update", 3) == 0) 
            find_self(&pos);
            if (update_position(&info, &pos) == -1) 
                printf("Failed to update.\n");
            
         else if (strncmp(cmdline, "find", 3) == 0) 
            sscanf(cmdline, "%10s %25s", cmd, targetname);
            if (get_position(&info, targetname, &targetpos) == -1) 
                printf("Couldn't find target.\n");
             else 
                show_route(&pos, &targetpos);
            
         else if (strncmp(cmdline, "quit", 4) == 0) 
            free(cmdline);
            break;
        
        free(cmdline);
    

    if (unregister_client(&info) == -1) 
        perror("Error unregistering client.");
        return -1;
    

    return 0;


int main(int argc, char **argv) 
    if (argc < 2) 
        printf("Usage: ./client <client name> <server_ip>\n");
        return 0;
    

    return run_client(argv[1], argv[2]);

socket.c

#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#include "sockets.h"


int create_dg_socket(in_port_t port) 

    // Create the socket

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) 
        printf("create_dg_socket cannot create socket!");
        return -1;
    

    // Bind the socket port

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) != 0) 
        printf("create_dg_socket cannot bind socket!");
        close(sockfd);
        return -1;
    

    return sockfd;



int open_dg_socket(char *ipaddr, in_port_t port, struct sockaddr_in *addr) 

    // Create the socket

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) 
        return -1;
    

    // Setup

    addr->sin_family = AF_INET;
    addr->sin_port = htons(port);
    addr->sin_addr.s_addr = inet_addr(ipaddr);

    return sockfd;



int dg_sendrecv(int sockfd,

                struct sockaddr_in *addr,
                char *message, size_t out_len,
                char *response, size_t in_len) 

    if (sendto(sockfd, message, out_len, 0, (struct sockaddr *)&addr, sizeof(*addr)) == -1) 
        printf("dg_sendrecv cannot send data!");
        return -1;
    

    int ret = recvfrom(sockfd, response, in_len, 0, NULL, NULL);
    if (ret == -1) 
        printf("dg_sendrecv cannot read data!");
        return -1;
    

    return ret;


/*
int dg_sendrecv(int sockfd,
                struct sockaddr_in *addr,
                char *message, size_t out_len,
                char *response, size_t in_len) 

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ipaddr);

    return dg_sendrecv_addr(sockfd, &addr, message, out_len, response, in_len);


*/

服务器.c

#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>

#include "sockets.h"
#include "gps.h"
#include "server.h"

#define NUM_CLIENTS 5

char clients[NUM_CLIENTS][NAME_LEN];
gpscoords coords[NUM_CLIENTS];



void do_register(int sockfd, struct sockaddr_in *clientaddr) 
    char msg[REGISTER_CMD_LEN];
    char *name;
    unsigned char id, i;

    if (recv(sockfd, msg, REGISTER_CMD_LEN, 0) == -1) 
        perror("Error receiving register command.");
        exit(-1);
    

    // first byte is server command, rest is name
    name = &msg[1];

    id = NO_RES;
    for (i = 0; i < NUM_CLIENTS; ++i) 
        if (clients[i][0] == '\0') 
            strncpy(clients[i], name, NAME_LEN);
            clients[i][NAME_LEN - 1] = '\0';
            bzero(&coords[i], sizeof(coords[i]));
            id = i + 1;
            break;
        
    

    if (sendto(sockfd, 
               &id, sizeof(id), 
               0,
               (struct sockaddr *)clientaddr, sizeof(*clientaddr)) == -1) 
        perror("Error replying to client.");
        exit(-1);
    


void do_unregister(int sockfd, struct sockaddr_in *clientaddr) 
    unsigned char msg[UNREGISTER_CMD_LEN];
    unsigned char id;
    char res = NO_RES;

    if (recv(sockfd, &msg, UNREGISTER_CMD_LEN, 0) == -1) 
        perror("Error receiving unregister command.");
        exit(-1);
    

    id = msg[1] - 1;
    if (id < NUM_CLIENTS) 
        clients[id][0] = '\0';
        res = SUCC_RES;
    

    if (sendto(sockfd, 
               &res, sizeof(res), 
               0, 
               (struct sockaddr *)clientaddr, sizeof(*clientaddr)) == -1) 
        perror("Error responding to client.");
        exit(-1);
    


int update_client(unsigned char id, gpscoords *pos) 
    id -= 1;
    if (id >= NUM_CLIENTS)
        return -1;
    memcpy(&coords[id], pos, sizeof(coords[id]));
    return 0;



void do_update(int sockfd, struct sockaddr_in *clientaddr) 
    /**
     * Receive an update message over the socket and unpack the
     * data.  Then use the update_client function to update the server's
     * internal representation of the client co-ordinates.  Then respond to the
     * client with the required value.
     * Use recv to read the full message (of length UPDATE_CMD_LEN) into buffer.
     * Unpack data, message of following format:
     *           U<id><fields of gpscoords>
     * Character 'U' (meaning update), followed by ID of the client (unsigned char)
     * and then the gpscoord structure in network format.
     *
     * 1. Unpack ID
     * 2. Unpack coordinates with (int update_client(unsigned char id, gpscoords *pos)
     * which takes ID and pointer to position struct, returns -1 on error and 0 on success
     * Server should then respond to client using sendto function and reply
     * NO_RES on error and SUCC_RES on success.
     */

    char msg[UPDATE_CMD_LEN];

    recv(sockfd, msg, UPDATE_CMD_LEN, flags);

    fprintf(stderr, "do_update not implemented!\n");
    exit(-1);


static gpscoords *get_client(char *name) 
    for (int i = 0; i < NUM_CLIENTS; ++i) 
        if (strcmp(name, clients[i]) == 0) 
            return &coords[i];
            break;
        
    
    return NULL;


void do_get(int sockfd, struct sockaddr_in *clientaddr) 
    char msg[GET_CMD_LEN];
    char res[GET_RES_LEN];
    char *name;

    if (recv(sockfd, msg, GET_CMD_LEN, 0) == -1) 
        perror("Error receiving get command.");
        exit(-1);
    

    name = &msg[1];
    name[NAME_LEN - 1] = '\0';

    gpscoords *pos = get_client(name);
    if (pos == NULL) 
        res[0] = NO_RES;
     else 
        res[0] = SUCC_RES;
        pack_gpscoords(pos, &res[1]);
    

    if (sendto(sockfd, 
               &res, GET_RES_LEN, 
               0, 
               (struct sockaddr *)clientaddr, sizeof(*clientaddr)) == -1) 
        perror("Error sending result to client.");
        exit(-1);
    




int serve(int sockfd) 
    char cmd;
    struct sockaddr_in clientaddr;
    socklen_t clientaddrlen = sizeof(clientaddr);

    for (;;) 
        if (recvfrom(sockfd, 
                     &cmd, sizeof(cmd), 
                     MSG_PEEK,
                     (struct sockaddr *)&clientaddr, &clientaddrlen) == -1) 
            exit(-1);
        

        switch (cmd) 
        case CMD_REGISTER:
            do_register(sockfd, &clientaddr);
            break;
        case CMD_UNREGISTER:
            do_unregister(sockfd, &clientaddr);
            break;
        case CMD_UPDATE:
            do_update(sockfd, &clientaddr);
            break;
        case CMD_GET:
            do_get(sockfd, &clientaddr);
            break;
        default: 
            break;
        
    



void init_info() 
    int i;

    for (i = 0; i < NUM_CLIENTS; ++i) 
        clients[i][0] = '\0';
    


int main(int argc, char **argv) 
    int sockfd = create_dg_socket(SERVER_PORT);
    if (sockfd == -1) 
        fprintf(stderr, "Error creating socket!\n");
        return -1;
    

    init_info();

    printf("Server ready.\n");

    serve(sockfd);

    // Note: server never terminates, we're relying on the system to clean up
    // our open socket file descriptor (and shut it down)

    return 0;

【问题讨论】:

@chqrlie 有什么建议吗? :) 我看不到您使用的协议。 @MartinJames 这是一个数据报套接字 【参考方案1】:

如果您正在寻找一种方法来保护和读取 char 数组中的数据,这可能会对您有所帮助:

char buffer[5+sizeof(gpscoords)];
int *int_pointer;
gpscoords *gps_pointer;

int id=1;
gpscoords gps="some gps contend";

int id_frombuffer;
gpscoords gps_frombuffer;
char c_frombuffer;

buffer[0]='U';

int_pointer=(int*)&buffer;
int_pointer+=1;
*int_pointer=id;

gps_pointer=(gpscoords*)&buffer;
gps_pointer+=5;
*gpspointer=gps;

//send buffer


c_frombuffer=buffer[0];//c_frombuffer now contains 'U'

int_pointer=(int*)&buffer;
int_pointer+=1;
id_frombuffer=*int_pointer;//id_frombuffer now contains 1

gps_pointer=(gpscoords*)&buffer;
gpscoords+=5;
gps_frombuffer=*pointer;//gps_frombuffer now contains "some gps contend"

例如,如果您的客户端代码如下所示:

void sendupdate(unsigned char id,gpscoords gps)

    char buffer[2+sizeof(gpscoords)];
    gpscoords *gps_pointer;

    buffer[0]='U';
    buffer[1]=id;

    gps_pointer=(gpscoords*)&buffer;
    gps_pointer+=2;
    *gpspointer=gps;
    send(socket, buffer, 2+sizeof(gpscoords), flags);

您的服务器功能应该如下所示:

void do_update(int sockfd, struct sockaddr_in *clientaddr)

    char buffer[2+sizeof(gpscoords)];

    recv(sockfd, buffer, 2+sizeof(gpscoords), flags);

    unsigned char id_frombuffer=buffer[1];

    gpscoords *gpspointer=(gpscoords*)&buffer;
    gpspointer+=2;

    update_client(id_frombuffer, gpspointer);


【讨论】:

我怎样才能在我的 do_update 方法中实现这个呢? 你能告诉我们发送缓冲区的客户端代码吗? 抱歉我的错,我的意思是代码中的部分是您发送在“void do_update()”方法中收到的缓冲区。如果我们不知道数据是如何打包的,我们无法帮助您解压数据。 server.c和client.c是一样的 让我们continue this discussion in chat。

以上是关于C:你如何通过套接字读取和解包消息?的主要内容,如果未能解决你的问题,请参考以下文章

C# Begin/EndReceive - 我如何读取大数据?

C错误从流式套接字读取数据包

从套接字读取消息时如何获取字节序?

如何在 Qt 中读取下一个数据包

Python:如何在数据包级别读取请求的详细信息

ioctl() 用于 C 中的套接字编程