关注

利用C语言实现信号相关检测并实现数据库存储及网络传输

目录

项目概述

系统背景

技术架构

功能特点

项目价值

关键技术

项目源代码

主流程:

辅助文件们:

文件处理部分FileProcessFunc.c和FileProcessFunc.h:

FileProcessFunc.h:

FileProcessFunc.c:

数据结构类部分Map.h和Map.c && LinkedList.h和LinkedList.c

Map.h:

Map.c:

LinkedList.h:

LinkedList.c:

数据库处理部分:DataBaseFunc.h && DataBaseFunc.c

DataBaseFunc.h:

DataBaseFunc.c:

存储选择最优小区部分CellFunc.h和CellFunc.c

CellFunc.h:

CellFunc.c:

网络通信模块部分:NetCommunicationFunc.h和NetCommunicationFunc.c

NetCommunicationFunc.h:

NetCommunicationFunc.c:

以上都是客户端的代码,下面是服务器端:Server.c

部分结果展示:


项目概述

        本项目是一个基于C/C++开发的移动通信小区选择系统,旨在解决现代通信网络中小区选择与信号匹配的关键问题。系统通过采集和分析I/Q信号数据,结合同步信号(SS)和主同步信号(PSS),实现对最佳服务小区的智能识别与选择,为移动通信网络优化与规划提供可靠的技术支持。

系统背景

        在5G/4G移动通信网络中,终端设备需要从多个可用小区中选择信号质量最佳的小区进行连接。这一过程涉及复杂的信号处理、数据分析与决策逻辑,对网络性能和用户体验有着直接影响。本系统针对这一关键环节,提供了从信号采集、数据处理到小区选择的全流程解决方案。

技术架构

系统采用模块化设计理念,基于C/C++语言开发,包含六大核心功能模块:

链表数据结构模块:提供高效的数据管理基础

小区管理模块:封装小区相关算法与逻辑

数据库交互模块:实现数据持久化与查询

文件处理模块:管理信号数据文件的读写操作

映射优化模块:提供数据映射与筛选机制

网络通信模块:支持分布式系统协同工作

功能特点

  1. 数据处理能力:支持大规模I/Q信号数据的高效处理与分析,通过优化的链表结构实现快速数据访问与管理。
  2. 智能匹配算法:采用先进的信号匹配算法,精确识别同步信号与数据信号之间的对应关系。
  3. 多维度评估:基于信号强度、峰值位置等多个维度对候选小区进行综合评估,保证选择最优服务小区。
  4. 数据持久化:集成MySQL数据库,实现处理结果的可靠存储与高效查询。
  5. 网络分布式:支持通过Socket网络通信实现系统组件的分布式部署,提高系统的可扩展性。
  6. 可视化展示:提供直观的结果展示与分析功能,便于操作人员理解和使用系统输出。

项目价值

        本系统通过高效处理与分析通信信号数据,实现对最佳服务小区的精准选择,有效提升了网络连接质量与用户体验。系统的模块化设计与良好的可扩展性,使其能够适应不同场景的应用需求,为移动通信网络的优化与发展提供了有力支持。同时,作为教学实验平台,系统也为通信工程专业学生提供了理论与实践相结合的学习环境。

关键技术

  • 双向链表数据结构
  • 基于MySQL的数据库管理
  • Windows Socket网络编程
  • 多线程并发处理
  • 信号处理与分析算法
  • 动态内存管理优化

        通过这些技术的综合应用,系统实现了从原始信号数据到最终小区选择的完整处理流程,为移动通信网络的智能化、高效化发展提供了重要技术保障。

项目源代码:

注意下面的都是客户端的设计:服务器端的设计在最后注意

主流程:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "NetCommunicationFunc.h"
#include "Map.h"
#include "LinkedList.h"
#include "FileProcessFunc.h"
#include "CellFunc.h"
#include "DataBaseFunc.h"
#define THRESHOLD 1000.0
//平均信号强度指定最大文件数目
#define FILE_COUNTS 3
#define INITIAL_CAPACITY_CELLS 1
#define DEFAULT_ENSURE_FILE_TYPE pss

#pragma region 函数声明
void caclateMultipleFileAvgSignalStrength(const LinkedList** dataSetList, int fileCount, double** resAvg);
void printDataSet(const LinkedList** dataSetList, E_Type_File fileType, int fileCount);
void addDataToMap(ResultsMap* resMap, const LinkedList** dataSetList, E_Type_File fileType, int fileCount);
void printAvgRes(const double* res, int fileCount);
double caculateSingleFileAvgSignalStrength(const LinkedList* list, const char* fileName);
void relatedTest(const ResultsMap* mapFilterd, const LinkedList** ssOrPssSetList, int dataFilteredFileCount, int ssOrPssFileCount, Cell** cells, int* cellsCount, int capacity, E_Type_File fileType);
void caculateRelated(const LinkedList* dataList, const LinkedList* ssOrPssList, double** res);
void insertOrderedDataFileInfoToAvgOfStrengthTable(const ResultsMap* orderedDataFileMap, MYSQL* connect);
void insertCellsInfoToRelatedTable(const Cell* cells, MYSQL* connect, int cellsCount);
void insertCellResultInfoToCellsSelectedTable(const Cell cell, MYSQL* connect, const char* resFileName);
#pragma endregion
#pragma comment(lib,"ws2_32.lib")

char resultCell[20];
int main() {

	#pragma region 测试 文件是否读取无误 计算平均信号强度是否正确
	//LinkedList* list = malloc(sizeof(LinkedList));
	//initialLinkedList(list);
	//测试加载数据
	//printList(&list);
	//char* fileName  = NULL ;
	//createFileName(data,&fileName, 6);
	//loadDataFromTxt(list,fileName);
	//printf("%s\n", fileName);
	//printList(list);
	//printf("%lf\n", caculateSingleFileAvgSignalStrength(list, fileName));
	//free(fileName);
	//destroyList(list);
	#pragma endregion

	#pragma region 初始化 初始化MySql连接 数据集的建立 即指针数组初始化 以及记录哈希表的初始化 还有最终存储候选集的初始化

	

	MYSQL* connect = NULL;
	initialMySqlAndConnectDatabase(&connect, "127.0.0.1", "root", "数据库密码", "数据库名字", 0, NULL, 0);
	dropDataBase(connect, "新建的数据库名字");
	createDataBase(connect, "新建的数据库名字");
	changeDataBase(connect, "新建的数据库名字");
	int dataFileCount = 0;
	int ssOrPssFileCount = 0;
	int cellsCount = 0;
	//double* res = NULL;
	LinkedList** dataSetList = NULL;							//data数据集
	LinkedList** ssOrPssSetList = NULL;							//ssOrPss数据集
	ResultsMap mapOriginal;										//初始数据集 存储所有的文件名及结果
	ResultsMap mapFiltered;										//筛选满足阈值的数据集
	ResultsMap mapTargetCountAndMax;							//筛选满足阈值同时满足数目的数据集
	Cell* cells = malloc(sizeof(Cell) * INITIAL_CAPACITY_CELLS);//这里设置的动态内存是为了代码的扩展性 如果以后会使用非常非常大的数据 这里就可以扩容

	initialResultsMap(&mapOriginal, 2);
	initialResultsMap(&mapFiltered, 2);
	initialResultsMap(&mapTargetCountAndMax, 2);
	initialCells(cells, cellsCount);
	
	#pragma endregion

	#pragma region 统一创建数据库中需要的表
	createDataStrengthTable(connect, "avg_of_strength_Table", "dataFileName", "avgOfStrength");//创平均信号强度表即avg_of_strength_Table
	createCellsTable(connect, "related_table", "dataFileName", "ssOrPssFileName", "mountainStrength", "mountainPosition");//创候选集表即related_table
	createCellSelectedResultTable(connect, "cells_selected_table", "dataFileName", "ssOrPssFileName", "mountainStrength", "mountainPosition", "selectedCellResult");//创建小区选择表
	#pragma endregion

	#pragma region 加载同一类型的文件至一个指针数组中存储备用 以及 将获取的指针数组经过计算后的平均信号强度 存储至原始文件数据哈希表中
	loadAllSamePrefixTxtFile(&dataSetList, &dataFileCount, data);//这里加载的是data数据集 注意这里的文件类型要和添加时一致 不然没有意义
	loadAllSamePrefixTxtFile(&ssOrPssSetList, &ssOrPssFileCount, DEFAULT_ENSURE_FILE_TYPE);//这里加载的是ssOrPss数据集
	addDataToMap(&mapOriginal, dataSetList, data, dataFileCount);//这里加载的是data数据集 注意这里的文件类型要和加载时一致 不然没有意义 这里传入的是数组指针 在内部进行计算后 存储的平均信号强度及文件名
	#pragma endregion

	#pragma region 人机交互 打印初始平均信号强度文件集 打印排序后的平均信号强度集 插入排序后的结果至数据库中 打印满足阈值的平均信号强度集 打印满足阈值同时指定最大数目的文件集
	printf("未排序前的数据:\n\n");
	printMap(&mapOriginal);
	printf("---------------------------------\n\n");
	printf("排序后的数据 :\n\n");
	sortMapByAvg(&mapOriginal);
	printMap(&mapOriginal);

	printf("---------------------------------\n\n");
	printf("向avg_of_strength_Table中插入数据\n\n");
	insertOrderedDataFileInfoToAvgOfStrengthTable(&mapOriginal, connect);//插入数据data排序后的数据

	printf("---------------------------------\n\n");
	printf("满足指定阈值要求的文件集(阈值为:%lf):\n\n", THRESHOLD);
	mapFiltered = *filterByThreshold(&mapOriginal, &mapFiltered, THRESHOLD);
	printMap(&mapFiltered);

	printf("---------------------------------\n\n");
	printf("满足指定阈值同时指定的最大数目为%d的文件集:\n\n", FILE_COUNTS);
	mapTargetCountAndMax = *targetCountsMaxAvgMap(&mapFiltered, &mapTargetCountAndMax, FILE_COUNTS);
	printMap(&mapTargetCountAndMax);

	printf("---------------------------------\n\n");
	#pragma endregion

	#pragma region 测试 数据集是否正确读取
	//for (int i = 0; i < fileCount; i++) {
	//	printList(dataSetList[i]);
	//}
	// 测试是否正确读取
	//printDataSet(dataSetList, data, fileCount);
	//printDataSet(ssOrPssSetList, ss, ssOrPssFileCount);
	// 测试计算结果
	//caclateMultipleFileAvgSignalStrength(dataSetList, fileCount, &res);
	//printAvgRes(res, fileCount);
	#pragma endregion

	#pragma region 用于进行相关检测计算和记录结果
	if (mapTargetCountAndMax.count == 0) {
		printf("没有满足阈值频点信息\n\n");
	} else {
		printf("\n");
		relatedTest(&mapTargetCountAndMax, ssOrPssSetList, dataFileCount, ssOrPssFileCount, &cells, &cellsCount, INITIAL_CAPACITY_CELLS, DEFAULT_ENSURE_FILE_TYPE);
		printf("候选集个数%d\n", cellsCount);
		printf("候选集组合(未排序):\n\n");
		printCells(cells, cellsCount);
		printf("\n");
		printf("候选集组合(已排序,按照峰值进行排序):\n\n");
		sortCellsBySingalStrength(cells);
		printf("---------------------------------\n\n");
		printCells(cells, cellsCount);
		printf("\n");
		printf("向related_table中插入数据\n\n");
		printf("---------------------------------\n\n");
		insertCellsInfoToRelatedTable(cells, connect, cellsCount); //插入候选集信息 即相关峰信息
		printf("---------------------------------\n\n");
	}
	//创建结果文件名
	char* cellSelectedName = NULL;
	if (cells != NULL) {
		cellSelectedName = createResFileName(cells[cellsCount - 1].actualName, cells[cellsCount - 1].ensureName, DEFAULT_ENSURE_FILE_TYPE);
		printf("最终选取的小区组合为:%s\n\n", cellSelectedName);
		printf("---------------------------------\n\n");
		//插入最终选择的小区结果
		printf("向cells_selected_table中插入数据\n\n");
		insertCellResultInfoToCellsSelectedTable(cells[cellsCount - 1], connect, cellSelectedName);//插入最终选择的小区结果信息
		strcpy(resultCell, cellSelectedName); // 赋值
		printf("%s", resultCell);
	}
	#pragma endregion

	#pragma region 打印数据库中的表信息
	//打印从数据库读取的内容信息
	printf("---------------------------------\n\n");
	printf("打印avg_of_strength_table信息\n\n");
	printTableInfo(connect, "avg_of_strength_table");//平均信号强度表
	printf("---------------------------------\n\n");
	printf("打印related_table信息\n\n");
	printTableInfo(connect, "related_table");//相关性表
	printf("---------------------------------\n\n");
	printf("打印cells_selected_table信息\n\n");
	printTableInfo(connect, "cells_selected_table");//小区选择表
	#pragma endregion

	#pragma region 统一释放申请内存
	//统一释放内存
	mysql_close(connect);
	destroyMap(&mapOriginal);
	destroyMap(&mapFiltered);
	destroyMap(&mapTargetCountAndMax);
	//统一释放dataSetList数据集
	for (int i = 0; i < dataFileCount; i++) {
		destroyList(dataSetList[i]);//每个链表信息
		free(dataSetList[i]);//每个头结点
	}
	//统一释放ssOrPssSetList数据集
	for (int i = 0; i < ssOrPssFileCount; i++) {
		destroyList(ssOrPssSetList[i]);//每个链表信息
		free(ssOrPssSetList[i]);//每个头结点
	}
	//free(res);
	destroyCells(cells, cellsCount);
	free(dataSetList);
	free(ssOrPssSetList);
	free(cellSelectedName);
	#pragma endregion

	#pragma region  和服务器交互模块
	initialNetBase();//初始化网络库
	//创建客户端套接字
	SOCKET clientSocket;
	int ret = createClientSocketAndConnect(&clientSocket, "127.0.0.1", 8082);
	if (ret != 0) {
		printf("与服务器连接失败\n");
		return -1;
	}
	HANDLE sendInfoThread = CreateThread(NULL, 0, sendInfoThreadFunc, (LPVOID)clientSocket, 0, NULL);
	if (sendInfoThread == NULL) {
		printf("sendInfoThread创建失败\n");
		return -1;
	}
	HANDLE recvInfoThread = CreateThread(NULL, 0, receiveInfoThreadFunc, (LPVOID)clientSocket, 0, NULL);
	if (recvInfoThread == NULL) {
		printf("sendInfoThread创建失败\n");
		return -1;
	}
	WaitForSingleObject(sendInfoThread, INFINITE);
	WaitForSingleObject(recvInfoThread, INFINITE);
	closesocket(clientSocket);
	WSACleanup();
	#pragma endregion


	return 0;
}

