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:你如何通过套接字读取和解包消息?的主要内容,如果未能解决你的问题,请参考以下文章