TCP服务器与客户端搭建

news/2025/2/8 18:47:17 标签: linux, C, tcp/ip

一、思维导图

二、给代码添加链表

        【server.c】

#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】

#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;
}


http://www.niftyadmin.cn/n/5845206.html

相关文章

小白系列:数据库基础知识解析

前言 今天&#xff0c;我打算用简单明了的语言来讲解一下数据库的基本概念。总体上&#xff0c;这些内容与我在视频中讲解的基本一致。如果你发现视频的讲解有些难以理解&#xff0c;不妨看看这篇文字版的解释&#xff0c;希望能够更快速地帮助你掌握数据库的相关知识。需要注…

java练习(10)

ps:题目来自力扣 给你两个二进制字符串 a 和 b &#xff0c;以二进制字符串的形式返回它们的和。 class Solution {public String addBinary(String a, String b) {StringBuilder result new StringBuilder();int i a.length() - 1;int j b.length() - 1;int carry 0;whi…

k8s部署go-fastdfs

前置环境:已部署k8s集群,ip地址为 192.168.10.1~192.168.10.5,总共5台机器。 1. 创建provisioner制备器(如果已存在,则不需要) 制备器的具体部署方式可参考我的上一篇文章: k8s部署rabbitmq-CSDN博客文章浏览阅读254次,点赞3次,收藏5次。k8s部署rabbitmqhttps://blo…

UnoCSS 自定义规则

UnoCSS 作为一款强大的原子化 CSS 框架&#xff0c;为我们提供了高效构建样式的方式。除了使用其预设的原子化类名&#xff0c;掌握 UnoCSS 的自定义规则技能&#xff0c;能让开发者根据项目的独特需求&#xff0c;定制出更贴合业务的样式规则&#xff0c;进一步提升开发的灵活…

算法13(力扣225)-用队列实现栈

1、问题 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 实现 MyStack 类&#xff1a; void push(int x) 将元素 x 压入栈顶。 int pop() 移除并返回栈顶元素。…

硬核技术:小程序能够调用手机的哪些传感器

一、加速度传感器 小程序可以调用手机的加速度传感器来检测设备的运动状态。加速度传感器能够测量设备在三个轴&#xff08;X、Y、Z&#xff09;上的加速度变化。通过分析这些数据&#xff0c;小程序可以实现一些功能&#xff0c;如运动检测、步数统计、游戏中的动作感应等。 健…

UE学习日志#24 C++笔记#10 内存管理1

注&#xff1a;此文章为学习笔记&#xff0c;只记录个人不熟悉或备忘的内容 1 使用动态内存 1.1 如何描述动态内存 区分好栈上自动分配的变量和自由存储区的变量。 1.2 分配和释放 1.使用new和delete delete ptr;ptrnullptr; 2.避免在C中使用malloc()和free()&#xff0c;…

CRM系统中的数据分析和报表功能如何帮助企业?

CRM系统中的数据分析和报表功能&#xff1a;企业战略决策的得力助手 在当今竞争激烈的商业环境中&#xff0c;企业要想保持竞争力并实现持续增长&#xff0c;必须依靠精准的数据分析来制定有效的战略决策。而客户关系管理&#xff08;CRM&#xff09;系统的数据分析与报表生成…