#pragma region 向表插入信息的相关函数
/*插入平均信号强度的数据集*/
void insertOrderedDataFileInfoToAvgOfStrengthTable(const ResultsMap* orderedDataFileMap, MYSQL* connect) {
	if (orderedDataFileMap == NULL || connect == NULL) {
		printf("数据库连接不正确或传入的哈希表为空\n");
		return;
	}
	printf("当前的表为:%s\n","avg_of_strength_Table");
	printf("表头为:%s\t%s\t\n\n","dataFileName", "avgOfStrength");
	for (int i = 0; i < orderedDataFileMap->count; i++) {
		//orderedDataFileMap->fileResults[i].fileName;
		insertInfoToDataStrengthTable(connect, "avg_of_strength_Table", "dataFileName", "avgOfStrength",
			_strdup(orderedDataFileMap->fileResults[i].fileName), orderedDataFileMap->fileResults[i].res);
	}
	printf("\n");
}
/*插入候选集信息*/
void insertCellsInfoToRelatedTable(const Cell* cells, MYSQL* connect, int cellsCount) {
	if (cells == NULL || connect == NULL) {
		printf("候选集为空或数据库连接不正确\n");
		return;
	}
	printf("当前的表为:%s\n", "related_table");
	printf("表头为:%s\t%s\t%s\t%s\t\n\n", "dataFileName", "ssOrPssFileName","mountainStrength", "mountainPosition");
	for (int i = 0; i < cellsCount; i++) {
		insertInfoToCellsTable(connect, "related_table", "dataFileName", "ssOrPssFileName", "mountainStrength", "mountainPosition",
			_strdup(cells[i].actualName), _strdup(cells[i].ensureName), cells[i].singalStrength, cells[i].maxIndex);
	}
	printf("\n");
}
/*插入最后选择的小区信息*/
void insertCellResultInfoToCellsSelectedTable(const Cell cell, MYSQL* connect,const char* resFileName) {
	printf("当前的表为:%s\n", "cells_selected_table");
	printf("表头为:%s\t%s\t%s\t%s\t%s\t\n\n", "selectedCellResult", "dataFileName", "avgOfStrength", "mountainStrength", "mountainPosition");
	insertInfoToCellResultTable(connect, "cells_selected_table", "dataFileName", "ssOrPssFileName", "mountainStrength", "mountainPosition", "selectedCellResult",
		_strdup(cell.actualName), _strdup(cell.ensureName), cell.singalStrength, cell.maxIndex, resFileName);
	printf("\n");
}
#pragma endregion

#pragma region 打印辅助函数
/*打印整个数据集信息*/
void printDataSet(const LinkedList** dataSetList, E_Type_File fileType, int fileCount) {
	if (fileCount == 0) return;
	for (int i = 0; i < fileCount; i++) {
		char* fileName = NULL;
		createFileName(fileType, &fileName, i);
		printf("成功打开文件:%s\t,其数据为:\n\n", fileName);
		printList(dataSetList[i]);
		free(fileName);
	}
}
/*打印平均信号强度数据集*/
void printAvgRes(const double* res, int fileCount) {
	for (int i = 0; i < fileCount; i++) {
		char* fileName = NULL;
		createFileName(data, &fileName, i);
		printf("当前文件名:%s\t;数据为%lf\n", fileName, res[i]);
		free(fileName);
	}
}
#pragma endregion

