有時候會有人來問怎麼用C語言寫程式來跟網頁伺服器溝通,
關於這個問題,其實我也不會,
因為我使用了libcurl來處理麻煩的HTTP通訊協定。
libcurl是一個功能強大的網路通訊程式庫,
支援的通訊協定有FTP,FTPS,HTTP,HTTPS,SCP,SFTP,TFTP,TELNET,
DICT,LDAP,LDAPS,FILE,IMAP,SMTP,POP3,RTMP及RTSP。
詳細的介紹就請自行到它的官網看囉~
再來就說一下怎麼用libcurl來模擬登入Facebook吧。
一、libcurl的編譯方式,請參考編譯各Library。
二、由於表達能力不太好,直接釋出悠閒農夫的使用者登入程式碼。
User.h
#ifndef USER_H #define USER_H #include <wx/string.h> #include <curl/curl.h> class User { public: User(); bool Login(); void SetFBID(wxString &value); void SetLoginName(wxString value); void SetPassword(wxString value); wxString GetFBID(){return wxString(fbid, wxConvUTF8);}; virtual ~User(); protected: private: CURL *handle; CURLcode code; char lsd[128]; char fbid[128]; //FILE *headerfile; wxString _name; char c_name[128]; wxString _pwd; char c_pwd[128]; }; #endif // USER_H
User.cpp
#include "User.h" #include <wx/wx.h> static size_t write_data(char *data, size_t size, size_t nmemb, wxString *writerData) { return nmemb; } void getCookies(CURL *handle, wxString name, char *data) { struct curl_slist *cookies; struct curl_slist *nc; curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &cookies); nc = cookies; int index = wxNOT_FOUND; int len = name.Len() + 2; wxString search; search += _T("\t") + name + _T("\t"); while (nc) { wxString msg(nc->data, wxConvUTF8); index = msg.Find(search); if(index != wxNOT_FOUND) { strcpy(data, (const char*)msg.Mid(index + len, msg.Len() - index - len).mb_str(wxConvUTF8)); break; } nc = nc->next; } curl_slist_free_all(cookies); } User::User() { //ctor //初始化 curl_global_init(CURL_GLOBAL_ALL); //取得handle handle = curl_easy_init(); code = curl_easy_setopt(handle, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-TW; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 GTB6 (.NET CLR 3.5.30729)"); code = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_data); code = curl_easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate"); code = curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); code = curl_easy_setopt(handle, CURLOPT_URL, "http://apps.facebook.com/farmgame_tw/"); code = curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1L); code = curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L); } User::~User() { curl_easy_cleanup(handle); curl_global_cleanup(); } bool isTest = false; bool isLogin = false; void User::SetFBID(wxString &value) { isTest = true; long res_code = 0; char *new_url; strcpy(fbid, value.mb_str(wxConvUTF8)); wxString fname = value; fname.Append(_T(".cks")); code = curl_easy_setopt(handle, CURLOPT_COOKIEFILE, (const char*)fname.mb_str(wxConvUTF8)); /* just to start the cookie engine */ code = curl_easy_setopt(handle, CURLOPT_COOKIEJAR, (const char*)fname.mb_str(wxConvUTF8)); code = curl_easy_perform(handle); curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &res_code); curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &new_url); wxString msg(new_url, wxConvUTF8); isLogin = (res_code == 200 && msg.StartsWith(_T("http://apps.facebook.com/farmgame_tw/"))); if(!isLogin) getCookies(handle, _T("lsd"), lsd); } bool User::Login() { if(isLogin) { return true; } if(!isTest) { code = curl_easy_setopt(handle, CURLOPT_COOKIEFILE, ""); /* just to start the cookie engine */ code = curl_easy_perform(handle); getCookies(handle, _T("lsd"), lsd); } struct curl_httppost *formpost=NULL; struct curl_httppost *lastptr=NULL; //塞Form的值 curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "charset_test", CURLFORM_COPYCONTENTS, "€,´,€,´,水,Д,Є", CURLFORM_END); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "next", CURLFORM_COPYCONTENTS, "http://apps.facebook.com/farmgame_tw/", CURLFORM_END); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "version", CURLFORM_COPYCONTENTS, "1.0", CURLFORM_END); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "api_key", CURLFORM_COPYCONTENTS, "c1f92c58896a86359ac815b2dc7c708a", CURLFORM_END); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "return_session", CURLFORM_COPYCONTENTS, "0", CURLFORM_END); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "session_key_only", CURLFORM_COPYCONTENTS, "0", CURLFORM_END); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "charset_test", CURLFORM_COPYCONTENTS, "€,´,€,´,水,Д,Є", CURLFORM_END); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "lsd", CURLFORM_COPYCONTENTS, lsd, CURLFORM_END); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "email", CURLFORM_COPYCONTENTS, c_name, CURLFORM_END); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "pass", CURLFORM_COPYCONTENTS, c_pwd, CURLFORM_END); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "login", CURLFORM_COPYCONTENTS, "登入", CURLFORM_END); if(handle) { code = curl_easy_setopt(handle, CURLOPT_URL, "https://login.facebook.com/login.php?login_attempt=1&canvas=1"); code = curl_easy_setopt(handle, CURLOPT_HTTPPOST, formpost); curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L); code = curl_easy_perform(handle); curl_formfree(formpost); if(code != CURLE_OK) throw wxString::Format(_T("錯誤:%s"), wxString(curl_easy_strerror(code), wxConvUTF8).c_str()); long res_code = 0; char *new_url; curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &res_code); curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &new_url); wxString msg(new_url, wxConvUTF8); isLogin = (res_code == 200 && msg.StartsWith(_T("http://apps.facebook.com/farmgame_tw/"))); if(isLogin) { getCookies(handle, _T("c_user"), fbid); wxString fname(fbid, wxConvUTF8); fname.Append(_T(".cks")); code = curl_easy_setopt(handle, CURLOPT_COOKIEJAR, (const char*)fname.mb_str(wxConvUTF8)); } else getCookies(handle, _T("lsd"), lsd); //msg.Clear(); //content.Clear(); } return isLogin; } void User::SetPassword(wxString value) { _pwd = value; strcpy(c_pwd, (const char*)value.mb_str(wxConvUTF8)); } void User::SetLoginName(wxString value) { _name = value; strcpy(c_name, (const char*)value.mb_str(wxConvUTF8)); }
大部份的網頁伺服器在判斷是不是合法的網頁使用都是靠cookies,
Facebook也不例外,
因此在登入後必須把cookies記起來,
在後續與Facebook的溝通都必須把cookies傳回去,
而libcurl在這部份已經都處理好了,
完全不用自己控制,
只須要把cookies存成檔案後,
叫libcurl去使用就可以了。
三、登入程式的使用方式
User user; user.SetFBID(_("Facebook編號")); user.SetLoginName(_("登入帳號")); user.SetPassword(_("登入密碼")); user.Login();
- 由於太過頻繁的同帳號登入,
會被Facebook擋掉不給登,
因此在登入成功後,
會產生一個以Facebook編號為檔名的.cks檔案來儲存cookies,
若有這個檔案存在,在登入前請先用SetFBID,
把Facebook編號傳入,
程式會先用cookies去跟facebook溝通,
如果cookies還沒過期,
那自然就不用再透過帳號、密碼的驗證了。
- 把登入帳號及登入密碼傳入
- call ser.Login()做登入的動作,若登入成功會回傳true。
四、使用libcurl的重點
- 使用前必需先呼叫curl_global_init(CURL_GLOBAL_ALL);做初始化的動作。
- 使用curl_easy_init();取得handle
- 如果網頁內容很大,
必需使用curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
讓它的內容有地方輸出,不然會因為buffer不足而出錯。
- 如果libcurl的method所回傳的狀態碼不是CURLE_OK時,
可以用curl_easy_strerror(code)
來取得錯誤訊息
若要知道libcurl還有哪些用法,
可以去看它提供的example及API說明
不過全都是英文的,不要跟我說看不懂,我英文也很差的!!
libcurl 真是好物,雖然我不是寫 C 的,不過有機會讀人家程式這種事我可不會放過,後來在老外的網站找到 libcurl.vb(封裝給 vb6 的版本),玩了一下... libcurl 真是太變態了,就像您說的很多麻煩事都給處理好了,以前自己用 Winsock + 一堆 API 收回來一些火星文,看了就先軟一半...
回覆刪除大大你好,先感謝你無私的分享!
回覆刪除小弟目前想做一個task,就是希望能在embedded system (without browser)上利用curl來做facebook login並且取得個人網頁上朋友的動態訊息,並顯示在embedded system上.
不過在網路上爬了很多文,都沒找到可以在不用browser下,成功login的方式(開發論壇上也說基於安全問題,facebook必須要透過browser login),但我看你開發的悠閒農夫,似乎就沒有透過browser就可以login,讓我看到一線希望.不過我借用你上面c"url的code,執行完curl_easy_perform()後,結果都得到"CURLE_UNSUPPORTED_PROTOCOL"的返回值.無法成功登錄.
因此有些問題想請教你,希望你不吝回答,謝謝~~
1.不曉得只用CURL在沒有browser的情況下,是否真的可以執行login的動作,也就是說,大大覺得我這個task可行性如何?
2.對於user.SetFBID(_("Facebook編號")); 這個function中Facebook編號,應該是要填什麼,是個人的ID,還是指開心農場的ID?
感謝你抽空閱讀,也希望能夠得到你的回應,謝謝~~~
給Hsu
回覆刪除1.可行,不過最好了解什麼是HTTP通訊協定及其運作方式。
2.user.SetFBID的用處請看「登入程式的使用方式」的第一點,Facebook編號不是農場的ID,基本上不填也是可以的。
這個 libcurl 是 ssl 版本嗎?
回覆刪除to sega:
回覆刪除它可以有ssl也可以沒有ssl
主要是看你怎麼編譯它。
現在這個 code 還可以用嗎?
回覆刪除我修改了來用似乎遇到問題
curl_easy_strerror 回傳
Unsupported protocol
殘念
to sega:
回覆刪除你用的protocol是什麼呢?
如果是https,那在編譯libcurl時要把openssl編譯進去才能用唷
不是也..我目前想寫的是http登入facebook
回覆刪除沒寫過 http 的東西
公司要我寫在 online game 裡面
可以打卡 po 留言以及照片到 facebook 的機制
到處找資料
所以找到你這
但似乎沒有很順利
你這麼一提我倒是看到..
回覆刪除你用"https://login.facebook.com/login.php?login_attempt=1&canvas=1"
那表示要用ssl版本的是吧
我來重新安裝一下相關元件
用了有 ssl 的 libcurl 好像成功了...感恩
回覆刪除