余長江, 周淵平, 呂海龍
(四川大學 電子信息學院,四川 成都 610065)
Linux作為一種開放源代碼的操作系統,對于軟件未來的發展方向起著一定的引導作用,其主要特點有:源代碼完全開放;具有多用戶特性并支持多進程與多線程;系統安全性可靠;擁有良好的用戶界面;網絡功能強大;支持多平臺;良好的兼容性等等。由于 Linux系統的諸多特性,使其在服務器和個人應用以及企業開發領域中的應用越來越普遍[1],在Linux環境下開發圖形界面也成為了眾多編程者的選擇。
GIMP是一種基于 Linux平臺且能與Photoshop相媲美的圖像處理軟件,而 GTK+最初則是 GIMP的專用開發庫,后來逐漸發展為Linux下開發圖形界面應用程序的主流工具之一,它使用 C語言開發,可以靈活的應用于不同系統,備受開源軟件開發者矚目。GTK+具有的特性如下:動態類型系統;用C語言編寫對象系統;實現了繼承、類型檢驗,以及信號/回調函數的基礎結構;類型和對象系統不特別針對GUI;GTK Widget對象使用對象系統,它定義了GTK+的圖形組件的使用接口;大量的 GTKWidget構件[2]。GTK+2.0的協議 GNU寬通用公共許可證(LGPL,GNU Lesser General Public License),允許任何開發者使用(包括那些開發中的專利軟件)且不收取任何費用。GTK+2.0是當今唯一100%零付費的工業級的圖形界面開發工具 。采用GTK+2.0編程技術設計出一個聊天室,再結合多線程技術,可實現在服務器端和客戶端之間的聊天功能。
本聊天室分為服務器端和客戶端兩部分,采用GTK+2.0,即可用簡短的代碼來編寫窗口并向窗口中插入各個控件,通過靈活地使用信號/回調函數機制,實現通信連接、信息發送、關閉窗口等功能,聊天室如圖1和圖2所示。首先運行服務器端,單擊“開啟”按鈕后服務器端等待客戶端連接,再運行客戶端,在登陸框輸入服務器端IP,單擊“登錄”按鈕即可與服務器端連接,然后雙方就可以進行通信了。

圖1 服務器端聊天室

