第一个C跨平台工具whoport

24 篇文章 0 订阅

有时候端口被不知道什么程序占用了,找不到很烦人,每次还要查询命令找进程。之前用C#实现了找出进程并且杀死的功能。这次用c实现第一个跨平台命令工具–whoport。

实现效果:
1.输入whoport提示输入要查询端口,输入端口后查询端口占用。
2."whoport 端口"直接查询跟的端口占用情况
3.“whoport -k 端口”,查询并杀死占用端口的进程

采用cmake构建工程,耗时一天,主要c操作字符串函数太弱了,测试dolerl,dolerp,dolerf几个方法画很多时间,用c写跨平台功能还是挺麻烦的,DotNetCore就容易多了。

window测试

Microsoft Windows [版本 10.0.19043.1766]
(c) Microsoft Corporation。保留所有权利。

C:\WINDOWS\system32>cd C:\Users\zhanglianzhu\Desktop\whoport\out\build\x64-Debug

C:\Users\zhanglianzhu\Desktop\whoport\out\build\x64-Debug>whoport.exe -k 8082
8082占用端口进程为:

iMedicalLIS监听程序.exe      26396 Console                    8     72,188 K

准备杀进程

C:\Users\zhanglianzhu\Desktop\whoport\out\build\x64-Debug>
C:\Users\zhanglianzhu\Desktop\whoport\out\build\x64-Debug>
C:\Users\zhanglianzhu\Desktop\whoport\out\build\x64-Debug>
C:\Users\zhanglianzhu\Desktop\whoport\out\build\x64-Debug>
C:\Users\zhanglianzhu\Desktop\whoport\out\build\x64-Debug>
C:\Users\zhanglianzhu\Desktop\whoport\out\build\x64-Debug>
C:\Users\zhanglianzhu\Desktop\whoport\out\build\x64-Debug>whoport.exe -k 8082
没有进程占用:8082端口
C:\Users\zhanglianzhu\Desktop\whoport\out\build\x64-Debug>whoport.exe -k 8082
占用端口进程为:

iMedicalLIS监听程序.exe      32700 Console                    8     64,096 K

准备杀进程

C:\Users\zhanglianzhu\Desktop\whoport\out\build\x64-Debug>

linux测试

[root@zlzlinux whoport]# cmake /zlz/whoport/CMakeLists.txt
-- Configuring done
-- Generating done
-- Build files have been written to: /zlz/whoport
[root@zlzlinux whoport]# make
Consolidate compiler generated dependencies of target whoport
[ 33%] Building C object CMakeFiles/whoport.dir/main.c.o
[ 66%] Linking C executable whoport
[100%] Built target whoport
[root@zlzlinux whoport]# ./whoport
请输入要查看的端口:
1444
占用端口进程为:
tcp6 0 0 :::1444 :::* LISTEN 925/dotnet 
[root@zlzlinux whoport]# ./whoport 1444
占用端口进程为:
tcp6 0 0 :::1444 :::* LISTEN 925/dotnet 
[root@zlzlinux whoport]# ./whoport -k 1444
占用端口进程为:
tcp6 0 0 :::1444 :::* LISTEN 925/dotnet 
准备杀进程
[root@zlzlinux whoport]# ./whoport --help
查看端口占用并杀死占用程序:
whoport -k port
查看端口占用:
whoport port

[root@zlzlinux whoport]# 

whoport.h

//查找端口占用和杀死占用端口的程序

//定义宏,防止头文件重复引用
#ifndef __WHOPORT
#define __WHOPORT
#include <stdio.h>
#include <string.h>

//定义是否是Windows
#ifdef _WIN32
#include <windows.h>
//定义是否是windows
#define IsWidows 1
#else
//定义是否是windows
#define IsWidows 0
#endif

/// <summary>
/// 执行命令
/// </summary>
/// <param name="cmd">命令</param>
/// <param name="result">结果</param>
/// <returns>是否成功</returns>
int ExecCmd(char* cmd, char* result);