#pragma region 相关计算过程
/*计算单个文件的平均信号强度*/
double caculateSingleFileAvgSignalStrength(const LinkedList* list, const char* fileName) {
	double res = 0, tempI = 0, tempQ = 0;
	if (list == NULL || fileName == NULL) {
		printf("文件名不正确或数据不存在\n");
		return -1;
	}
	Node* temp = list->head;
	while (temp) {
		tempI = temp->i;
		tempQ = temp->q;
		res += sqrt(tempI * tempI + tempQ * tempQ);
		temp = temp->next;
	}
	return res / list->size;
}
/*处理多文件即一整个数据集的平均信号强度*/
void caclateMultipleFileAvgSignalStrength(const LinkedList** dataSetList, int fileCount, double** resAvg) {
	*resAvg = malloc(sizeof(double) * fileCount);
	for (int i = 0; i < fileCount; i++) {
		char* fileName = NULL;
		createFileName(data, &fileName, i);
		if (*resAvg == NULL) {
			printf("用于存储的结果数组分配内存失败,请检查这里的指针是否有效\n");
		} else {
			(*resAvg)[i] = caculateSingleFileAvgSignalStrength(dataSetList[i], fileName);
		}
		free(fileName);
	}
}
/*相关计算的核心*/
void caculateRelated(const LinkedList* dataList, const LinkedList* ssOrPssList, double** res) {
	if (dataList == NULL || ssOrPssList == NULL) {
		printf("传入的数据集为空\n");
		return;
	}
	int maxIndex = 0;
	double imaginary = 0, real = 0;
	double curRes = 0, maxRes = 0;

	Node* dataCurNode = dataList->tail;
	int windowCount = dataList->size - ssOrPssList->size + 1;
	if (windowCount <= 0) {
		printf("窗口长度不足,无法计算相关性\n");
		return;
		//*res = malloc(sizeof(double) * 2);
		//if (res == NULL) {
		//	printf("为存储结果分配的临时内存失败\n");
		//	return;
		//} else {
		//	(*res)[0] = 0;
		//	(*res)[1] = -1;
		//	return;
		//}
	}
	for (int i = 0; i < windowCount; i++) {
		imaginary = 0; real = 0;//每移动一个窗口 就进行重置一次
		curRes = 0;
		Node* ssOrPssCurNode = ssOrPssList->tail;//初始参与计算的ssOrPss节点 相当于确定信号结构体数组中的第一个
		Node* dataCaculateCurNode = dataCurNode; //进行滑动的实际窗口 只有每次对应ssOrPssCurNode滑动完之后才会移动到下一个
		if (dataCurNode == NULL) break;
		while (dataCaculateCurNode && ssOrPssCurNode) {//相关计算
			real +=		 ssOrPssCurNode->i * dataCaculateCurNode->i +
						 ssOrPssCurNode->q * dataCaculateCurNode->q;
			imaginary += ssOrPssCurNode->q * dataCaculateCurNode->i -
						 ssOrPssCurNode->i * dataCaculateCurNode->q;
				
			ssOrPssCurNode = ssOrPssCurNode->pre;
			dataCaculateCurNode = dataCaculateCurNode->pre;
		}
		curRes = sqrt(real * real + imaginary * imaginary);
		if (curRes > maxRes) {
			maxRes = curRes;
			maxIndex = dataCurNode->id;
		}
		dataCurNode = dataCurNode->pre;//每次往前移动一个
	}
	double* temp = malloc(sizeof(double) * 2);
	if (temp == NULL) {
		printf("临时分配的结果数组内存分配失败\n");
		return;
	} else {
		*res = temp;
		(*res)[0] = maxRes;
		(*res)[1] = maxIndex;
	}
	//*res = malloc(sizeof(double)*2);
	return;
}
/*传入的参数为 过滤后指定数目的实际文件集即data文件 确定信号集即SSOrPSS文件 以及这两个文件的数目 还有一个用于记录的结构体数组*/
void relatedTest(const ResultsMap* mapFilterd, const LinkedList** ssOrPssSetList, int dataFilteredFileCount, int ssOrPssFileCount, Cell** cells, int* cellsCount, int capacity,E_Type_File fileType) {
	int index = 0;
	if (capacity == 0) {
		printf("初始容量不能为0!!!!!以默认容量为1\n");
		capacity = 1;
	}
	for (int i = 0; i < mapFilterd->count; i++) {
		LinkedList* dataList = malloc(sizeof(LinkedList));
		initialLinkedList(dataList);
		loadDataFromTxt(dataList, mapFilterd->fileResults[i].fileName);//一个list就是指定的一组data数据
		for (int j = 0; j < ssOrPssFileCount; j++) {
			double* res = NULL;		//会有两个数据 第一个数据是峰值 第二个数据是峰值位置
			char* fileName = NULL;
			//ssOrPssSetList[j];

			caculateRelated(dataList, ssOrPssSetList[j], &res);
			createFileName(fileType, &fileName, j);
			if (index >= capacity) {
				capacity *= 2;
				Cell* temp = realloc(*cells, sizeof(Cell) * capacity);
				if (temp == NULL) {
					printf("小区数组扩容失败\n");
					return;
				} else {
					*cells = temp;
				}
			}
			if (*cells == NULL) {
				printf("用于存储的小区结构体数组内存分配无效\n");
			} else {
				if (res == NULL) {
					printf("临时分配数组失败请检查临时结果数组\n");
					return;
				} else {
					(*cells)[index].singalStrength = res[0];
					(*cells)[index].maxIndex = (int)res[1];
					(*cells)[index].ensureName = _strdup(fileName);
					(*cells)[index].actualName = _strdup(mapFilterd->fileResults[i].fileName);
					index++;
				}
			}
			free(fileName);
			free(res);

		}
		free(dataList);
	}
	Cell* temp =realloc(*cells, sizeof(Cell) * index);
	if (temp == NULL) {
		printf("小区数据数组缩容失败,将保留原始容量数组\n");
	} else {
		//做个标记 哪里有问题记得来看这里
		*cells = temp;
		//*cells = realloc(*cells, sizeof(Cell) * index);
	}
	*cellsCount = index;
}

#pragma endregion

/*将数据加入至map中*/
void addDataToMap(ResultsMap* resMap, const LinkedList** dataSetList, E_Type_File fileType, int fileCount) {
	double* resAvg = NULL;
	caclateMultipleFileAvgSignalStrength(dataSetList, fileCount, &resAvg);
	for (int i = 0; i < fileCount; i++) {
		char* fileName = NULL;
		createFileName(fileType, &fileName, i);
		addNewFileResult(resMap, fileName, resAvg[i]);
		free(fileName);
	}
	free(resAvg);
}

辅助文件们:

文件处理部分FileProcessFunc.c和FileProcessFunc.h:

这一部分主要实现了单个文件的内容读取,创建指定名的文件以及批处理同类型文件

FileProcessFunc.h:
#pragma once
#include"LinkedList.h"
#define INITIAL_CAPACITY 10
typedef enum E_Type_File
{
	data,
	ss,
	pss
}E_Type_File;
/*加载指定文件到一个列表中*/
int loadDataFromTxt(LinkedList* list, char* fileName);
/*创建指定文件名例如datax.txt,SSx.txt,PSSx.txt*/
void createFileName(E_Type_File fileType, char** fileName, int index);
/*将指定类型的文件 存储至同一个结构体数组指针中 方便后续使用*/
void loadAllSamePrefixTxtFile(LinkedList*** dataSetList, int* fileCount, E_Type_File fileType);
/*创建最后的结果文件名*/
char* createResFileName(const char* dataFileName, const char* ssOrPssFileName, E_Type_File type);
FileProcessFunc.c:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include"FileProcessFunc.h"
/*加载单个指定txt文件*/
int loadDataFromTxt(LinkedList* list, char* fileName) {
	if (fileName == NULL) {
		printf("文件名无效\n");
		return -1;
	}
	FILE* file = fopen(fileName, "r");
	if (file == NULL) {
		printf("文件名:  %s\t打开失败\n", fileName);
		return -1;
	}

	int count = 0;
	double tempI = 0, tempQ = 0;
	while (fscanf(file, "%lf", &tempI) == 1) {
		if (fscanf(file, "%lf", &tempQ) != 1) {
			printf("文件数据不成对\n");
			return -1;
		}
		Node* node = createNode(tempI, tempQ, count);
		addHeadList(list, node);
		count++;
	}
	// 检查是否因IO错误退出循环
	if (ferror(file)) {
		fprintf(stderr, "读取文件时发生错误\n");
		fclose(file); // 关闭文件
		return -1;
	}
	fclose(file);
	return 0;
}
/*创建指定名的文件如datax.txt,SSx.txt,PSS.txt*/
void createFileName(E_Type_File fileType, char** fileName, int index) {
	int fileNameSize = 0;
	switch (fileType)
	{
	case data:
		fileNameSize = snprintf(NULL, 0, "data%d.txt", index);
		*fileName = malloc(fileNameSize + 1);
		snprintf(*fileName, fileNameSize + 1, "data%d.txt", index);
		break;
	case ss:
		fileNameSize = snprintf(NULL, 0, "SS%d.txt", index);
		*fileName = malloc(fileNameSize + 1);
		snprintf(*fileName, fileNameSize + 1, "SS%d.txt", index);
		break;
	case pss:
		fileNameSize = snprintf(NULL, 0, "PSS%d.txt", index);
		*fileName = malloc(fileNameSize + 1);
		snprintf(*fileName, fileNameSize + 1, "PSS%d.txt", index);
		break;
	default:
		printf("文件名创建失败,请检查是否是data.txt,ss.txt,pss.txt文件\n");
		break;
	}
	return;
}
/*加载同类型的全部文件 放到一个指针数组中 三级指针是外部不变的地址 解引用一次得到指针数组 解引用两次得到每个list的地址*/
void loadAllSamePrefixTxtFile(LinkedList*** dataSetList, int* fileCount, E_Type_File fileType) {
	int capacity = INITIAL_CAPACITY;
	*fileCount = 0;
	*dataSetList = malloc(sizeof(LinkedList*) * capacity);
	for (int i = 0; ; i++) {
		char* fileName = NULL;
		LinkedList* list = malloc(sizeof(LinkedList));
		initialLinkedList(list);
		createFileName(fileType, &fileName, i);//二级指针返回
		int success = loadDataFromTxt(list, fileName);
		if (success == -1) {
			free(fileName);
			free(list);
			printf("此类型%d文件已经读取完毕(0代表data数据,\t1代表SS数据,\t2代表PSS数据)\n\n", fileType);
			break;
		}
		if (*fileCount >= capacity) {
			capacity *= 2;
			LinkedList** temp = realloc(*dataSetList, sizeof(LinkedList*) * capacity);
			if (temp == NULL) {
				printf("为数据集即dataSetList内存分配失败\n");
				return;
			} else {
				*dataSetList = temp;
			}
			//*dataSetList = realloc(*dataSetList, sizeof(LinkedList*) * capacity);
		}
		(*dataSetList)[(*fileCount)++] = list;
		free(fileName);
	}
	*dataSetList = realloc(*dataSetList, sizeof(LinkedList*) * (*fileCount));
	return;
}

/*创建每一个data结果文件*/
char* createResFileName(const char* dataFileName, const char* ssOrPssFileName,E_Type_File type) {
	//
	//sscanf(dataFileName, "data%d.txt", &dataNum);
	//sscanf(ssFileName, "SS%d.txt", &ssNum);
	//int fileNameSize = snprintf(NULL, 0, "data%dss%d", dataNum, ssNum);
	//char* fileName = (char*)malloc(fileNameSize + 1);
	//snprintf(fileName, fileNameSize + 1, "data%dss%d", dataNum, ssNum);
	//return fileName;
	int dataNum = 0, ssOrPssNum = 0, fileNameSize = 0;
	char* fileName = NULL;
	if (sscanf(dataFileName, "data%d.txt", &dataNum) != 1) {
		printf("提取基本文件名data中的数字失败\n");
		return NULL;
	}
	switch (type)
	{
	case ss:
		if (sscanf(ssOrPssFileName, "SS%d.txt", &ssOrPssNum) != 1) {
			printf("提取确定信号SS中的数字失败\n");
			return NULL;
		}
		fileNameSize = snprintf(NULL, 0, "data%dss%d", dataNum, ssOrPssNum);
		fileName = (char*)malloc(fileNameSize + 1);
		snprintf(fileName, fileNameSize + 1, "data%dss%d", dataNum, ssOrPssNum);
		return fileName;
	case pss:
		if (sscanf(ssOrPssFileName, "PSS%d.txt", &ssOrPssNum) != 1) {
			printf("提取确定信号PSS中的数字失败\n");
			return NULL;
		}
		fileNameSize = snprintf(NULL, 0, "data%dss%d", dataNum, ssOrPssNum);
		fileName = (char*)malloc(fileNameSize + 1);
		snprintf(fileName, fileNameSize + 1, "data%dss%d", dataNum, ssOrPssNum);
		return fileName;
	default:
		break;
	}
}

数据结构类部分Map.h和Map.c && LinkedList.h和LinkedList.c

        这一部分的主要作用就是实现了对于结果的处理,在map中实现了对于最强平均信号强度和文件名的存储。链表的数据结构主要是对于计算过程中从文件中读取的数据的存储。

