CSocketListener

CSocketListener

CSocketListener是一个抽象类,作用是创建Server端的socket,完成socket的操作,调用listen()等待client的连接,接收client的数据,并提供了两个发送接口用来发给所有的client或者特定的client

CSocketListener只负责接受和发送,具体数据的处理由下一个名为CFrameworkListener的派生类继实现

设置listener socket

1
2
3
4
5
6
7
8
9
10
CSocketListener::CSocketListener(int port)
{
//Creat mListenerSocket
mListenerSocket.create(CSocket::C_SOCKET_NETWORK);
mListenerSocket.bind(nullptr, port);
mListenerSocket.setBlocking(false);
//Stop main thread or not
//default value is false
mShouldStop = false;
}

主线程

  • 在使用Carlsdk开发server时,需要主动调用此方法,使主线不会退出

  • 在创建listener socket时将其设置为了非阻塞模式,这里使用多路复用IO select,来检测是否有新的连接接入

  • 每当有新的连接接入,使用接受的socket_fd,创建一个CSocketClient对象,并将此对象放到列表中维护

  • 每当创建CSocketClient对象时,向其发送Carlsdk的版本,同时可以让client主动确认已经连接成功

  • onDataAvailable 就是接受数据,如果有数据就处理,由于是非阻塞,所以没有数据就会返回0,所以此函数也承担着检查client和server的连接状态的职责

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//Listener main thread function
void CSocketListener::run()
{
std::list<std::shared_ptr<CSocketClient >>::iterator it;
int socket_fd = -1;

mListenerSocket.listen();

mSelectFds.zeroFds(CSelect::C_FDS_READ | CSelect::C_FDS_ERROR);
mSelectFds.addFd(CSelect::C_FDS_READ, mListenerSocket.getSocketFd());

while(!mShouldStop)
{
/*When the listener fd is readable, it indicates that there is a new connection*/
if (mSelectFds.select(CSelect::C_FDS_READ| CSelect::C_FDS_ERROR) <= 0)
{
printf("ms_socket_listener select error, errno = %d.", errno);
CThread::sleep(1000);
continue;
}

if (mSelectFds.isSetFd(CSelect::C_FDS_READ, mListenerSocket.getSocketFd()))
{
socket_fd = mListenerSocket.accept();

if (socket_fd > 0)
{
std::shared_ptr<CSocketClient > client = std::make_shared<CSocketClient >(socket_fd);
char version[32] = {0};
sprintf(version, "CVersion:%d", mVersion);
client->sendData(version, strlen(version));

mSelectFds.addFd(CSelect::C_FDS_READ| CSelect::C_FDS_ERROR, socket_fd);
mMutex.lock();
mClientLists.push_back(client);
mMutex.unlock();
}
}
/*Whether all clients in client lists is readable or not*/
mMutex.lock();
for (it = mClientLists.begin(); it != mClientLists.end();)
{
socket_fd = (*it)->getSocket()->getSocketFd();

if(mSelectFds.isSetFd(CSelect::C_FDS_READ, socket_fd))
{
mMutex.unlock();
bool data_avaiable = onDataAvailable((*it));
mMutex.lock();
if (!data_avaiable)
{
mSelectFds.removeFd(CSelect::C_FDS_READ | CSelect::C_FDS_ERROR, socket_fd);
(*it) = nullptr;
it = mClientLists.erase(it);
continue;
}
}

//Error handling
if (mSelectFds.isSetFd(CSelect::C_FDS_ERROR, socket_fd))
{
mSelectFds.removeFd(CSelect::C_FDS_READ | CSelect::C_FDS_ERROR, socket_fd);
(*it) = nullptr;
it = mClientLists.erase(it);
continue;
}
it++;
}
mMutex.unlock();
}
}

在数据处理时可以多线程处理,考虑到数据处理函数可以根据不同场景,不同数据结构修改,所以将onDataAvailable写为纯虚函数,数据处理函数由派生类定义,而不是在此处实现onDataAvailable,将数据处理函数定义为纯虚函数,固定死

两个发送接口

不过多赘述,直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*Send to all clients*/
void CSocketListener::sendBroadcast(char *data, int length)
{
std::list<std::shared_ptr<CSocketClient >>::iterator it;
mMutex.lock();
for (it = mClientLists.begin(); it != mClientLists.end(); it++)
{
if ((*it)->sendData(data, length) < 0)
{
/*error*/
}
}
mMutex.unlock();
}

/*Send to the specified client*/
int CSocketListener::sendClient(std::shared_ptr<CSocketClient > client, char *data, int length)
{
int ret = -1;
std::list<std::shared_ptr<CSocketClient >>::iterator it;
mMutex.lock();
for (it = mClientLists.begin(); it != mClientLists.end(); it++)
{
if ((*it).get() == client.get())
{
ret = (*it)->sendData(data, length);
if ( ret < 0)
{
/*error*/
}
break;
}
}
mMutex.unlock();
return ret;
}

其他

为了线程安全,保护CSocketClient对象,对CSocketClient加了锁


CSocketListener
https://carl-5535.github.io/2022/11/15/CarlSDK/CSocketListener/
作者
Carl Chen
发布于
2022年11月15日
许可协议