轉自:http://blog.csdn.net/wuyuxing24/article/details/48758927
一, 背景
先說下我要實現的功能,server端一直在linux平台下面跑,當客戶端有請求過來的時候server端接受到請求,拿到客戶端的數據,根據拿到的數據做出相應的處理,得到處理的結果直接把結果數據發送給客戶端。這樣一個連接的請求結束,我的不是長連接的情況,不會一直保持客戶端的連接。來一個處理一個處理完了就結束了。
二,源碼下載(包括客戶端測試代碼)
- 我把邏輯處理部分簡單化了,如果這份代碼對你們有用的話,可以自己實現邏輯處理部分。
- 代碼是要傳入參數的,端口
-
代碼我已經在Linux下面編譯過了,也測試號了,因為代碼中用到了數據庫,如果你沒有編譯過可能要在Linux下裝Mysql數據庫。
三,代碼的簡單介紹
-
socket接受線程:C語言為了高並發所以選擇了epoll。當程序啟動的時候(g_net_update.c文件中main函數,會啟動一個thread見函數create_accept_task)這個thread就處理一件事情,只管接收客戶端的連接,當有連接進來的時候 通過epoll_ctl函數,把socket fd 加入到epoll里面去,epoll設置監聽事件EPOLLIN | EPOLLET; 主要是監聽的是加入到epoll中的socket是否可讀(因為我的需求是客戶端連上了server就會馬上向server發送一份數據的)。其它的部分在主線程中處理。
-
主線程:是一個無線循環,epoll_wait 函數相當於把客戶端的連接從epoll中拿出來(因為我們監聽的是EPOLLIN | EPOLLET)說明這個時候客戶端有數據發送過來)。再通過recv_buffer_from_fd 函數把客戶端發送過來的數據讀出來。然后其他的一切就拋給線程池去處理。
-
線程池:(代碼中我會在池里面創建15個線程) 雙向鏈表。加入線程就是在鏈表后面加一個鏈表項,鏈表的前面會一個一個被拿出來處理。主要是malloc 函數free函數,sem_wait函數sem_post的處理(sem_wait 會阻塞當值大於0是會減一,sem_post是值加一)。typedef void* (FUNC)(void arg, int index);是我們自定義的線程的邏輯處理部分,arg是參數,index是第幾個線程處理(我們隱形的給每個線程都標了號),例如代碼中的respons_stb_info,更加具體可以看看代碼里面是怎么實現的。聰明的你也可以改掉這塊的內容改成動態線程池,當某個時刻的處理比較多的時候能夠動態的增加線程,而不像我代碼里面的是固定的。
-
數據庫連接池:按照我的需求在處理客戶端請求數據的時候是要訪問數據庫的。就是一下子創建出一堆的數據連接。要訪問數據庫的時候先去數據庫連接池中找出空閑的連接,具體可以看下代碼。使用的時候可以參考下database_process.c文件(代碼中數據庫連接池和線程池中的個數是一樣的)。這里我想說下get_db_connect_from_pool這個函數,我用了隨機數,我是為了不想每次都從0開始去判斷哪個連接沒有用到。為了數據庫連接池中的每個鏈接都能等概率的使用到,具體的還是可以看下代碼的實現。
- log文件,代碼中是可以自動保存log信息到文件中去的,具體可以看下代碼。
四,碰到的一些問題和解決辦法
-
最初的時候server程序跑起來占掉了linux 90%多的使用率,因為是我們在create_accept_task 中socket沒有設置成阻塞的。
-
server經常碰到一些莫名其妙的死機,沒辦法用了core dump 去抓死機的堆棧信息看在哪個函數死機的。
-
在處理數據庫的時候有的數據會自動的斷掉(說是說8個小時) 后來采用的辦法是每次都先mysql_ping一次讓他重新連接上。
就說幾點吧,其實還有好多其他的就不說了。
五,在Linux下面用到的幾個命令
-
./server程序名 & //加&后台運行。
-
killall server程序名 // 停掉server的運行。要在server目錄下面執行
-
netstat -antp|grep :端口號 // 查看端口下的socket狀態
-
ps -eaf | grep server程序名 // 檢查程序是否在運行,不過我一般是netstat -antp|grep :端口號 來看程序是否在運行。
好了 就到這里吧,如果你想實現java的高並發可以稍微看下 Linux java + apache mina + maven 實現高並發服務器