/// <summary>
/// 按分割串取长度
/// </summary>
/// <param name="source">字符串</param>
/// <param name="split">分割串</param>
/// <returns>分割的长度</returns>
int dolerl(const char* source, char* split);

/// <summary>
/// 按分割串取子串
/// </summary>
/// <param name="source">字符串</param>
/// <param name="split">分割串</param>
/// <param name="index">取位数</param>
/// <param name="retStr">输出字符串</param>
/// <returns>1成功,0失败</returns>
void dolerp(char* source, char* split, int index, char* retStr);

/// <summary>
/// 把多个连续字符合并为一个
/// </summary>
/// <param name="source">源串</param>
/// <param name="oneChar">要合并的字符</param>
/// <param name="retStr">输出串</param>
void MergeCharToOne(char* source, char oneChar, char* retStr);

/// <summary>
/// 查找字符位置
/// </summary>
/// <param name="source">源串</param>
/// <param name="childStr">子串</param>
/// <param name="startIndex">开始位置</param>
/// <param name="findNum">查找格式</param>
/// <returns>返回位置</returns>
int dolerf(char* source, char* childStr, int startIndex, int findNum);

/// <summary>
/// 通过端口找到进程号
/// </summary>
/// <param name="port">端口</param>
/// <param name="retPidArr">进程号字符串数组</param>
/// <returns>返回个数</returns>
int FindPidByPort(const char* port, char** retPidArr);

/// <summary>
/// 按进程号杀进程
/// </summary>
/// <param name="count">数量</param>
/// <param name="retPidArr">进程号字符串数组</param>
int KillPid(int count, char** retPidArr);
#endif

whoport.c

#include "whoport.h"

#ifdef _WIN32
/// <summary>
/// 执行命令
/// </summary>
/// <param name="cmd">命令</param>
/// <param name="result">结果</param>
/// <returns>是否成功</returns>
int ExecCmd(char* cmd, char* result)
{
	SECURITY_ATTRIBUTES sa;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;
	HANDLE h_read, h_write;
	if (!CreatePipe(&h_read, &h_write, &sa, 0))
	{
		return 0;
	}
	STARTUPINFO si = { sizeof(STARTUPINFO) };
	GetStartupInfo(&si);
	si.wShowWindow = SW_HIDE;
	si.hStdError = NULL;
	si.hStdOutput = h_write;
	si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;

	PROCESS_INFORMATION pi;
	if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi))
	{
		return -1;
	}
	CloseHandle(h_write);
	CloseHandle(pi.hThread);
	CloseHandle(pi.hProcess);
	DWORD i = 0, bytes_read;
	while (i < 2048)
	{
		char buffer[1024] = { 0 };
		if (ReadFile(h_read, buffer, 1023, &bytes_read, NULL) == NULL)
		{
			break;
		}
		strcat(result, buffer);
	}
	CloseHandle(h_read);
	return 1;
}
#else
/// <summary>
/// 执行命令
/// </summary>
/// <param name="cmd">命令</param>
/// <param name="result">结果</param>
/// <returns>是否成功</returns>
int ExecCmd(const char* cmd, char* result)
{
	FILE* pipe = popen(cmd, "r");
	if (!pipe)
	{
		return 0;
	}
	char buffer[128] = { 0 };
	while (!feof(pipe))
	{
		if (fgets(buffer, 128, pipe))
			strcat(result, buffer);
	}
	pclose(pipe);
	return 1;
}
#endif

