有时候端口被不知道什么程序占用了,找不到很烦人,每次还要查询命令找进程。之前用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:WINDOWSsystem32>cd C:UserszhanglianzhuDesktopwhoportoutbuildx64-Debug C:UserszhanglianzhuDesktopwhoportoutbuildx64-Debug>whoport.exe -k 8082 8082占用端口进程为: iMedicalLIS监听程序.exe 26396 Console 8 72,188 K 准备杀进程 C:UserszhanglianzhuDesktopwhoportoutbuildx64-Debug> C:UserszhanglianzhuDesktopwhoportoutbuildx64-Debug> C:UserszhanglianzhuDesktopwhoportoutbuildx64-Debug> C:UserszhanglianzhuDesktopwhoportoutbuildx64-Debug> C:UserszhanglianzhuDesktopwhoportoutbuildx64-Debug> C:UserszhanglianzhuDesktopwhoportoutbuildx64-Debug> C:UserszhanglianzhuDesktopwhoportoutbuildx64-Debug>whoport.exe -k 8082 没有进程占用:8082端口 C:UserszhanglianzhuDesktopwhoportoutbuildx64-Debug>whoport.exe -k 8082 占用端口进程为: iMedicalLIS监听程序.exe 32700 Console 8 64,096 K 准备杀进程 C:UserszhanglianzhuDesktopwhoportoutbuildx64-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] = ' '; 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] = ' '; } ////// 查找字符位置 /// /// 源串 /// 子串 /// 开始位置 /// 查找格式 ///返回位置 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, "rn"); //遍历每行数据 for (int i = 0; i < len; i++) { char oneLine[1024] = { 0 }; //分割得到对应行数据 dolerp(result, "rn", 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("%sn", "占用端口进程为:"); printf("%sn", 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("%sn", "占用端口进程为:"); printf("%sn", 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("%sn", "查看端口占用并杀死占用程序:"); printf("%sn","whoport -k port"); printf("%sn", "查看端口占用:"); printf("%sn", "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("%sn","准备杀进程"); 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: 如有需要,请添加测试并安装目标。