圖2 客戶端聊天室
聊天過程采用TCP/IP協議下的Client/Server網絡通信模式實現,通過套接字(Socket)接口可方便的實現TCP、UDP傳輸協議完成數據的網絡傳輸[4],對可靠性要求高的數據通訊系統往往使用TCP協議傳輸數據 。Socket是網絡通信的基本構件,可看作網絡通信的一個端點,在網絡通信中使用 Socket函數必須有客戶和服務器2個進程,以實現網絡端點之間的連接和數據交換[6]。在雙方進行通信前,要先運行服務器端程序,等待客戶端的連接。開啟服務器端后主要有以下工作。
(1)建立一個socket通信
調用函數socket(int domain, int type, int protocol)來建立一個套接字,即向系統注冊,通知系統建立一個通信端口。參數 domain 表示所采用的地址類型,此處取值為AF_INET,即ARPA internet地址格式;type為新套接口的類型描述,取值為SOCK_STREAM,表示提供雙向連續且可信賴的數據流,即 TCP;protocol表示 socket所使用傳輸協議的編號,通常取值為0。若成功則返回一個socket描述符。
(2)對 socket定位
調用函數 bind(int sockfd, struct sockaddr *my_addr, int addrlen)將新建的套接字與本地網絡地址聯系起來。sockfd即為調用socket函數后所返回的socket描述符;my_addr為包含本機IP地址和端口號的指針;addrlen為sockaddr的結構長度。
(3)等待客戶端的連接
調用函數listen(int sockfd, int backlog)使socket處于監聽模式。參數backlog表示同時能處理的最大連接數目。listen()并未開始接收連線,只是設置socket為監聽模式,調用accept()成功后才是真正接收客戶端的連線。所以listen()應該在socket(),bind()之后,在accept()之前調用。
(4)接收客戶端socket連線
調用函數accept(int sockfd, struct sockaddr *addr,int *addrlen)來接收客戶端的連線請求。sockfd是被監聽的socket的描述符;連線成功后,addr所指的結構會被填入客戶端主機的地址數據;addrlen是sockaddr的結構長度。若連接成功則返回新的socket處理代碼。
相對服務器端復雜的過程而言,客戶端的工作比較簡單。
運行客戶端程序后,在登錄框輸入服務器端IP地址,該點間隔地址將被函數 inet_addr()轉換成in_addr結構的地址,然后單擊“登陸”按鈕,此時程序首先調用函數socket(),像服務器端那樣建立一個套接字,然后調用函數 connect(int sockfd, struct sockaddr *serv_addr, int addrlen)將客戶端連接至服務器端。sockfd即為新建的客戶端的socket描述符;serv_addr所指向的結構被填入經 inet_addr()轉換后的地址;參數addrlen為sockaddr的結構長度。
客戶端與服務器端連接成功后,雙方即可進行通信,send()和recv()這兩個函數用來在面向連接的socket上進行數據傳輸,其調用方式如下:
1)函數send(int sockfd, const void *msg, int len,int flags)用來發送數據。sockfd是建立好連接的socket描述符;msg指針指向要發送的數據;len是以字節為單位的數據的長度,flags設置為0。
2)函 數 recv(int sockfd, void *buf, int len,unsigned int flags)用來接收數據。sockfd是建立好連接的socket描述符;buf指向存放接收數據的緩沖區;len是緩沖的長度;flags設置為0。
進入聊天程序后,程序要不斷檢測是否有新的信息發送過來,如果只是簡單的采用無限循環這個操作,程序會進入死機狀態,此時就無法進行發送信息等其他的操作了,而使用多線程技術,就可將接收信息的操作置于一個新的線程,從而避免無法發送信息的情況。所謂多線程,就是將一個進程分成多個執行線程,各個線程可以獨立運行。多線程程序采用一種多任務、并發的工作方式,主要優點有:提高應用程序的響應;更有效的使用多處理器;改善程序結構;占用較少的系統資源。
在 main函數中創建多個線程,則該程序運行時,操作系統為每個線程分配不同的CPU時間片,并根據線程優先級進行調度。由于每個時間片的時間很短,雖然實際上同一時刻只有一個線程在運行,但是看上去好像多個線程是并發運行[7]。客戶端程序中的多線程代碼框架如下:


若使用線程,在初始化 GTK+庫函數之前必須運行 g_thread_init(NULL)和 gdk_threads_init()來初始化線程應用。創建線程調用函數g_thread_create(),如上面程序中,g_thread_create()函數用來生成接收消息的線程,get_message()即為線程的具體事件回調函數。當線程例程(即線程執行的代碼)開始時,通過gdk_threads_enter()來獲得一個唯一的全局鎖,當線程例程返回時,通過gdk_threads_leave()釋放該全局鎖。線程創建成功后,新創建的線程開始運行回調函數且不影響原來的線程繼續運行。同樣,服務器端的多線程編程與此類似。
隨著開源編程的迅猛發展,在Linux下開發程序已成為一種潮流,XWindow和 GNU編譯系統已成為Linux上最主要的軟件系統,GTK+作為這兩者相結合的編程開發包更是廣受歡迎。通過對GTK+編程技術的研究,設計了一個簡單的聊天室,采用TCP/IP協議下的Client/Server網絡通信模式,詳細介紹了聊天時雙方通信的建立和信息收發的方法,再結合多線程技術,實現了雙方正常的聊天功能。
[1]初振林.Linux安全強化方案[J].信息安全與通信保密,2007(11):49-50.
[2]張同光,潘紅.Linux中GTK+編程技術研究及其應用[J].軟件導刊, 2007(08):38-40.
[3]宋國偉.GTK+2.0編程范例[M].北京:清華大學出版社,2002.
[4]陳周國.基于Linux QT技術的遠程監控GUI設計[J].通信技術, 2009, 42(12):234-236.
[5]李磊,劉嘉勇.關于對 QQ文件傳輸特點的研究[J].信息安全與通信保密, 2011(04):36-37.
[6]曹玲芝.基于DDE和Socket技術的網絡控制系統仿真平臺[J].通信技術, 2009,42(08):30-33.
[7]金衛民,魯志康.GTK環境下的多線程設計[J].微計算機信息, 2008, 24(07):310-312.