/// <summary>
/// 按分割串取长度
/// </summary>
/// <param name="source">字符串</param>
/// <param name="split">分割串</param>
/// <returns>分割的长度</returns>
int dolerl(const char* source, char* split)
{
	int ret = 1;
	//源串为空返回空
	if (source == "")
	{
		return ret;
	}
	//分割串为空返回空
	if (split == "")
	{
		return ret;
	}
	int sourceLen = strlen(source);
	//源串为空返回空
	if (sourceLen == 0)
	{
		return ret;
	}
	int spLen = strlen(split);
	//分割串为空返回空
	if (spLen == 0)
	{
		return ret;
	}
	int curIndex = 0;
	int startIndex = 0;
	int endIndex = 0;
	//遍历找到按分割串分割的指定位数的串
	for (int i = 0; i < sourceLen; i++)
	{
		//当前位置大于等于分割串长度,且当前字符等于分割串最后一位就比前面字符
		if (i >= spLen - 1 && source[i] == split[spLen - 1])
		{
			//时候和分割串相同
			int isCommon = 1;
			for (int j = 1; j < spLen; j++)
			{
				if (source[i - j] != split[spLen - 1 - j])
				{
					isCommon = 0;
					break;
				}
			}
			if (isCommon == 1)
			{
				ret++;
				i += spLen - 1;
			}

		}
	}
	return ret;
}

/// <summary>
/// 按分割串取子串
/// </summary>
/// <param name="source">字符串</param>
/// <param name="split">分割串</param>
/// <param name="index">取位数</param>
/// <param name="retStr">输出字符串</param>
/// <returns>1成功,0失败</returns>
void dolerp(char* source, char* split, int index, char* retStr)
{
	//源串为空返回空
	if (source == "")
	{
		return;
	}
	//分割串为空返回空
	if (split == "")
	{
		return;
	}
	//位置不合格返回空
	if (index < 0)
	{
		return;
	}
	int sourceLen = strlen(source);
	//源串为空返回空
	if (sourceLen == 0)
	{
		return;
	}
	int spLen = strlen(split);
	//分割串为空返回空
	if (spLen == 0)
	{
		return;
	}
	int curIndex = 0;
	int startIndex = 0;
	int endIndex = 0;
	//遍历找到按分割串分割的指定位数的串
	for (int i = 0; i < sourceLen; i++)
	{
		//当前位置大于等于分割串长度,且当前字符等于分割串最后一位就比前面字符
		if (i >= spLen - 1 && source[i] == split[spLen - 1])
		{
			//是否和分割串相同
			int isCommon = 1;
			for (int j = 0; j < spLen; j++)
			{
				if (source[i - j] != split[spLen - 1 - j])
				{
					isCommon = 0;
					break;
				}
			}
			if (isCommon == 1)
			{
				curIndex++;
				if (curIndex == index + 1)
				{
					endIndex = i - spLen;
					break;
				}
				if (curIndex == index)
				{
					startIndex = i + 1;
				}
				i += spLen - 1;
			}

		}
	}
	//不包含子串返回源串
	if (curIndex == -1 && index == 0)
	{
		strcpy(retStr, source);
		return;
	}
	//没有取的位数返回空
	else if (curIndex < index)
	{
		retStr = "";
		return;
	}
	else
	{
		if (startIndex - endIndex == 1)
		{
			retStr = "";
			return;
		}
		if (startIndex > endIndex)
		{
			endIndex = sourceLen - 1;
		}
		int oindex = 0;
		for (int k = startIndex; k <= endIndex; k++)
		{
			retStr[oindex] = source[k];
			oindex++;
		}
		retStr[oindex] = '\0';
		return;
	}
}

/// <summary>
/// 把多个连续字符合并为一个
/// </summary>
/// <param name="source">源串</param>
/// <param name="oneChar">要合并的字符</param>
/// <param name="retStr">输出串</param>
void MergeCharToOne(char* source, char oneChar, char* retStr)
{
	//源串为空返回空
	if (source == "")
	{
		return;
	}
	//分割串为空返回空
	if (oneChar == "")
	{
		strcpy(retStr, source);
		return;
	}
	int len = strlen(source);
	char preChar;
	int index = 0;
	for (int i = 0; i < len; i++)
	{
		if (i > 0 && source[i] == preChar && source[i] == oneChar)
		{
			continue;
		}
		retStr[index] = source[i];
		index++;
		preChar = source[i];
	}
	retStr[index] = '\0';
}

