把這陣子研究用C語言與網路上的伺服器一問一答的工具和大家分享一下
如果有觀念錯誤或不對的地方請先進前輩們不吝指教,感謝^^!
首先....要能以"網址反查出網址"??這是在說什麼呀?
喔!簡單講就是在程式裏面不能以例如yahoo.com.tw或google.com當成參數來連上網路
所以您必須要有:
main()
{
hostname[]="yahoo.com.tw";
char *addr;
int i;
addr=malloc(128);
memset(addr,'\0',127);
i=lee_hostname2addr(hostname,addr);
printf("********** i=%d addr=%s hostname=%s **********\n",i,addr,hostname);
}
int lee_hostname2addr(char *hostname,char *addrs)
{
struct hostent *hp;
struct in_addr in;
struct sockaddr_in local_addr;
/*printf("hostname=%s \n",hostname);
*/
if(!(hp=gethostbyname(hostname)))
{
fprintf(stderr,"lee_hostname2addr()反查錯誤!hostname參數需先以menset()函數清空的空間\n");
return(-1);
}
memcpy(&local_addr.sin_addr.s_addr,hp->h_addr,sizeof(hp->h_addr));
in.s_addr=local_addr.sin_addr.s_addr;
strcpy(addrs,inet_ntoa(in));
/*printf("IP=%s addrs=%s size=%d ...lee_hostname2addr\n",inet_ntoa(in),addrs,sizeof(hp->h_addr));
*/
return(1);
}
/*接受addr+port字串將之拆成addr及port
如176.38.182.18:8044這樣的一個字串會被拆成176.38.182.18--->以字串型態傳回
及8044--->轉成數字後設定給*po*/
char *lee_addrport(char *host,int *po)
{
char *addr,*port;
int di,sn,i,n;
sn=strlen(host);
di=lee_memchunt(host,":",0,strlen(host),FROM);
addr=calloc(sizeof(char),di+1);
port=calloc(sizeof(char),sn-di);
for(i=0;i<di;i++)
*(addr+i)=*(host+i);
for(i=di+1,n=0;i<sn;i++,n++)
*(port+n)=*(host+i);
//printf("host=%s addr=%s port=%s \n",host,addr,port);
*po=atoi(port);
free(port);
return(&*addr);
}
然後就可以跟他來個一問一答了
首先是問的部份
/*
使用者端傳送資料
*/
//void lee_send(int fd,char *fmap,int mapsz,char *fkw,char *ekw)
void lee_send(int fd,char *sendmem,int sendmemsz)
{
char *tmp;
int i,tmpsz;
/*除錯*/
printf("\n[傳送開始]***********************************************************[傳送開始]\n");
/**/
/*傳去的資料量要減一是字串陣列的末尾'\0'字元否則封包都會多傳一個'\0' */
/* if(write(fd,tmp,tmpsz-1)>0 )
printf("錯誤:傳送失敗\n");
/**/
/* if(send(fd,tmp,tmpsz-1,0) < 0)
printf("錯誤:傳送失敗\n");
/**/
if(write(fd,sendmem,sendmemsz)<=0 )
printf("錯誤:傳送失敗\n");
/*除錯*/
for(i=0;i<sendmemsz;i++)
printf("%c",*(sendmem+i));
printf("[傳送完畢]***** lee_send()... sendmemsz=%d *****[傳送完畢]\n",
sendmemsz);
/**/
// free(tmp);
}
再來當然是接收他的答了,這裡可能稍微複雜一點,因為發回來的資料可能
1.資料有壓縮過
2.資料編碼方式跟您目前系統的不一樣
3.資料的長度到底是多少,該宣告多大的空間來接收
4.目前還沒遇到的其他各種狀況
關於第1點前陣子有討論過了有興趣可以看一下
http://phorum.study-area.org/index.php/topic,61245.0.html這裡貼一下比較"正常狀況下"可以解決第3點問題的代碼
char *lee_recvc(int fd,int *recvsz)
{
char ch[8];
char *tmp;
int i,gi,page;
page=1;
tmp=malloc((1024*page) * sizeof(char));/*宣告空間*/
*recvsz=gi=0;
/*除錯*/
printf("\n[接收開始]***********************************************************[接收開始]\n");
/**/
while(/*recv(fd,ch,1,0)>0*/ read(fd,ch,1)>0 )/*接收資料*/
{
*(tmp+gi)=ch[0];//只複製一個字元時這樣寫應該比strncpy(tmp+gi,ch,1);快很多
gi+=1;
if(gi > page*1024)
{
page+=1;
tmp=realloc(tmp,(1024*page));
}
}
*recvsz=gi;
if(lee_memchunt(tmp,"Could not connect Database",0,gi,FROM) !=-1)
printf("Could not connect Database 對方主機無回應!\n");
/*除錯*/
for(i=0;i<gi;i++) printf("%c",*(tmp+i));
printf("\n[接收完畢]***** lee_recv()...page=%d gi=%d *recvsz=%d *****[接收完畢]\n",
page,gi,*recvsz);
/**/
return(&*tmp);
}
然後是整合問與答兩者的連線函數
char *lee_http(char *haddr,
char *sendtmp,int sendtmpsz,
int *recvsz/*int code*/)
{
struct sockaddr_in address;
int fd,len;
char *addr,*recvtmp;
int hap,port;
if(haddr==NULL)
{
*recvsz=0;
recvtmp=calloc(sizeof(char),1);
return(recvtmp);
}
//printf("lee_http()...haddr=%s[%d] addr=%s port=%d \n",haddr,strlen(haddr),addr,port);
/*addr格式是xx.xx.xx.xx:zzzz 其中的:zzzz是指埠號,以*/
if((hap=lee_memchunt(haddr,":",0,strlen(haddr),FROM)) !=-1)
addr=lee_addrport(haddr,&port);//將addr及埠號分解
else
{
addr=haddr;
port=80;
}
//printf("lee_http()...haddr=%s addr=%s port=%d \n",haddr,addr,port);
/*建立連線*/
fd = socket(AF_INET,SOCK_STREAM, 0);
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(addr);
address.sin_port = htons(/*80*/port);
len = sizeof(address);
if(connect(fd, (struct sockaddr *)&address, len) == -1)
printf("錯誤:連線失敗\n");
/*傳資料給伺服器*/
lee_send(fd,sendtmp,sendtmpsz);
/*接收伺服器的資料*/
// recvtmp=lee_recv(fd,&*recvsz);
recvtmp=lee_recvc(fd,&*recvsz);
printf("\nlee_http()...haddr=%s addr=%s port=%d \n",haddr,addr,port);
close(fd);/*中斷連線*/
if(hap!=-1)
free(addr);
return(recvtmp);
}
等一下!等一下!......這裡說的一問一答到底都問些啥答些啥
其實就是我們以Wireshark所擷取的封包是也,把所擷取的封包照著發一次就好啦!
不過Wireshark擷取的封包必須先處理一下才能拿來用
要不然您也可以學我以前看著螢幕一個字一個字的照著敲然後用strcat()串接起來發出去
不過保證您玩個三天準備看醫生~~~~算了,不囉唆來看看怎麼取用Wireshark擷取的封包吧
首先當然是要有Wireshark啦,什麼!你沒有!!!google一下就有啦^^!
好的,這裡不是Wireshark教學所以自己網路上學一下,其實沒很困難,只要您能從上面看到這裡
相信也是"住巷子裡的"所以抓下來的封包就給他存起來吧
如果您是在win系統存起來的應該是*.pcap的檔案
這時請不要帥氣的左鍵連點兩下點開他
而是以右鍵點一下,然後選[開啟檔案]->[WordPasd]什麼,用Word開啟他....是的....其實用筆記本開就可以了=.=
然後給他另存新檔,
檔案類型保持(或選擇) 文字文件 存起來的檔案就大概長這樣子
??\##P?�J??P#��###U#�K��##?##?###??###?{?d## ?#!E##�jY@#@##|o�#
??\##P}??�Z�P#��?##GET /images/leelib/xyzxxx.gif HTTP/1.1
Accept: */*
Accept-Language: zh-tw
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; GTB6.4; InfoPath.1)
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
U#�K v #########?{#??#�d## ##!E##��/@#?��??\o�#
#P#?�Z�}?�P##��##HTTP/1.1 304 Not Modified
Date: Thu, 20 May 2010 10:56:19 GMT
Server: Apache/2.2.3 (Unix) PHP/5.2.5
Connection: close
(以上封包有略為修改)
然後把GET部份的封包用lee_http()發出去就能得到HTTP/1.1 304 Not Modified這個相同的回應了嗎?
基本上是這樣沒錯的,不過其中有個"眉角"
就是您必須在每一行資料列後面跟著一個"\r\n"這樣的換列字元
不過這個"\r\n"您直接敲進去又不行,也就是說您把他改成這樣子
GET /images/leelib/xyzxxx.gif HTTP/1.1\r\n
Accept: */*\r\n
Accept-Language: zh-tw\r\n
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; GTB6.4; InfoPath.1)\r\n
Accept-Encoding: gzip, deflate\r\n
Connection: Keep-Alive\r\n
\r\n
這肯定是行不通的,因為這個\r\n是"兩個字元",如果直接在文書處理器敲進去的則是4個字元的\r\n
這時如果您手上有vi或另一台電腦是Linux(裏面有vi)那麼將WordPasd存起來的那個檔案利用gmaile傳到Linux電腦上...
(不一定要靠gmaile啦,不過我都是用gmaile傳的,因為.......他免費=..=")
然後如果有轉碼問題(像我ubuntu附的文字編輯器無法自動辨識)則可以用OpenOffice開啟(開的時候會自動轉碼)
然後給他存下去,當然以保留目前格式方式存
再來以vi 打開這個檔案時您會發現在每一列的後面有個奇怪符號像這個樣子
GET /images/leelib/xyzxxx.gif HTTP/1.1^M
Accept: */*^M
Accept-Language: zh-tw^M
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; GTB6.4; InfoPath.1)^M
Accept-Encoding: gzip, deflate^M
Connection: Keep-Alive^M
^M
是的!你得到他了
不過.....這一個^M你就算自己敲上去也沒用
因為他的顏色會明顯與您自己敲進去的字元的顏色不一樣(但這個字元在vi內是可以複製的)

這個\r\n還有第2種方法可以取得,前面有說過用strcat()函數串接吧
strcat(tmp,"GET /images/leelib/xyzxxx.gif HTTP/1.1\r\n");
strcat(tmp,"Accept: */*\r\n");
.....
.....
這樣子的方式其實也是可以的,不過如果我們如果可以把每一個GET或POST動作存在檔案裏面變成其中的一個步驟而已
那麼........事情應該會變得簡單多了
當然,讓事情變"簡單"以前,肯定是有點小複雜的,這就是我們要研究的地方啦.....待續