Map.h:
#pragma once
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
	char* fileName;
	double res;
}Result;
typedef struct {
	Result* fileResults;
	int count;
	int capacity;

}ResultsMap;
/*初识化哈希表,分配初始内存*/
void initialResultsMap(ResultsMap* map, int capacity);
/*销毁 释放内存 C语言中必须自己手动释放*/
void destroyMap(ResultsMap* map);
/*将指定文件集 的 平均信号强度写入*/
void dataToMap(ResultsMap* map, int fileCount);
/*辅助函数 打印哈希表内容*/
void printMap(const ResultsMap* map);
/*排序函数 将哈希表按平均信号强度进行升序排序*/
void sortMapByAvg(ResultsMap* map);
/*辅助函数 交换哈希表中的两个结构体的值*/
void swap(ResultsMap* map, int i, int j);
/*增加 为哈希表增添新元素*/
int addNewFileResult(ResultsMap* map, const char* fileName, double value);
/*获取指定文件名的平均信号强度值 不过只有在判断存不存在的时候用了一下 作用中规中矩*/
double getValueOfFileName(ResultsMap* map, const char* fileName);
/*筛选满足阈值的文件集*/
ResultsMap* filterByThreshold(const ResultsMap* map, ResultsMap* resMap, double threshold);
/*筛选满足阈值的文件集的最大信号强度的指定数目*/
ResultsMap* targetCountsMaxAvgMap(const ResultsMap* originalMap, ResultsMap* resMap, int targetCounts);


Map.c:
#include "Map.h"
/*初始化一个Map*/
void initialResultsMap(ResultsMap* map,int capacity) {
	map->count = 0;
	map->capacity = capacity;
	map->fileResults = malloc(capacity * sizeof(Result));
}
/*添加一个新的文件数据进入map*/
int addNewFileResult(ResultsMap* map,const char* fileName,double value) {
	if (getValueOfFileName(map, fileName) == -1) {
		if (map->count >= map->capacity) {
			map->capacity *= 2;
			Result* temp = realloc(map->fileResults, map->capacity * sizeof(Result));
			if (temp == NULL) {
				printf("map扩容失败\n");
				return -1;
			} else {
				map->fileResults = temp;
			}
			//map->fileResults = realloc(map->fileResults, map->capacity * sizeof(Result));
		}
		if (fileName == NULL) {
			printf("请检查传入的文件名,文件名为空\n");
			return -1;
		} else {
			map->fileResults[map->count].fileName = _strdup(fileName);//注意这里 是重新在堆上开辟的一段新内存
			map->fileResults[map->count].res = value;
			map->count++;
		}
		return 0;
	}
	return -1;
}
/*获取指定文件名的结果值 顺便判断是否存在该文件名*/
double getValueOfFileName(ResultsMap* map,const char* fileName) {
	for (int i = 0; i < map->count; i++) {
		if (strcmp(map->fileResults[i].fileName,fileName)==0) {//字典序比较
			return map->fileResults[i].res;
		}
	}
	return -1;
}
/*清理!释放内存*/
void destroyMap(ResultsMap* map) {
	for (int i = 0; i < map->count; i++) {
		free(map->fileResults[i].fileName);
	}
	free(map->fileResults);
	map->capacity = 0;
	map->count = 0;
}
/*辅助函数 打印出来看看 */
void printMap(const ResultsMap* map) {
	for (int i = 0; i < map->count; i++) {
		printf("文件名:%s\t; 信号强度平均值:%lf \n", map->fileResults[i].fileName,
												map->fileResults[i].res);
	}
}
/*排序*/
void sortMapByAvg(ResultsMap* map) {
	int count = map->count;
	for (int i = count - 1; i >= 0; i--) {
		for (int j = 0; j < i; j++) {
			if (map->fileResults[j].res > map->fileResults[j + 1].res) {
				swap(map, j, j + 1);
			}
		}
	}
}
/*交换指定位置的两个结构体*/
void swap(ResultsMap* map, int i, int j) {
	Result temp = map->fileResults[i];
	map->fileResults[i].fileName = _strdup(map->fileResults[j].fileName);
	map->fileResults[i].res = map->fileResults[j].res;
	map->fileResults[j].fileName = _strdup(temp.fileName);
	map->fileResults[j].res = temp.res;
}
/*获取指定最大阈值的指定文件集*/
ResultsMap* targetCountsMaxAvgMap(const ResultsMap* originalMap, ResultsMap* resMap, int targetCounts) {
	int count = 0;
	for (int i = originalMap->count - 1; i >= 0; i--) {
		addNewFileResult(resMap, originalMap->fileResults[i].fileName, originalMap->fileResults[i].res);
		count++;
		if (count >= targetCounts) {
			break;
		}
	}
	return resMap;
}
/*筛选满足阈值要求的所有文件集*/
ResultsMap* filterByThreshold(const ResultsMap* map, ResultsMap* resMap, double threshold) {
	for (int i = 0; i < map->count; i++) {
		if (map->fileResults[i].res >= threshold) {
			addNewFileResult(resMap, map->fileResults[i].fileName, map->fileResults[i].res);
		}
	}
	printf("满足阈值要求的文件集共有:%d个\n", resMap->count);
	return resMap;
}

/*
 如果左边是结构体本身,就用点 (.)
 如果左边是指向结构体的指针,就用箭头 (->)
它们俩一个是“直接访问”,一个是“间接访问”。
*/
LinkedList.h:
#pragma once
/*结点结构体*/
typedef struct Node {
	double i;
	double q;
	int id;
	struct Node* pre;  //前驱结点
	struct Node* next; //后继结点
}Node;
/*链表结构体*/
typedef struct {
	Node* head;
	Node* tail;
	int size;
}LinkedList;
/*初始化链表 指定size==0 头结点 尾节点为空*/
void initialLinkedList(LinkedList* list);
/*创建每一个新节点*/
Node* createNode(double valueI, double valueQ, int i);
/*头插法 插入节点*/
void addHeadList(LinkedList* list, Node* node);
/*指定位置插入节点*/
void insertList(LinkedList* list, Node* node, int index);
/*删除指定节点*/
int deleteNode(LinkedList* list, Node* node);
/*得到指定节点的位置*/
void getNodePositionOfList(LinkedList* list, Node* node);
/*获取链表大小*/
int size(LinkedList* list);
/*清理链表*/
void destroyList(LinkedList* list);
/*打印链表内容*/
void printList(const LinkedList* list);
LinkedList.c:
#include "LinkedList.h"
#include <stdio.h>
#include <stdlib.h>
/*初始化链表*/
void initialLinkedList(LinkedList* list) {
	list->head = NULL;
	list->tail = NULL;
	list->size = 0;
}
/*创建一个结点*/
Node* createNode(double valueI, double valueQ, int i) {
	Node* node = (Node*)malloc(sizeof(Node));
	if (node == NULL) {
		printf("节点创建失败\n");
		return NULL;
	}
	node->i = valueI;
	node->q = valueQ;
	node->id = i;
	node->pre = NULL;
	node->next = NULL;
	return node;
}
/*头插法*/
void addHeadList(LinkedList* list, Node* node) {
	if (list && node) {
		if (list->size == 0) {
			list->head = node;
			list->tail = node;
			node->next = NULL;
			node->pre = NULL;
		} else {
			node->pre = NULL;
			node->next = list->head;
			list->head->pre = node;
			list->head = node;
		}
		list->size++;
	}
}
/*插入指定位置*/
void insertList(LinkedList* list, Node* node, int index) {
	if (!list || !node) return;
	if (index < 0 || index > list->size) return; // 越界保护
	if (list->size == 0) {
		list->head = node;
		list->tail = node;
	} else if (index == 0) {//头插
		addHeadList(list, node);
		return;
	} else if (index == list->size) {//尾插
		node->pre = list->tail;
		list->tail->next = node;
		node->next = NULL;
		list->tail = node;
	} else {
		Node* temp = list->head;
		for (int i = 0; i < index - 1; i++) {
			temp = temp->next;
		}
		node->pre = temp;
		node->next = temp->next;
		temp->next->pre = node;
		temp->next = node;

	}
	list->size++;
}
/*删除节点*/
int deleteNode(LinkedList* list, Node* node) {
	if (list && node) {
		if (node == list->head) {
			list->head = list->head->next;
			if (list->head) {//判单节点情况
				list->head->pre = NULL;
			} else {
				list->tail = NULL;
			}
			free(node);
			list->size--;
			return 0;
		} else if (node == list->tail) {
			list->tail = list->tail->pre;
			if (list->tail) {
				list->tail->next = NULL;
			} else {
				list->head = NULL;  // 链表变为空
			}
			free(node);
			list->size--;
			return 0;
		} else {
			Node* temp = list->head;
			while (temp != node && temp != NULL) {
				temp = temp->next;
			}
			if (temp == node) {
				temp->pre->next = temp->next;
				temp->next->pre = temp->pre;
				free(temp);
				list->size--;
				return 0;
			}
		}
	}
	return -1;
}
/*获取节点位置*/
void getNodePositionOfList(LinkedList* list, Node* node) {
	if (list) {
		Node* temp = list->head;
		while (temp) {
			if (temp == node) {
				printf("该节点的位置在:%d", list->size - temp->id - 1);
				return;
			}
			temp = temp->next;
		}
		printf("未找到指定节点\n");
		return;
	}
	return;
}
/*获取链表大小*/
int size(LinkedList* list) {
	return list->size;
}
/*销毁链表 */
void destroyList(LinkedList* list) {
	if (list) {
		Node* cur = list->head;
		while (cur) {
			Node* next = cur->next;//务必先记录下一个节点
			free(cur);			   // 不能使用释放后的内存
			cur = next;
		}
		list->head = NULL;
		list->tail = NULL;
		list->size = 0;
	}
	//free(list);
	return;
}
/*辅助函数打印链表内容*/
void printList(const LinkedList* list) {
	if (!list) {
		printf("链表为空\n");
		return;
	}
	if (list) {
		Node* temp = list->head;
		while (temp) {
			printf("第 %d 个节点的I数据为\t%lf\t,Q数据为:%lf\n", temp->id, temp->i, temp->q);
			temp = temp->next;
		}
	}
	printf("----------------------------------------------------------------------------------------------------------------------\n");
	return;
}

数据库处理部分:DataBaseFunc.h && DataBaseFunc.c

这里主要就是实现了将C语言中一些调用数据库API的封装,使得使用起来更方便