/// <summary>
/// 查找字符位置
/// </summary>
/// <param name="source">源串</param>
/// <param name="childStr">子串</param>
/// <param name="startIndex">开始位置</param>
/// <param name="findNum">查找格式</param>
/// <returns>返回位置</returns>
int dolerf(char* source, char* childStr, int startIndex, int findNum)
{
	//源串为空返回空
	if (source == "")
	{
		return -1;
	}
	//源串为空返回空
	if (childStr == "")
	{
		return -1;
	}
	if (findNum <= 0)
	{
		return -1;
	}
	int sourceLen = strlen(source);
	int childLen = strlen(childStr);
	if (startIndex >= sourceLen)
	{
		return -1;
	}
	int retIndex = -1;
	int curIndex = 0;
	for (int i = startIndex + childLen; i < sourceLen; i++)
	{
		//当前位置大于等于分割串长度,且当前字符等于分割串最后一位就比前面字符
		if (i >= childLen && source[i] == childStr[childLen - 1])
		{
			//时候和分割串相同
			int isCommon = 1;
			for (int j = 0; j < childLen; j++)
			{
				if (source[i - j] != childStr[childLen - 1 - j])
				{
					isCommon = 0;
					break;
				}
			}
			if (isCommon == 1)
			{
				curIndex++;
				if (curIndex == findNum)
				{
					retIndex = i + 1;
					break;
				}
				i += childLen;
			}

		}
	}
	return retIndex;
}


/// <summary>
/// 通过端口找到进程号
/// </summary>
/// <param name="port">端口</param>
/// <param name="retPidArr">进程号字符串数组</param>
/// <returns>返回个数</returns>
int FindPidByPort(const char* port, char** retPidArr)
{
	int pidNum = 0;
	//windows执行指令
	if (IsWidows == 1)
	{
		char* result[1024 * 10] = { 0 };
		//执行命令行
		int ret = ExecCmd("netstat -ano", result);
		//得到分割长度
		int len = dolerl(result, "\r\n");
		//遍历每行数据
		for (int i = 0; i < len; i++)
		{
			char oneLine[1024] = { 0 };
			//分割得到对应行数据
			dolerp(result, "\r\n", i, oneLine);
			//判断包含端口
			if (dolerf(oneLine, port, 0, 1) > -1)
			{
				char oneLineMerge[1024] = { 0 };
				//合并多个连续空格
				MergeCharToOne(oneLine, ' ', oneLineMerge);
				//得到一行的按空格分割列
				int spLen = dolerl(oneLineMerge, " ");
				//是要的数据
				if (spLen > 5)
				{
					char flag[1024];
					char ipPort[20] = "0.0.0.0:";
					strcat(ipPort, port);
					char ipPort1[20] = "127.0.0.1:";
					strcat(ipPort1, port);
					dolerp(oneLineMerge, " ", 2, flag);
					if (strcmp(flag, ipPort) == 0 || strcmp(flag, ipPort1) == 0)
					{
						//把pid取到返回数组
						dolerp(oneLineMerge, " ", 5, retPidArr[pidNum]);
						pidNum++;
						char getexeNameCmd[50] = "tasklist /NH /FI \"PID eq ";
						strcat(getexeNameCmd, retPidArr[pidNum]);
						strcat(getexeNameCmd, "\"");
						char exeInfo[1024 * 20] = { 0 };
						//执行命令行
						int ret = ExecCmd(getexeNameCmd, exeInfo);
						printf("%s\n", "占用端口进程为:");
						printf("%s\n", exeInfo);
					}

				}
			}

		}
		return pidNum;
	}
	//linux
	else
	{
		char* result[1024 * 100] = { 0 };
		//执行命令行
		int ret = ExecCmd("netstat -tunlp", result);
		//得到分割长度
		int len = dolerl(result, "\n");
		//遍历每行数据
		for (int i = 0; i < len; i++)
		{
			char oneLine[1024] = { 0 };
			//分割得到对应行数据
			dolerp(result, "\n", i, oneLine);
			//判断包含端口
			if (dolerf(oneLine, port, 0, 1) > -1)
			{
				char oneLineMerge[1024] = { 0 };
				//合并多个连续空格
				MergeCharToOne(oneLine, ' ', oneLineMerge);
				//得到一行的按空格分割列
				int spLen = dolerl(oneLineMerge, " ");
				//是要的数据
				if (spLen > 7)
				{
					char flag[1024];
					char ipPort[20] = ":::";
					strcat(ipPort, port);
					char ipPort1[20] = "127.0.0.1:";
					strcat(ipPort1, port);
					dolerp(oneLineMerge, " ", 3, flag);
					if (strcmp(flag, ipPort) == 0 || strcmp(flag, ipPort1) == 0)
					{
						char tmp[100];
						//把pid取到返回数组
						dolerp(oneLineMerge, " ", 6, tmp);
						dolerp(tmp, "/", 0, retPidArr[pidNum]);
						pidNum++;
						printf("%s\n", "占用端口进程为:");
						printf("%s\n", oneLineMerge);
					}

				}
			}

		}
		return pidNum;
	}
}

