最近爲了學習linux 下的多線程,自己用c++封裝了一個簡易的局域網多線程聊天服務器,期間遇到了一些坑寫到這裏與大家共勉!
主要功能: 封裝了一個名叫pthread_serv的類對每一個客戶端的響應建立一個進程進行信息轉發。
遇到的問題: 在使用linux提供的線程創建函數
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,
(void*)(*start_rtn)(void*),void *arg)
時因爲線程主函數start_rt函數是pthread_serv類中的普通函數,在調用的時候c++ 會隱式的傳入this指針,這樣start_rtn就有了兩個參數,但linux提供的pthread_create函數中start_rtn只能有一個(void*) 參數,這是個很嚴重的問題,我們編譯都過不去…..
針對該問題的解決方法: 很明顯,我們必須幹掉start_rtn中這個可惡的this指針。現在有兩個辦法可以做到這點:
1. 將start_rtn聲明爲該類的友元函數
2. 將start_rtn聲明爲靜態函數
我用了第二種方法,將start_rtn聲明爲靜態函數,這樣再調用
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,
(void*)(*start_rtn)(void*),void *arg)
就沒有問題了,不過舊問題去了,新問題又來了!!! 想哭…。c++ 中靜態函數中只能調用靜態成員,靜態函數。所以start_rtn函數不能調用類裏面的變量&&函數了,我也不能將所有的變量,函數都聲明成靜態的吧。所以我的解決方法是將this指針當做start_rtn的參數。
pthread_create(&id_t,NULL,hander_clnt,this);
再在start_rtn中將參數類型轉化成一個對象指針,用這個對象指針調用一個普通的函數,這個普通的函數就作爲線程主函數使用。
void* pthread_serv::hander_clnt(void *msg)
{
pthread_serv *serv = static_cast<pthread_serv*>(msg);
serv->run_clnt(); //run_clnt()就作爲線程主函數用
}
下面是我的代碼,比較搓,求輕拍..(逃~~)
#ifndef PTHREAD_SERV_H
#define PTHREAD_SERV_H
#include <iostream>
#include <algorithm>
#include <list>
#include <string.h>
#include <stdio.h>
#include <arpa/inet.h> //sockaddr_in
#include <pthread.h>
#include <unistd.h>
#include <sys/socket.h>
class pthread_serv
{
public:
explicit pthread_serv();
inline void error_hander(char *msg);
static void *hander_clnt(void *msg);
void send_msg(char *msg,int len);
void run();
void run_clnt();
pthread_t id_t;
private:
pthread_mutex_t mutex;
int serv_sock;
int clnt_sock;
sockaddr_in serv_addr;
sockaddr_in clnt_addr;
std::list<int> clnt_sock_list;
};
#endif // PTHREAD_SERV_H
#include "pthread_serv.h"
pthread_serv::pthread_serv()
{
serv_sock = socket(PF_INET,SOCK_STREAM,0);
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8888);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(serv_sock,(sockaddr*)&serv_addr,sizeof(serv_addr)) == -1)
error_hander("bind() error!");
if(listen(serv_sock,100) == -1)
error_hander("listen() error!");
}
inline void pthread_serv::error_hander(char *msg)
{
fputs(msg,stdout);
}
void pthread_serv::send_msg(char *msg,int len)
{
std::list<int>::iterator it;
pthread_mutex_lock(&mutex);
for(it=clnt_sock_list.begin(); it!=clnt_sock_list.end(); it++)
write(*it,msg,len);
pthread_mutex_unlock(&mutex);
}
void pthread_serv::run_clnt()
{
int str_len;
char str[1000];
while ((str_len = read(clnt_sock,str,1000)) != 0)
send_msg(str,str_len);
pthread_mutex_lock(&mutex);
clnt_sock_list.remove(clnt_sock);
pthread_mutex_unlock(&mutex);
close(clnt_sock);
}
void* pthread_serv::hander_clnt(void *msg)
{
pthread_serv *serv = static_cast<pthread_serv*>(msg);
serv->run_clnt();
}
void pthread_serv::run()
{
socklen_t clt_sz = sizeof(clnt_addr);
while (true)
{
clnt_sock = accept(serv_sock,(sockaddr*)&clnt_addr,&clt_sz);
fprintf(stdout,"new connect: %s\n",inet_ntoa(clnt_addr.sin_addr));
pthread_create(&id_t,NULL,hander_clnt,this);
pthread_detach(id_t);
pthread_mutex_lock(&mutex);
clnt_sock_list.push_back(clnt_sock);
pthread_mutex_unlock(&mutex);
}
}