DataBaseFunc.h:
#pragma once
#include <mysql.h>
/*销毁指定数据库*/
void dropDataBase(MYSQL* connect, const char* dataBaseName);
/*切换至指定数据库*/
void changeDataBase(MYSQL* connect, const char* dataBaseName);
/*创建指定名数据库*/
void createDataBase(MYSQL* connect, const char* dataBaseName);
/*创建平均信号强度表*/
void createDataStrengthTable(MYSQL* connect, const char* tableName, const char* dataFileNameField, const char* mounatainStrengthField);
/*创建候选集信息表*/
void createCellsTable(MYSQL* connect, const char* tableName, const char* dataFileNameField, const char* ssOrPssFileNameField, const char* mounatainStrengthField, const char* mountainPositionField);
/*创建小区最终选择结果表*/
void createCellSelectedResultTable(MYSQL* connect, const char* tableName, const char* dataFileNameField, const char* ssOrPssFileNameField, const char* mounatainStrengthField, const char* mountainPositionField, const char* cellSelectedField);
/*初始化MySql并与初始化数据库建立连接*/
void initialMySqlAndConnectDatabase(MYSQL** connect, const char* targetServer, const char* user, const char* password, const char* initialDataBase, unsigned int port, const char* unix_socket, unsigned long clientflag);
/*向候选集信息表插入信息*/
int insertInfoToCellsTable(MYSQL* connect, const char* tableName, const char* dataFileNameField, const char* ssOrPssFileNameField, const char* mounatainStrengthField, const char* mountainPositionField,
	const char* dataFileName, const char* ssOrPssFileName, double mountainStrength, int mountainPosition);
/*向最终结果表插入信息*/
int insertInfoToCellResultTable(MYSQL* connect, const char* tableName, const char* dataFileNameField, const char* ssOrPssFileNameField, const char* mounatainStrengthField, const char* mountainPositionField, const char* cellSelectedFiled,
	const char* dataFileName, const char* ssOrPssFileName, double mountainStrength, int mountainPosition, const char* cellSelectedName);
/*向平均信号强度表插入信息*/
int insertInfoToDataStrengthTable(MYSQL* connect, const char* tableName, const char* dataFileNameField, const char* mounatainStrengthField, const char* dataFileName, double mountainStrength);
/*打印表中信息*/
void printTableInfo(MYSQL* connect, const char* tableName);
/*删除指定表*/
void dropTable(MYSQL* connect, const char* tableName);
DataBaseFunc.c:
#include <stdio.h>
#include"DataBaseFunc.h"
/*初始化数据库并建立连接*/
void initialMySqlAndConnectDatabase(MYSQL** connect, const char* targetServer, const char* user, const char* password, const char* initialDataBase, unsigned int port, const char* unix_socket, unsigned long clientflag) {
	*connect = mysql_init(NULL);
	if (*connect == NULL) {
		printf("初始化失败\t错误信息:%s\n", mysql_error(*connect));
		return;
	}
	if (mysql_real_connect(*connect, targetServer, user, password, initialDataBase, port, unix_socket, clientflag) == NULL) {
		printf("数据库连接失败,\t请检查数据库名是否存在\t错误信息:%s\n", mysql_error(*connect));
		mysql_close(*connect);
		return;
	} else {
		printf("当前服务器为:\t\%s\n", targetServer);
		printf("当前数据库名:\t%s\n", initialDataBase);
		printf("当前用户:\t%s\n", user);
		printf("当前接口为:\t%u\n", port);
	}	//设置中文适配字符集
	if (mysql_set_character_set(*connect, "utf8mb4") != 0) {
		printf("字符集设置失败,请注意不要使用中文即可,当前链接无问题\n");
	}
}

/*创建指定名字的数据库*/
void createDataBase(MYSQL* connect, const char* dataBaseName) {
	if (connect == NULL || dataBaseName == NULL) {
		printf("数据库名无效或数据库连接建立不正确\n");
		return;
	} else {
		int actionSize = snprintf(NULL, 0, "CREATE DATABASE `%s`", dataBaseName);
		char* action = (char*)malloc(actionSize + 1);
		if (action == NULL) {
			printf("内存分配失败\n");
			return;
		}
		snprintf(action, actionSize + 1, "CREATE DATABASE `%s`", dataBaseName);
		if (mysql_query(connect, action) != 0) {
			printf("创建数据库失败,请检查数据库名是否合理,请不要使用中文数据库名\t错误信息:%s\n", mysql_error(connect));
			free(action);
			return;
		} else {
			printf("创建数据库成功,新建数据库为:%s\t\n", dataBaseName);
			free(action);
			return;
		}
	}
}

/*切换指定名字的数据库*/
void changeDataBase(MYSQL* connect, const char* dataBaseName) {
	if (connect == NULL || dataBaseName == NULL) {
		printf("数据库名无效或数据库连接建立不正确\n");
		return;
	} else {
		int actionSize = snprintf(NULL, 0, "USE `%s`", dataBaseName);
		char* action = (char*)malloc(actionSize + 1);
		if (action == NULL) {
			printf("内存分配失败\n");
			return;
		}
		snprintf(action, actionSize + 1, "USE `%s`", dataBaseName);
		if (mysql_query(connect, action) != 0) {
			printf("切换数据库失败,请检查数据库是否存在\t错误信息:%s\n", mysql_error(connect));
			free(action);
			return;
		} else {
			printf("切换数据库成功,当前数据库为:%s\t\n", dataBaseName);
			free(action);
			return;
		}
	}
}

/*删除指定数据库*/
void dropDataBase(MYSQL* connect, const char* dataBaseName) {
	if (connect == NULL || dataBaseName == NULL) {
		printf("数据库名无效或数据库连接建立不正确\n");
		return;
	} else {
		int actionSize = snprintf(NULL, 0, "DROP DATABASE `%s`", dataBaseName);
		char* action = (char*)malloc(actionSize + 1);
		if (action == NULL) {
			printf("内存分配失败\n");
			return;
		}
		snprintf(action, actionSize + 1, "DROP DATABASE `%s`", dataBaseName);
		if (mysql_query(connect, action) != 0) {
			printf("删除数据库失败,请检查数据库名是否合理,请不要使用中文数据库名\t错误信息:%s\n", mysql_error(connect));
			free(action);
			return;
		} else {
			printf("删除数据库成功\t\n");
			free(action);
			return;
		}
	}
}

/*创建小区候选集表*/
void createCellsTable(MYSQL* connect, const char* tableName, const char* dataFileNameField, const char* ssOrPssFileNameField, const char* mounatainStrengthField, const char* mountainPositionField) {
	if (connect == NULL || tableName == NULL || dataFileNameField == NULL || ssOrPssFileNameField == NULL || mounatainStrengthField == NULL || mountainPositionField == NULL) {
		printf("数据库连接不正确或输入参数有误\n");
		return;
	} else {
		const char* actionTemplate = "CREATE TABLE `%s`("
			"id INT PRIMARY KEY AUTO_INCREMENT NOT NULL, "
			"`%s` VARCHAR(20) NOT NULL, "
			"`%s` VARCHAR(20) NOT NULL, "
			"`%s` DOUBLE,"
			"`%s` INT"
			")";
		int actionSize = snprintf(NULL, 0, actionTemplate, tableName, dataFileNameField, ssOrPssFileNameField, mounatainStrengthField, mountainPositionField);
		char* action = malloc(actionSize + 1);
		if (action == NULL) {
			printf("内存分配失败\n");
			return;
		}
		snprintf(action, actionSize + 1, actionTemplate, tableName, dataFileNameField, ssOrPssFileNameField, mounatainStrengthField, mountainPositionField);
		if (mysql_query(connect, action) != 0) {
			printf("创建表失败\t错误信息:%s\n", mysql_error(connect));
			free(action);
			return;
		} else {
			printf("%s表创建成功\n\n", tableName);
			printf("当前表头为:\n\n");
			printf("编号\t%-20s %-20s %-20s %-20s\n\n", "原始数据文件", "ssOrPss文件", "峰值强度", "峰值位置");
			printf("id\t%-20s %-20s %-20s %-20s\n\n", dataFileNameField, ssOrPssFileNameField, mounatainStrengthField, mountainPositionField);
			free(action);
			return;
		}
	}
}

/*创建小区选择结果表*/
void createCellSelectedResultTable(MYSQL* connect, const char* tableName, const char* dataFileNameField, const char* ssOrPssFileNameField, const char* mounatainStrengthField, const char* mountainPositionField, const char* cellSelectedField) {
	if (connect == NULL || tableName == NULL || dataFileNameField == NULL || ssOrPssFileNameField == NULL || mounatainStrengthField == NULL || mountainPositionField == NULL) {
		printf("数据库连接不正确或输入参数有误\n");
		return;
	} else {
		const char* actionTemplate = "CREATE TABLE `%s`("
			"id INT PRIMARY KEY AUTO_INCREMENT NOT NULL, "
			"`%s` VARCHAR(255) NOT NULL,"
			"`%s` VARCHAR(255) NOT NULL, "
			"`%s` VARCHAR(255) NOT NULL, "
			"`%s` DOUBLE,"
			"`%s` INT"
			")";
		int actionSize = snprintf(NULL, 0, actionTemplate, tableName, cellSelectedField, dataFileNameField, ssOrPssFileNameField, mounatainStrengthField, mountainPositionField);
		char* action = malloc(actionSize + 1);
		if (action == NULL) {
			printf("内存分配失败\n");
			return;
		}
		snprintf(action, actionSize + 1, actionTemplate, tableName, cellSelectedField, dataFileNameField, ssOrPssFileNameField, mounatainStrengthField, mountainPositionField);
		if (mysql_query(connect, action) != 0) {
			printf("创建表失败\t错误信息:%s", mysql_error(connect));
			free(action);
			return;
		} else {
			printf("%s表创建成功\n\n", tableName);
			printf("当前表头为:\n\n");
			printf("编号\t%-20s %-20s %-20s %-20s %-20s\n\n", "选择的小区", "原始数据文件", "ssOrPss文件", "峰值强度", "峰值位置");
			printf("id\t%-20s %-20s %-20s %-20s %-20s\n\n", cellSelectedField, dataFileNameField, ssOrPssFileNameField, mounatainStrengthField, mountainPositionField);
			free(action);
			return;
		}
	}
}