/// <summary>
/// 按进程号杀进程
/// </summary>
/// <param name="count">数量</param>
/// <param name="retPidArr">进程号字符串数组</param>
int KillPid(int count, char** retPidArr)
{
	//windows执行指令
	if (IsWidows == 1)
	{
		for (int i = 0; i < count; i++)
		{
			char* result[1024] = { 0 };
			char cmd[50] = "taskkill /pid ";
			strcat(cmd, retPidArr[i]);
			strcat(cmd, " -t -f");
			//执行命令行
			int ret = ExecCmd(cmd, result);
			return ret;
		}

	}
	else
	{
		for (int i = 0; i < count; i++)
		{
			char* result[1024] = { 0 };
			char cmd[50] = "kill -9 ";
			strcat(cmd, retPidArr[i]);
			//执行命令行
			int ret = ExecCmd(cmd, result);
			return ret;
		}
	}
	return 0;
}

main.c

#include "whoport.h"

/// <summary>
/// 入口
/// </summary>
/// <param name="count">参数个数</param>
/// <param name="args">参数</param>
/// <returns>返回</returns>
int main(int count, char* args[])
{
	//端口
	char portArr[10];
	//没传参数,提示输入端口
	if (count == 1)
	{
		printf("请输入要查看的端口:\n");
		scanf("%s", &portArr);
	}
	//有两个参数就认为传的端口
	else if (count == 2)
	{
		//帮助
		if (strcmp(args[1], "--help") == 0)
		{
			printf("%s\n", "查看端口占用并杀死占用程序:");
			printf("%s\n","whoport -k port");
			printf("%s\n", "查看端口占用:");
			printf("%s\n", "whoport port");
			return 0;
		}
		strcpy(portArr, args[1]);
	}
	//有三个参数就认为第三个传的端口
	else if (count == 3)
	{
		strcpy(portArr, args[2]);
	}
	//放查找的pdf
	char* pidArr[30];
	for (int i = 0; i < 20; i++)
	{
		char tmp[10];
		pidArr[i] = tmp;
	}
	//查找端口占用
	int num = FindPidByPort(portArr, pidArr);
	//杀进程
	if (count >= 3 && strcmp(args[1], "-k") == 0)
	{
		//有占用的就杀进程
		if (num > 0)
		{
			printf("%s\n","准备杀进程");
			KillPid(num, pidArr);
		}
		else
		{
			
			printf("没有进程占用:%s端口", portArr);
		}
	}
	return 0;
}

CMakeLists.txt

# CMakeList.txt: whoport 的 CMake 项目,在此处包括源代码并定义
# 项目特定的逻辑。
#
cmake_minimum_required (VERSION 3.8)

project ("whoport")

# 将源代码添加到此项目的可执行文件。
add_executable (whoport "whoport.c" "whoport.h" "main.c")

# TODO: 如有需要,请添加测试并安装目标。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小乌鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值