一、思维导图
二、给代码添加链表
【server.c】
cpp
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
enum Type{
TYPE_REGIST,
TYPE_LOGIN
};
typedef struct Pack{
int size;
enum Type type;
char buf[2048];
int count;
}pack_t;
/*
typedef struct user_info{
union Data{
struct user_info* tail;
char ui[20];
}data;
struct user_info* next;
struct user_info* prev;
}UserInfo;
UserInfo* create_node(){
UserInfo* node = malloc(sizeof(UserInfo));
node->next = NULL;
node->prev = NULL;
node->data.tail = node;
return node;
}
*/
//从pack协议包中解析数据
void read_pack(pack_t* pack)
{
char* buf = pack->buf;
//记录buf已经读取的字节个数,方便定位到最靠前的未读取的位置
int readed_size = 0;
while(1){
//将buf中最靠前未读取的2个字节,当作short读取,并赋值给data_size
short data_size = *(short*)(buf + readed_size);
if(data_size == 0){
printf("数据解析完毕\n");
break;
}
//因为已经读取了2个字节,readed_size要自增2字节
readed_size += 2;
//准备一个大小为data_size+1的字符数组,用来接收数据
char temp[data_size + 1];
//初始化该数组
memset(temp, 0, data_size+1);
memcpy(temp, buf + readed_size, data_size);
readed_size += data_size;
printf("temp = %s\n", temp);
}
}
int main(int argc, const char* argv[])
{
if(argc != 2){
printf("请输入端口号!\n");
return 1;
}
//将字符串转换成int类型port
int port = atoi(argv[1]);
//创建服务器套接字
int server = socket(AF_INET, SOCK_STREAM, 0);
//准备网络地址结构体: struct sockaddr_in
addr_in_t addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
//为套接字绑定ip和port
if(bind(server, (addr_t*)&addr, sizeof(addr)) == -1){
perror("bind()");
return 1;
}
//监听
listen(server, 10);
//接收客户端的连接
addr_in_t client_addr = {0};
int client_addr_len = sizeof(client_addr);
int client = accept(server, (addr_t*)&client_addr, &client_addr_len);
if(client != -1){
printf("=== 客户端连接 ===\n");
}
//读取客户端发来的消息
while(1){
int pack_size = 0;
int res = read(client, &pack_size, 4);
if(res == 0){
printf("=== 客户端断开连接 === \n");
return 0;
}
//当前的 pack_size = 4(pack.size大小) + 4(pack.type大小) + buf大小
pack_t pack = {0};
read(client, (char*)&pack+4, pack_size-4);
pack.size = pack_size;
//解析协议包的所有数据
read_pack(&pack);
}
return 0;
}
【client.c】
cpp
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
enum Type{
TYPE_REGIST,
TYPE_LOGIN
};
typedef struct Pack{
int size; //记录整个协议包实际大小(定长部分,占4个字节)
enum Type type; //定长部分,占4个字节
char buf[2048]; //变长部分,占count个字节
int count; //记录指针偏移量,即buf使用了多少个字节
}pack_t;
/*
typedef struct user_info{
union Data{
struct user_info* tail;
char ui[20];
}data;
struct user_info* next;
struct user_info* prev;
}UserInfo;
UserInfo* create_node(){
UserInfo* node = malloc(sizeof(UserInfo));
node->next = NULL;
node->prev = NULL;
node->data.tail = node;
return node;
}
*/
void append(pack_t* pack, const char* data)
{
char* buf = pack->buf;
int len = strlen(data);
//将buf的前2个字节当作short来存储数据len
*(short*)(buf + pack->count) = len;
pack->count += 2;
memcpy(buf + pack->count, data, len);
pack->count += len;
pack->size = pack->count + 8;
}
int main(int argc, const char* argv[])
{
if(argc != 2){
printf("请输入端口号!\n");
return 1;
}
//将字符串转换成int类型port
int port = atoi(argv[1]);
int client = socket(AF_INET, SOCK_STREAM, 0);
addr_in_t addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr("192.168.126.155");
if(connect(client, (addr_t*)&addr, sizeof(addr)) == -1){
perror("connect()");
return 1;
}
while(1){
pack_t pack = {0};
pack.type=TYPE_LOGIN;
char name[20] = "";
char pwd[20] = "";
printf("请输入账号:");
scanf("%20s", name);
while(getchar() != 10);
printf("请输入密码:");
scanf("%20s", pwd);
while(getchar() != 10);
//前2个字节记录数据长度n,紧接着的n个字节记录数据本身
//将name和pwd存入pack.buf中(即将name和pwd按照既定格式添加进协议包))
append(&pack, name);
append(&pack, pwd);
//协议包总共占据 pack.count + 4 字节
//pack.count 的长度,根据name和pwd的长度动态决定
write(client, &pack, pack.size);
}
return 0;
}