/*创建原始数据按平均信号强度排序后的表*/
void createDataStrengthTable(MYSQL* connect, const char* tableName, const char* dataFileNameField, const char* avgOfStrengthField) {
	if (connect == NULL || tableName == NULL || dataFileNameField == NULL) {
		printf("数据库连接不正确或输入参数有误\n");
		return;
	} else {
		const char* actionTemplate = "CREATE TABLE `%s`("
			"id INT PRIMARY KEY AUTO_INCREMENT NOT NULL, "
			"`%s` VARCHAR(20) NOT NULL, "
			"`%s` DOUBLE"
			")";
		int actionSize = snprintf(NULL, 0, actionTemplate, tableName, dataFileNameField, avgOfStrengthField);
		char* action = malloc(actionSize + 1);
		if (action == NULL) {
			printf("内存分配失败\n");
			return;
		}
		snprintf(action, actionSize + 1, actionTemplate, tableName, dataFileNameField, avgOfStrengthField);
		if (mysql_query(connect, action) != 0) {
			printf("创建表失败\t错误信息:%s\n", mysql_error(connect));
			free(action);
			return;
		} else {
			printf("%s表创建成功\n\n", tableName);
			printf("当前表头为:\n\n");
			printf("编号\t%-20s %-20s\n\n", "原始数据文件", "平均信号强度");
			printf("id\t%-20s %-20s\n\n", dataFileNameField, avgOfStrengthField);
			free(action);
			return;
		}
	}
}

/*插入一行数据到related_table相关表中*/
int insertInfoToCellsTable(MYSQL* connect, const char* tableName,
	const char* dataFileNameField, const char* ssOrPssFileNameField, const char* mounatainStrengthField, const char* mountainPositionField,
	const char* dataFileName, const char* ssOrPssFileName, double mountainStrength, int mountainPosition) {
	if (connect == NULL || dataFileName == NULL || ssOrPssFileName == NULL) {
		printf("数据库连接失败或插入数据无效或文件名不对\n");
		return -1;
	} else {
		const char* actionTemplate = "INSERT INTO %s (%s,%s,%s,%s) VALUES"
			"('%s','%s',%lf,%d)"
			"";
		int actionSize = snprintf(NULL, 0, actionTemplate, tableName, dataFileNameField, ssOrPssFileNameField, mounatainStrengthField, mountainPositionField,
			dataFileName, ssOrPssFileName, mountainStrength, mountainPosition);
		char* action = malloc(actionSize + 1);
		if (action == NULL) {
			printf("内存申请失败\n");
			return -1;
		}
		snprintf(action, actionSize + 1, actionTemplate, tableName, dataFileNameField, ssOrPssFileNameField, mounatainStrengthField, mountainPositionField,
			dataFileName, ssOrPssFileName, mountainStrength, mountainPosition);
		if (mysql_query(connect, action) != 0) {
			printf("数据库连接失败\t错误信息为:%s\n", mysql_error(connect));
			free(action);
			return -1;
		} else {
			printf("成功插入一条信息\t");
			printf("插入记录为:");
			printf("\t%s\t%s\t%lf\t%d\n", dataFileName, ssOrPssFileName, mountainStrength, mountainPosition);
			free(action);
			return 0;
		}
	}
}

/*向avg_of_strength_Table强度表中插入一组数据*/
int insertInfoToDataStrengthTable(MYSQL* connect, const char* tableName, const char* dataFileNameField, const char* mounatainStrengthField, const char* dataFileName, double mountainStrength) {
	if (connect == NULL || tableName == NULL || dataFileNameField == NULL || mounatainStrengthField == NULL || dataFileName == NULL) {
		printf("数据库连接失败或插入数据无效或文件名不对\n");
		return -1;
	} else {
		const char* actionTemplate = "INSERT INTO %s (%s,%s) VALUES"
			"('%s',%lf)"
			"";
		int actionSize = snprintf(NULL, 0, actionTemplate, tableName, dataFileNameField, mounatainStrengthField, dataFileName, mountainStrength);
		char* action = malloc(actionSize + 1);
		if (action == NULL) {
			printf("内存申请失败\n");
			return -1;
		}
		snprintf(action, actionSize + 1, actionTemplate, tableName, dataFileNameField, mounatainStrengthField, dataFileName, mountainStrength);
		if (mysql_query(connect, action) != 0) {
			printf("数据库连接失败\t错误信息为:%s\n", mysql_error(connect));
			free(action);
			return -1;
		} else {
			printf("成功插入一条信息\t");
			printf("插入记录为:");
			printf("\t%s\t%lf\n", dataFileName, mountainStrength);
			free(action);
			return 0;
		}
	}
}

/*将小区选择结果插入cells_selected_table表中*/
int insertInfoToCellResultTable(MYSQL* connect, const char* tableName,
	const char* dataFileNameField, const char* ssOrPssFileNameField, const char* mounatainStrengthField, const char* mountainPositionField, const char* cellSelectedFiled,
	const char* dataFileName, const char* ssOrPssFileName, double mountainStrength, int mountainPosition, const char* cellSelectedName) {
	if (connect == NULL || dataFileName == NULL || ssOrPssFileName == NULL) {
		printf("数据库连接失败或插入数据无效或文件名不对\n");
		return -1;
	} else {
		const char* actionTemplate = "INSERT INTO %s (%s,%s,%s,%s,%s) VALUES"
			"('%s','%s','%s',%lf,%d)"
			"";
		int actionSize = snprintf(NULL, 0, actionTemplate, tableName, cellSelectedFiled, dataFileNameField, ssOrPssFileNameField, mounatainStrengthField, mountainPositionField,
			cellSelectedName, dataFileName, ssOrPssFileName, mountainStrength, mountainPosition);
		char* action = malloc(actionSize + 1);
		if (action == NULL) {
			printf("内存申请失败\n");
			return -1;
		}
		snprintf(action, actionSize + 1, actionTemplate, tableName, cellSelectedFiled, dataFileNameField, ssOrPssFileNameField, mounatainStrengthField, mountainPositionField,
			cellSelectedName, dataFileName, ssOrPssFileName, mountainStrength, mountainPosition);
		if (mysql_query(connect, action) != 0) {
			printf("数据库连接失败\t错误信息为:%s\n", mysql_error(connect));
			free(action);
			return -1;
		} else {
			printf("成功插入一条信息\t");
			printf("插入记录为:");
			printf("%-20s\t%s\t%s\t%lf\t%d\n", cellSelectedName, dataFileName, ssOrPssFileName, mountainStrength, mountainPosition);
			free(action);
			return 0;
		}
	}
}

/*打印指定名的表内容*/
void printTableInfo(MYSQL* connect, const char* tableName) {
	if (connect == NULL || tableName == NULL) {
		printf("数据库连接失败或输出数据名无效\n");
		return;
	}
	int actionSize = snprintf(NULL, 0, "SELECT * FROM `%s`", tableName);
	char* action = malloc(actionSize + 1);
	if (action == NULL) {
		printf("初始化内存失败\n");
		return;
	}
	snprintf(action, actionSize + 1, "SELECT * FROM `%s`", tableName);
	if (mysql_query(connect, action) != 0) {
		printf("数据库操作失败\t错误信息%s\n", mysql_error(connect));
		return;
	} else {
		MYSQL_RES* dataOfDataBase = mysql_store_result(connect);//获取数据集指针 指向一整片数据结果
		int fieldCount = mysql_num_fields(dataOfDataBase);      //字段数
		printf("当前表为:%s\n", tableName);
		printf("表头为:\n");
		MYSQL_FIELD* fields = mysql_fetch_fields(dataOfDataBase);//字段结构体数组指针
		for (int i = 0; i < fieldCount; i++) {
			printf("%-5s\t\t", fields[i].name);
		}
		printf("\n");
		MYSQL_ROW everyRow;
		while ((everyRow = mysql_fetch_row(dataOfDataBase))) {
			for (int i = 0; i < fieldCount; i++) {
				printf("%-5s\t\t", everyRow[i]);
			}
			printf("\n");
		}
		printf("\n");
		free(action);
		mysql_free_result(dataOfDataBase);
	}

}

/*删除指定表内容*/
void dropTable(MYSQL* connect, const char* tableName) {
	if (connect == NULL || tableName == NULL) {
		printf("数据库连接失败或指定表格名无效\n");
		return;
	}
	int actionSize = snprintf(NULL, 0, "DROP TABLE `%s`", tableName);
	char* action = malloc(actionSize + 1);
	if (action == NULL) {
		printf("内存分配失败\n");
		return;
	}
	snprintf(action, actionSize + 1, "DROP TABLE `%s`", tableName);
	if (mysql_query(connect, action) != 0) {
		printf("数据库连接失败\t错误信息:%s\n", mysql_error(connect));
		return;
	} else {
		printf("删除表:%s成功\n", tableName);
		free(action);
		return;
	}
}

存储选择最优小区部分CellFunc.h和CellFunc.c

CellFunc.h:
#pragma once
typedef struct {
	char* ensureName;	   //确定信号的名字 即SS0 1 2 或PSS0 1 2
	char* actualName;	   //实际信号的名字 即data0 1 2 3 4 5 6
	double singalStrength;//每个组合的信号强度值
	int maxIndex;		   //峰值位置
}Cell;
/*清理 统一释放内存*/
void destroyCells(Cell* cells, int cellsCount);
/*辅助函数 打印小区选择信息*/
void printCells(const Cell* cells, int cellsCount);
/*辅助函数 交换两个结构体*/
void swapCell(Cell* cells, int i, int j);
/*初始化结构体数组*/
void initialCells(Cell* cells, int cellsCount);
/*将获得小区信息按照最大峰数据进行排序*/
void sortCellsBySingalStrength(Cell* cells);
/*设置终端颜色*/
void setColor(int color);
CellFunc.c:
#include "CellFunc.h"
#include<stdio.h>
#include <Windows.h>
/*初始化Cells*/
void initialCells(Cell* cells,int cellsCount) {
	for (int i = 0; i < cellsCount; i++) {
		cells[i].actualName = NULL;
		cells[i].ensureName = NULL;
	}
}
/*释放申请的内存*/
void destroyCells(Cell* cells, int cellsCount) {
	for (int i = 0; i < cellsCount; i++) {
		if (cells[i].actualName) {
			free(cells[i].actualName);
		}
		if (cells[i].ensureName) {
			free(cells[i].ensureName);
		}
	}
	//free(cells);
}
/*辅助函数 打印*/
void printCells(const Cell* cells,int cellsCount) {
	for (int i = 0; i < cellsCount; i++) {
		setColor(10); // 绿色
		printf("当前实际数据文件(即data文件):%s\t", cells[i].actualName);
		printf("当前确定信号文件(即ss或pss文件):%s\n", cells[i].ensureName);
		setColor(7);
		setColor(12); // 红色
		printf("当前最大平均信号强度:%lf\t\t", cells[i].singalStrength);
		setColor(7); // 默认
		printf("当前峰值位置为   :%d\n", cells[i].maxIndex);
	}
}
/*将组合进行排序 从小到大*/
void sortCellsBySingalStrength(Cell* cells) {
	for (int i = 8; i >= 0; i--) {
		for (int j = 0; j < i; j++) {
			if (cells[j].singalStrength > cells[j + 1].singalStrength) {
				swapCell(cells, j, j + 1);
			}
		}
	}
}
/*辅助函数 帮助于交换Cell*/
void swapCell(Cell* cells, int i, int j) {
	Cell temp = cells[i];
	cells[i] = cells[j];
	cells[j] = temp;
}
/*设置字体颜色*/
void setColor(int color) {
	HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(hConsole, color);
}

