有时候端口被不知道什么程序占用了,找不到很烦人,每次还要查询命令找进程。之前用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
#include
//定义是否是Windows
#ifdef _WIN32
#include
//定义是否是windows
#define IsWidows 1
#else
//定义是否是windows
#define IsWidows 0
#endif
///
/// 执行命令
///
/// 命令
/// 结果
/// 是否成功
int ExecCmd(char* cmd, char* result);
///
/// 按分割串取长度
///
/// 字符串
/// 分割串
/// 分割的长度
int dolerl(const char* source, char* split);
///
/// 按分割串取子串
///
/// 字符串
/// 分割串
/// 取位数
/// 输出字符串
/// 1成功,0失败
void dolerp(char* source, char* split, int index, char* retStr);
///
/// 把多个连续字符合并为一个
///
/// 源串
/// 要合并的字符
/// 输出串
void MergeCharToOne(char* source, char oneChar, char* retStr);
///
/// 查找字符位置
///
/// 源串
/// 子串
/// 开始位置
/// 查找格式
/// 返回位置
int dolerf(char* source, char* childStr, int startIndex, int findNum);
///
/// 通过端口找到进程号
///
/// 端口
/// 进程号字符串数组
/// 返回个数
int FindPidByPort(const char* port, char** retPidArr);
///
/// 按进程号杀进程
///
/// 数量
/// 进程号字符串数组
int KillPid(int count, char** retPidArr);
#endif
whoport.c
#include "whoport.h"
#ifdef _WIN32
///
/// 执行命令
///
/// 命令
/// 结果
/// 是否成功
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
///
/// 执行命令
///
/// 命令
/// 结果
/// 是否成功
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
///
/// 按分割串取长度
///
/// 字符串
/// 分割串
/// 分割的长度
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;
}
///
/// 按分割串取子串
///
/// 字符串
/// 分割串
/// 取位数
/// 输出字符串
/// 1成功,0失败
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;
}
}
///
/// 把多个连续字符合并为一个
///
/// 源串
/// 要合并的字符
/// 输出串
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';
}
///
/// 查找字符位置
///
/// 源串
/// 子串
/// 开始位置
/// 查找格式
/// 返回位置
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;
}
///
/// 通过端口找到进程号
///
/// 端口
/// 进程号字符串数组
/// 返回个数
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;
}
}
///
/// 按进程号杀进程
///
/// 数量
/// 进程号字符串数组
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"
///
/// 入口
///
/// 参数个数
/// 参数
/// 返回
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: 如有需要,请添加测试并安装目标。