作者:fbysss
关键字: 实用工具源码 Windows下查看Linux编译信息
一、背景:本人写C程序不多,更不用说Linux下了。偶然一个机会,接了个这样的活,vi我用的还马马虎虎,但程序超过一千行,看起来就有些眼花了。于是只好在VC下编写代码,ftp传到Linux服务器,再用gcc编译,出错了再到VC下修改,再上传,如此这般,颇为费劲。Linux图形界面下安装一个C的开发工具?还是算了,我的Linux是虚拟机,在本机上使用Shell我都用的SecureCRT,就是不想切换来切换去。那有什么方式能更自动化一点呢?似乎没有比较贴心的方案,犹豫再三,花了一个下午写了这个工具,完全满足了自己的要求。在此分享给大家,希望对读者有所帮助。
二、设计思路:在Linux下运行一个守护进程,Windows下提供一个客户端供IDE调用,传递gcc命令行、文件名、文件内容(最开始考虑用ftp,后来一不做二不休,想让一切更简单些,索性把文件内容也通过Socket传过去了。)
三、源代码:
1.Linux下守护程序compileDaemon.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#include <netdb.h>
#include <pthread.h>
#include <sys/stat.h>
/*******************************************************
文件名:compileDaemon.c
功能:Windows下查看Linux下编译信息--Linux下编译守护程序
编写人:fbysss
blog:blog.csdn.net/fbysss
email:[email protected]
********************************************************/
#define SPLIT "###SPLIT###"
#define BUFLEN 1024*128
#define THREAD_NUM 1
pthread_mutex_t sock_mutex;
pthread_cond_t sock_cond;
#define MAXSIZE 512
#define resultFile "compile.log"
/**
所需参数:shellCommand,由client传递
包括gcc +参数 里面包含c源程序和目标文件名
*/
/********处理每个连接*******/
void * dealEachConn(void * arg)
{ char buf[MAXSIZE];
char recvBuf[BUFLEN];
//int filesize;//实际上没有使用。
FILE * fdSrcFile;
char cmdLine[MAXSIZE]={0};
int len;
char * recvBufHead;
char * recvBufP;
char filepath[256];
char compileStr[1024]={0};
struct stat file_stat;
int each_conn_fd=(int) arg;
len = recv(each_conn_fd, recvBuf, BUFLEN, 0);
if (len < 0)
{
perror("nothing recieved.");
return;
}
recvBufHead = recvBuf;
recvBufP = recvBuf;
sprintf(cmdLine,"%s",recvBufP);
recvBufP+=strlen(cmdLine);
recvBufP++;//字符串结束符
recvBufP+=strlen(SPLIT);
//得到文件名
sprintf(filepath,"%s",recvBufP);
printf("filepath :%s/n",filepath);
recvBufP+=strlen(filepath);
recvBufP++;//字符串结束符
recvBufP+=strlen(SPLIT);
//写文件
fdSrcFile = fopen(filepath,"w");
fwrite(recvBufP,len-(recvBufP-recvBufHead)-1,1,fdSrcFile);
fclose(fdSrcFile);
printf("file total %d bytes writed,/n",len-(recvBufP-recvBufHead));
//组装命令行
strcat(cmdLine," 2>&1|tee ");
strcat(cmdLine,resultFile);
printf("commandLine is:%s/n",cmdLine);
system(cmdLine);
FILE * fd = fopen(resultFile,"r");
while(fgets(buf, MAXSIZE, fd)!=NULL)
{
strcat(compileStr,buf);
}
printf("compileStr is:%s/n",compileStr);
int rtValue = send(each_conn_fd, compileStr, strlen(compileStr), 0);
fclose(fd);
close(each_conn_fd);
}
//启动一个线程
void startThreadPro(void * proc,void *arg)
{
/* initialize */
pthread_t* arr_pid = (pthread_t*)malloc(sizeof(pthread_t) * THREAD_NUM);
pthread_mutex_init(&sock_mutex, NULL);
pthread_cond_init(&sock_cond, NULL);
printf("create thread a proc .../n");
pthread_create(&arr_pid, NULL, proc, arg);
}
/**************************主程序*******************************/
int main(int argc, char **argv)
{
int each_conn_fd;
int sockfd;
socklen_t len;
struct sockaddr_in my_addr, their_addr;
unsigned int myport, lisnum;
/*****************************************初始化*****************************************/ if (argv[1])
myport = atoi(argv[1]);
else
myport = 4444;
if (argv[2])
lisnum = atoi(argv[2]);
else
lisnum = 2;
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = PF_INET;
my_addr.sin_port = htons(myport);
if (argv[3])//第三个参数为ip地址,指定绑定到哪个ip地址上。
my_addr.sin_addr.s_addr = inet_addr(argv[3]);
else
my_addr.sin_addr.s_addr = INADDR_ANY;
/*******************************端口绑定bind**************************************************/
if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))
== -1) {
perror("bind");
exit(1);
}
/***********************************侦听*****************************************************/
if (listen(sockfd, lisnum) == -1) {
perror("listen");
exit(1);
}
while (1) {//while 1 //循环侦测客户端连接
len = sizeof(struct sockaddr);
if ((each_conn_fd =
accept(sockfd, (struct sockaddr *) &their_addr,//通过accept能够取得跟服务器连接的客户机地址their_addr,返回new_fd
&len)) == -1) {
if (errno == EINTR){//避免信号对accept的影响
continue;
}
perror("accept");
exit(errno);
} else
{
printf("server: got connection from ip:%s, port: %d, socketfd: %d/n",
inet_ntoa(their_addr.sin_addr),
ntohs(their_addr.sin_port), each_conn_fd);
}
/* 开始处理每个新连接上的数据收发 */
startThreadPro(dealEachConn,(void*)each_conn_fd);
}//end while
printf("start to close main socket.../n");
close(sockfd);
return 0;
}
2.Windows下客户端程序compileClient.c
#include "winsock.h"
#include "stdio.h"
/*******************************************************
文件名:compileClient.c
功能:Windows下查看Linux下编译信息--Windows 客户端
编写人:fbysss
blog:blog.csdn.net/fbysss
email:[email protected]
********************************************************/
#define BUFLEN 1024*128//这里一定要足够大。考虑到能够传送一般的c文件
#define LENGTH128 128
#define SPLIT "###SPLIT###"
#define resultFile "compile.log"
void SocketError(char * call)
{
fprintf(stderr," WinSock Error# function: %s, error code:%ld/n",call,WSAGetLastError());
}
/**
argv[1]:server ip
argv[2]:server port
argv[3]:send command
argv[4]:send file dir
argv[5]:send filename
*/
main(int argc,char ** argv)
{
int index;
int iRes,iPort,iTmp;
char * rt;
SOCKET sockfd;
SOCKADDR_IN sin;
WSADATA wsad;
WORD wVersionReq;
FILE * fp;
FILE * fdSrcFile ;
int sendLen;
char sendBuf[BUFLEN]={0};
char recvBuf[BUFLEN]={0};
char * sdBufHead = sendBuf;
char * sdBufferP = sendBuf;
char filename[LENGTH128];
char buf[LENGTH128]={0};
if(argc<5)
{
fprintf(stderr,"Usage:winLinuxGcc <host> <port> <commandStr> <dir> <filename>/n");
return -1;
}
if(inet_addr(argv[1])==INADDR_NONE)
{
fprintf(stderr,"Wrong ip :/n");
return -1;
}
iPort=0;
iPort=atoi(argv[2]);
sin.sin_addr.s_addr=inet_addr(argv[1]);
sin.sin_family=PF_INET;
sin.sin_port=htons(iPort);
if(iPort<=0)
{
fprintf(stderr,"must specify a number for port/n");
return -1;
}
wVersionReq=MAKEWORD(1,1);
iRes=WSAStartup(wVersionReq,&wsad);
if(iRes!=0)
{
SocketError("WSAStartup()");
return -1;
}
sockfd=socket(PF_INET,SOCK_STREAM,0);
if(sockfd==INVALID_SOCKET)
{
SocketError("socket()");
return -1;
}
iTmp=sizeof(sin);
fprintf(stderr,"WinSock Client Start....../n");
if(connect(sockfd,(LPSOCKADDR)&sin,iTmp)==SOCKET_ERROR)
{
SocketError("connect()");
closesocket(sockfd);
WSACleanup();
return -1;
}
//printf("debug1/n");
strcpy(sdBufferP,argv[3]);//命令行
//写分隔符
sdBufferP+=strlen(argv[3]);
sdBufferP++;//字符串结束符
memcpy(sdBufferP,SPLIT,strlen(SPLIT));
sdBufferP+=strlen(SPLIT);
//写文件名
memcpy(sdBufferP,argv[5],strlen(argv[5]));
sdBufferP+=strlen(argv[5]);
//写分隔符
sdBufferP++;
memcpy(sdBufferP,SPLIT,strlen(SPLIT));
sdBufferP+=strlen(SPLIT);
//写文件内容
strcpy(filename,argv[4]);
strcat(filename,"//");
strcat(filename,argv[5]);
printf("debug:filename is:%s/n",filename);
fdSrcFile = fopen(filename,"r");
if(fdSrcFile<0)
{
perror("open file error:");
}
rt="1";
index = 0;
while(rt!=NULL)
//while(rt!=NULL)
{ memset(buf,0,LENGTH128);
index ++;
rt=fgets(buf, LENGTH128, fdSrcFile);
memcpy(sdBufferP,buf,strlen(buf));
sdBufferP+=strlen(buf);
} sendLen = sdBufferP - sdBufHead;//计算发包长度
iRes=send(sockfd,sendBuf,sendLen,0);
if(iRes==SOCKET_ERROR)
{
SocketError("send()");
closesocket(sockfd);
WSACleanup();
return -1;
} fp =fopen(resultFile,"a+b");
if(fp==NULL)
return -1;
//读取返回的编译信息
iRes=recv(sockfd,recvBuf,BUFLEN,0);
while(iRes!=SOCKET_ERROR&&iRes!=0)
{
printf("Received %d bytes,Data:/n------------------/n%s/n------------------/n",iRes,recvBuf);
fwrite(recvBuf,sizeof(char),iRes,fp);//写日志文件
iRes=recv(sockfd,recvBuf,BUFLEN,0);
}
fclose(fp);
closesocket(sockfd);
WSACleanup();
return 0;
}
四.编译运行守护程序
- 上传compileDaemon到Linux;
- 在Linux shell下,运行gcc -o compileDaemon compileDaemon.c编译程序;
- 再输入./compileDaemon 4444 1,运行成功,进入守护监听状态。
五.客户端与VC集成配置
- 首先建立一个工程,winLinuxGcc,加入文件compileClient.c,build之后,生成winLinuxGcc.exe。
- tools->customize..-tools
,添加Menu Contents,输入
LinuxGCC,command输入winLinuxGcc.exe,Arguments输入<host> <port> "gcc -o ttt $(FileName)$(FileExt)" $(FileDir) $(FileName)$(FileExt) (注意:<host> <port>请根据实际情况进行修改),并选中Use Output Window
- 任意打开一个需要在Linux下编译的程序,点击菜单tools,可以看到增加了一个菜单项“LinuxGcc”,点击该菜单,即可可以看到IDE下方输出窗口中的编译信息。
六、结束语:
- 本程序是针对GCC编写的, 客户端的第三个参数可以根据实际情况进行灵活配置。可以完全把Linux命令行格式放置到客户端,这样,不仅仅是gcc,其他编译器也可以适用(服务端稍作改动即可)。
- 另外,除了VC,Editplus、UtraEdit以及其他可加入扩展命令的IDE同样适用本工具。进一步,不仅仅是C语言,其他语言,同样可以采用本工具来达到类似效果。
- 局限性:本工具的编写主要是出于“背景”所述原因,满足的需求有限,并不能完成真正意义的交互调试,我不过是抛砖引玉,希望这样的尝试,能给大家带来更多启示,如果有人能把gdb的交互调试也在vc下实现了,那必定很酷。