网络通信模块部分:NetCommunicationFunc.h和NetCommunicationFunc.c

NetCommunicationFunc.h:
#pragma once
#include <winsock2.h>
#include <ws2tcpip.h>
#include <Windows.h>
#include <stdio.h>
/*初始化网络库*/
int initialNetBase();
/*接收信息字节长度*/
int receiveInfoLength(const SOCKET* clientSocket);
/*发送信息长度*/
int sendInfoLength(const SOCKET* clientSocket, int length);
/*接收一条信息*/
int receiveOneBarInfo(const SOCKET* clientSocket, char** info);
/*发送一条信息*/
int sendOneBarInfo(const SOCKET* clientSocket, const char* info);
/*创建客户端套接字并与之建立连接*/
int createClientSocketAndConnect(SOCKET* clientSocket, char* serverIP, int port);
/*子线程 用于接受信息*/
DWORD WINAPI receiveInfoThreadFunc(LPVOID param);
/*子线程 用于发送信息*/
DWORD WINAPI sendInfoThreadFunc(LPVOID param);
extern char resultCell[20];// 声明全局变量
NetCommunicationFunc.c:
#include "NetCommunicationFunc.h"
char resultCell[20];
/*创建客户端套接字以及和指定服务器IP和端口建立连接*/
int createClientSocketAndConnect(SOCKET* clientSocket, char* serverIP, int port) {
	//创建套接字
	*clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (*clientSocket == INVALID_SOCKET) {
		printf("客户端套接字创建失败\n");
		return -1;
	}
	//寻服务器地址
	struct sockaddr_in serverAddr;		//创建地址结构体
	serverAddr.sin_family = AF_INET;	//设置网络协议族
	serverAddr.sin_port = htons(port);			//设置端口
	if (inet_pton(AF_INET, serverIP, &serverAddr.sin_addr) <= 0) {//将人能看懂的转换为机器能看懂的
		printf("错误 无效的IP地址\n");
		return -1;
	}
	if (connect(*clientSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
		printf("连接失败\n");
		return -1;
	}
	return 0;
}
/*初始化网络库 就相当于是打开网络 告诉操作系统 嘿 这个程序需要联网*/
int initialNetBase() {
	WSADATA wsaData;
	//MAKEWORD 是一个宏 表示需要使用的版本号 这里表示2.2版本 它会自动合成一个16位的版本号
	int res = WSAStartup(MAKEWORD(2, 2), &wsaData);//返回0 说明初始化成功 返回非0 说明初始化失败 网络打不开
	if (res != 0) {
		printf("网络初始化失败,不能联网\n");
		return -1;
	}
	return 0;
}
/*发送信息长度*/
int sendInfoLength(const SOCKET* clientSocket, int length) {
	if (length == 0) {
		printf("长度为0,无法发送\n");
		return -1;
	}
	int netWorkLength = htonl(length);//将主机序转为网络序
	int totalSent = 0;
	while (totalSent < sizeof(int)) {
		int tempRes = send(*clientSocket, (char*)&netWorkLength + totalSent,
			sizeof(int) - totalSent, 0);
		if (tempRes == SOCKET_ERROR) {
			printf("发送长度信息失败\n");
			return -1;
		}
		totalSent += tempRes;
	}
	printf("长度信息发送成功:[%d] 字节\n\n", length);
	return 0;
}
/*返回值为接收到的信息长度*/
int receiveInfoLength(const SOCKET* clientSocket) {
	int hostLength = 0;
	int totalGet = 0;
	while (totalGet < sizeof(int)) {
		int tempRes = recv(*clientSocket, (char*)&hostLength + totalGet, sizeof(int) - totalGet, 0);
		if (tempRes == SOCKET_ERROR) {
			printf("接收长度信息失败\n");
			return -1;
		}
		totalGet += tempRes;
	}
	hostLength = ntohl(hostLength);//收到的网络序 转回来变成主机序
	printf("成功接收信息长度:[%d] 字节\n\n", hostLength);
	return hostLength;
}
/*接收到一条信息*/
int receiveOneBarInfo(const SOCKET* clientSocket, char** info) {
	if (*clientSocket == INVALID_SOCKET) return-1;
	struct sockaddr_in clientAddr;		//为获取客户端IP服务
	int addrLen = sizeof(clientAddr);
	int clientPort = -1;
	char clientIP[INET_ADDRSTRLEN] = "未知IP";
	//获取IP 端口信息
	if (getpeername(*clientSocket, (struct sockaddr*)&clientAddr, &addrLen) == 0) {
		inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN);
		clientPort = ntohs(clientAddr.sin_port);
	}

	//先计算需要获取的字节长度
	int receiveLength = receiveInfoLength(clientSocket);
	*info = malloc(receiveLength + 1);
	if (*info == NULL) {
		printf("内存分配失败\n");
		return -1;
	}
	int totalRecvLength = 0;
	while (totalRecvLength < receiveLength) {
		int tempRes = recv(*clientSocket, *info + totalRecvLength, receiveLength - totalRecvLength, 0);
		if (tempRes == SOCKET_ERROR) {
			printf("接收信息失败\n");
			free(*info);
			return -1;
		}
		totalRecvLength += tempRes;
	}
	(*info)[receiveLength] = '\0';
	printf("成功收到IP:%s, 端口:%d的信息,信息为「 %s 」\n\n", clientIP, clientPort, *info);
	return 0;
}
/*发送一条信息*/
int sendOneBarInfo(const SOCKET* clientSocket, const char* info) {
	if (*clientSocket == INVALID_SOCKET) return -1;
	if (info == NULL) {
		printf("发送的信息不能为空!");
		return -1;
	}
	sendInfoLength(clientSocket, strlen(info));//先发送一个长度信息
	int totalSent = 0;
	while (totalSent < strlen(info)) {
		int tempRes = send(*clientSocket, info + totalSent, strlen(info) - totalSent, 0);
		if (tempRes == SOCKET_ERROR) {
			printf("发送信息失败\n");
			return -1;
		}
		totalSent += tempRes;
	}
	printf("成功发送一条信息:{ %s }\n\n", info);
	return 0;
}
/*收信息线程函数*/
DWORD WINAPI receiveInfoThreadFunc(LPVOID param) {
	SOCKET sock = (SOCKET)param;
	while (1) {
		char* info = NULL;
		receiveOneBarInfo(&sock, &info);
		free(info);
	}
}
/*发信息线程函数*/
DWORD WINAPI sendInfoThreadFunc(LPVOID param) {
	SOCKET sock = (SOCKET)param;
	printf("这里是[082C]\n");
	sendOneBarInfo(&sock, "082C");
	sendOneBarInfo(&sock, "最终选择的小区组合为");
	sendOneBarInfo(&sock, resultCell);
	while (1) {
		printf("[082C(客户端)]请输入信息:\t\n");
		char message[256];
		fgets(message, sizeof(message), stdin);
		message[strcspn(message, "\n")] = '\0';
		sendOneBarInfo(&sock, message);
	}
}

以上都是客户端的代码,下面是服务器端:Server.c

#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <Windows.h>
typedef struct {
	SOCKET clientSocket;
	char clientName[20];
}ClientsInfo;


int initialNetBase();
int initialServerSocketAndBind(SOCKET* serverSocket, int port);
int listenFunc(const SOCKET* serverSocket);
int receiveInfoLength(const SOCKET* clientSocket);
int sendInfoLength(const SOCKET* clientSocket, int length);
int receiveOneBarInfo(const SOCKET* clientSocket, char** info);
int sendOneBarInfo(const SOCKET* clientSocket, const char* info);

SOCKET acceptFunc(const SOCKET* serverSocket);
DWORD WINAPI receiveClientInfoThreadFunc(LPVOID param);
DWORD WINAPI sendClientInfoThreadFunc(LPVOID param);
DWORD WINAPI acceptClientThreadFunc(LPVOID param);
SOCKET clientSockets[20] = { 0 };
ClientsInfo clientsSocketInfo[20];

int main() {
	printf("0708号服务器正式开服啦!\n");
	//初始化网络库 目的是为了启动网路 联网功能打开
	initialNetBase();
	//初始化一些数据
	int result = 0;
	SOCKET serverSocket;
	//初始化服务器端socket以及绑定IP端口
	initialServerSocketAndBind(&serverSocket, 8082);
	//监听以及接收
	int ret = listenFunc(&serverSocket);
	if (ret != 0) {
		printf("无法监听\n");
		return -1;
	}

	//SOCKET clientSocket = acceptFunc(&serverSocket);
	HANDLE acceptThread = CreateThread(NULL, 0, acceptClientThreadFunc, (LPVOID)serverSocket, 0, NULL);
	//HANDLE broadThread = CreateThread(NULL, 0, broadAllClientsSocket, NULL, 0, NULL);

	//getchar();
	//sendToDesignatedClient("082C");
	int communicationMode = 0; // 0: 多人, 1: 单人
	char currentTarget[20] = ""; // 当前单人模式的目标用户名

	while (1) {
		printf("\n输入m切换多人,s切换单人,或直接输入消息:\n> ");
		char input[256];
		fgets(input, sizeof(input), stdin);
		input[strcspn(input, "\n")] = '\0';
		if (strcmp(input, "m") == 0) {
			communicationMode = 0;
			printf("已切换到【多人模式】,你可以输入信息内容啦\n");
		} else if (strcmp(input, "s") == 0) {
			communicationMode = 1;
			printf("已切换到【单人模式】,请输入你想指定连接的用户,现在已上线的用户有:082C 360B 095A\n> ");
			fgets(currentTarget, sizeof(currentTarget), stdin);
			currentTarget[strcspn(currentTarget, "\n")] = '\0';
			printf("已成功切换至指定用户,当前用户为【%s】\n", currentTarget);
		} else {
			if (communicationMode == 0) {
				// 广播
				for (int i = 0; i < 20; i++) {
					if (clientSockets[i] != 0) {
						sendOneBarInfo(&clientSockets[i], input);
					}
				}
			} else {
				// 单人
				SOCKET clientCurSocket = INVALID_SOCKET;
				for (int i = 0; i < 20; i++) {
					if (strcmp(currentTarget, clientsSocketInfo[i].clientName) == 0) {
						clientCurSocket = clientsSocketInfo[i].clientSocket;
						break;
					}
				}
				if (clientCurSocket != INVALID_SOCKET) {
					sendOneBarInfo(&clientCurSocket, input);
				} else {
					printf("未找到该用户,请重新切换目标用户。\n");
					communicationMode = 0; // 自动切回多人模式或提示重新输入
				}
			}
		}
	}


	WaitForSingleObject(acceptThread, INFINITE);

	#pragma region test
	//printf("*****************************************\n");
	//sendOneBarInfo(&clientSocket, "你好这里是0708号服务器,很高兴为您服务");
	//char* info = NULL;
	//receiveOneBarInfo(&clientSocket, &info);
	//free(info);
	//while (1) {
	//	char* tempInfo = NULL;
	//	receiveOneBarInfo(&clientSocket, &tempInfo);

	//	printf("[服务器]请输入文本信息\t");
	//	char message[256];
	//	fgets(message, sizeof(message), stdin);
	//	message[strcspn(message, "\n")] = '\0';
	//	sendOneBarInfo(&clientSocket, message);

	//	free(tempInfo);
	//}
	#pragma endregion

	//关闭连接
	closesocket(serverSocket);
	WSACleanup();
	system("pause");
	return 0;
}




/*初始化网络库 就相当于是打开网络 告诉操作系统 嘿 这个程序需要联网*/
int initialNetBase() {
	WSADATA wsaData;
	//MAKEWORD 是一个宏 表示需要使用的版本号 这里表示2.2版本 它会自动合成一个16位的版本号
	int res = WSAStartup(MAKEWORD(2, 2), &wsaData);//返回0 说明初始化成功 返回非0 说明初始化失败 网络打不开
	if (res != 0) {
		printf("网络初始化失败,不能联网\n");
		return -1;
	}
	return 0;
}
/*接收一个客户端的连接*/
SOCKET acceptFunc(const SOCKET* serverSocket) {
	SOCKET clientSocket = accept(*serverSocket, NULL, NULL);
	if (clientSocket == INVALID_SOCKET) {
		printf("无法与客户端建立连接\n");
		return INVALID_SOCKET;
	}
	return clientSocket;
}
/*默认设置地址格式为IPV4 监听本地所有IP出口*/
int initialServerSocketAndBind(SOCKET* serverSocket, int port) {
	*serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (*serverSocket == INVALID_SOCKET) {
		printf("服务器套接字创建失败\t错误码为%ld\n", WSAGetLastError());
		return WSAGetLastError();
	}
	struct sockaddr_in serverIpAddress;
	serverIpAddress.sin_family = AF_INET;
	serverIpAddress.sin_port = htons(port);//主机序转网络序 16位 端口用
	//类似的还有 htonl 主机序转网络序 32位 地址用
	//		   ntohs 网络序转主机序 16位 端口用
	//		   ntohl 网络序转主机序 32位 地址用
	serverIpAddress.sin_addr.s_addr = INADDR_ANY;
	if (bind(*serverSocket, (struct sockaddr*)&serverIpAddress, sizeof(serverIpAddress)) == SOCKET_ERROR) {
		printf("绑定失败\t错误代码为%ld", WSAGetLastError());
		return WSAGetLastError();
	} else {
		printf("服务器地址为本地环回地址127.0.0.1\n");
		printf("绑定端口成功,当前端口为:%d,可以开始准备监听\n", port);
		return 0;
	}
}
/*监听和接收 注意这里不会关闭服务器的Socket 以及清理网络库 主要是想的自己在外部统一管理*/
int listenFunc(const SOCKET* serverSocket) {
	if (listen(*serverSocket, SOMAXCONN) == SOCKET_ERROR) {
		printf("监听失败\t错误代码%ld\n", WSAGetLastError());
		return WSAGetLastError();
	}
	return 0;
}
/*发送表示即将发送信息的长度*/
int sendInfoLength(const SOCKET* clientSocket, int length) {
	if (length == 0) {
		printf("长度为0,无法发送\n");
		return -1;
	}
	int netWorkLength = htonl(length);//将主机序转为网络序
	int totalSent = 0;
	while (totalSent < sizeof(int)) {
		int tempRes = send(*clientSocket, (char*)&netWorkLength + totalSent,
			sizeof(int) - totalSent, 0);
		if (tempRes == SOCKET_ERROR) {
			printf("发送长度信息失败\n");
			return -1;
		}
		totalSent += tempRes;
	}
	printf("长度信息发送成功:[%d] 字节\n\n", length);
	return 0;
}
/*返回值为接收到的信息长度*/
int receiveInfoLength(const SOCKET* clientSocket) {
	int hostLength = 0;
	int totalGet = 0;
	while (totalGet < sizeof(int)) {
		int tempRes = recv(*clientSocket, (char*)&hostLength + totalGet, sizeof(int) - totalGet, 0);
		if (tempRes == SOCKET_ERROR) {
			printf("接收长度信息失败\n");
			return -1;
		}
		totalGet += tempRes;
	}
	hostLength = ntohl(hostLength);//收到的网络序 转回来变成主机序
	printf("成功接收信息长度:[%d] 字节\n\n", hostLength);
	return hostLength;
}
/*接收一条信息*/
int receiveOneBarInfo(const SOCKET* clientSocket, char** info) {
	struct sockaddr_in clientAddr;		//为获取客户端IP服务
	int addrLen = sizeof(clientAddr);
	int clientPort = -1;
	char clientIP[INET_ADDRSTRLEN] = "未知IP";
	//获取IP 端口信息
	if (getpeername(*clientSocket, (struct sockaddr*)&clientAddr, &addrLen) == 0) {
		inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN);
		clientPort = ntohs(clientAddr.sin_port);
	}

	//先计算需要获取的字节长度
	int receiveLength = receiveInfoLength(clientSocket);
	if (receiveLength == -1) {
		printf("客户端IP:%s, 端口:%d 已断开\n\n", clientIP, clientPort);
		return -1;
	}
	*info = malloc(receiveLength + 1);
	if (*info == NULL) {
		printf("内存分配失败\n");
		return -1;
	}
	int totalRecvLength = 0;
	while (totalRecvLength < receiveLength) {
		int tempRes = recv(*clientSocket, *info + totalRecvLength, receiveLength - totalRecvLength, 0);
		if (tempRes == SOCKET_ERROR) {
			printf("接收信息失败\n");
			free(*info);
			return -1;
		}
		totalRecvLength += tempRes;
	}
	(*info)[receiveLength] = '\0';
	printf("成功收到IP:%s, 端口:%d的信息,信息为「 %s 」\n\n", clientIP, clientPort, *info);
	if (strcmp(*info, "082C") == 0) {
		strcpy(clientsSocketInfo[0].clientName, "082C");
		clientsSocketInfo[0].clientSocket = *clientSocket;
		printf("082C用户已上线\n");
	} else if (strcmp(*info, "360B") == 0) {
		strcpy(clientsSocketInfo[1].clientName, "360B");
		clientsSocketInfo[1].clientSocket = *clientSocket;
		printf("360B用户已上线\n");
	} else if (strcmp(*info, "095A") == 0) {
		strcpy(clientsSocketInfo[2].clientName, "095A");
		clientsSocketInfo[2].clientSocket = *clientSocket;
		printf("095A用户已上线\n");
	}
	return 0;
}
/*发送一条信息*/
int sendOneBarInfo(const SOCKET* clientSocket, const char* info) {
	if (info == NULL) {
		printf("发送的信息不能为空!");
		return -1;
	}
	sendInfoLength(clientSocket, strlen(info));//先发送一个长度信息
	int totalSent = 0;
	while (totalSent < strlen(info)) {
		int tempRes = send(*clientSocket, info + totalSent, strlen(info) - totalSent, 0);
		if (tempRes == SOCKET_ERROR) {
			printf("发送信息失败\n");
			return -1;
		}
		totalSent += tempRes;
	}
	printf("成功发送一条信息:{ %s }\n\n", info);
	return 0;
}
/*接收信息线程函数*/
DWORD WINAPI receiveClientInfoThreadFunc(LPVOID param) {
	SOCKET sock = (SOCKET)param;
	while (1) {
		char* tempInfo = NULL;
		int ret = receiveOneBarInfo(&sock, &tempInfo);
		if (ret == -1) {
			for (int i = 0; i < 20; i++) {
				if (clientSockets[i] == sock) {
					clientSockets[i] = 0;
					closesocket(sock);
					return;
				}
			}
		}
		if (ret != -1)
			free(tempInfo);
	}
}
/*发送信息线程函数*/
DWORD WINAPI sendClientInfoThreadFunc(LPVOID param) {
	SOCKET sock = (SOCKET)param;
	while (1) {
		printf("[服务器]请输入文本信息\t");
		char message[256];
		fgets(message, sizeof(message), stdin);
		message[strcspn(message, "\n")] = '\0';
		sendOneBarInfo(&sock, message);
	}
}
/*接收客户端连接线程函数*/
DWORD WINAPI acceptClientThreadFunc(LPVOID param) {
	int index = 0;
	SOCKET serverSocket = (SOCKET)param;
	while (index < 20) {
		SOCKET clientSocket = acceptFunc(&serverSocket);
		if (clientSocket == INVALID_SOCKET) {
			continue;
		}
		clientSockets[index] = clientSocket;
		HANDLE receiveThread = CreateThread(NULL, 0, receiveClientInfoThreadFunc, (LPVOID)clientSockets[index], 0, NULL);
		//HANDLE sendThread = CreateThread(NULL, 0, sendClientInfoThreadFunc, (LPVOID)clientSockets[index], 0, NULL);
		while (1) {
			int flag = 0;
			for (int i = 0; i < 20; i++) {
				if (clientSockets[i] == 0) {
					index = i;
					flag = 1;
					break;
				}
			}
			if (flag) break;
			Sleep(1000); // 加上这个,CPU占用会大幅下降
		}
	}
}

以上就是全流程代码了,后面我会上传项目压缩包。

部分结果展示:

 

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/m0_74212916/article/details/149564098

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--