面向连接的Socket Server的简单实现(简明易懂)
一、基本原理
有时候我们需要实现一个公共的模块,需要对多个其他的模块提供服务,最常用的方式就是实现一个Socket Server,接受客户的请求,并返回给客户结果。
这经常涉及到如果管理多个连接及如何多线程的提供服务的问题,常用的方式就是连接池和线程池,基本流程如下:
![]()
首先服务器端有一个监听线程,不断监听来自客户端的连接。
当一个客户端连接到监听线程后,便建立了一个新的连接。
监听线程将新建立的连接放入连接池进行管理,然后继续监听新来的连接。
线程池中有多个服务线程,每个线程都监听一个任务队列,一个建立的连接对应一个服务任务,当服务线程发现有新的任务的时候,便用此连接向客户端提供服务。
一个Socket Server所能够提供的连接数可配置,如果超过配置的个数则拒绝新的连接。
当服务线程完成服务的时候,客户端关闭连接,服务线程关闭连接,空闲并等待处理新的任务。
连接池的监控线程清除其中关闭的连接对象,从而可以建立新的连接。
二、对Socket的封装
Socket的调用主要包含以下的步骤:
![]()
调用比较复杂,我们首先区分两类Socket,一类是Listening Socket,一类是Connected Socket.
Listening Socket由MySocketServer负责,一旦accept,则生成一个Connected Socket,又MySocket负责。
MySocket主要实现的方法如下:
|
int MySocket::write(const char * buf, int length) |
|
int MySocket::read(char * buf, int length) return index; |
|
int MySocket::status() FD_ZERO(&checkset); timeout.tv_sec = 10; status = select((int)m_socket + 1, &checkset, 0, 0, &timeout); |
|
int MySocket::close() |
MySocketServer的主要方法实现如下:
|
int MySocketServer::init(int port) struct sockaddr_in serverAddr; if(bind(m_socket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1) if(listen(m_socket, SOMAXCONN) == -1) struct linger lin; setsockopt(m_socket, SOL_SOCKET, SO_LINGER, (const char *)&lin, sizeof(lin)); |
|
MySocket * MySocketServer::accept() |
|
MySocket * MySocketServer::accept(int timeout) fd_set checkset; int status = (int)select((int)(m_socket + 1), &checkset, NULL, NULL, &timeout); if(FD_ISSET(m_socket, &checkset)) |
三、线程池的实现
一个线程池一般有一个任务队列,启动的各个线程从任务队列中竞争任务,得到的线程则进行处理:list<MyTask *> m_taskQueue;
任务队列由锁保护,使得线程安全:pthread_mutex_t m_queueMutex
任务队列需要条件变量来支持生产者消费者模式:pthread_cond_t m_cond
如果任务列表为空,则线程等待,等待中的线程个数为:m_numWaitThreads
需要一个列表来维护线程池中的线程:vector<MyThread *> m_threads
每个线程需要一个线程运行函数:
|
void * __thread_new_proc(void *p) |
每个线程由MyThread类负责,主要函数如下:
|
int MyThread::start() pthread_attr_t attr; int ret = pthread_create(&m_thread, &attr, thread_func, args); if(ret != 0) } |
|
int MyThread::stop() int ret = pthread_kill(m_thread, SIGINT); if(ret != 0) |
|
int MyThread::join() { int ret = pthread_join(m_thread, NULL); if(ret != 0) return –1; } |
|
void MyThread::run() { while (false == m_bStop) { MyTask *pTask = m_threadPool->getNextTask(); if (NULL != pTask) { pTask->process(); } } } |
线程池由MyThreadPool负责,主要函数如下:
|
int MyThreadPool::init() pthread_condattr_t cond_attr; if (ret_val != 0) pthread_mutexattr_t attr; if (ret_val != 0) for (int i = 0; i< m_poolSize; ++i) return 0; |
|
int MyThreadPool::start() ret = pthread_cond_broadcast(&m_cond); if(ret != 0) |
|
void MyThreadPool::addTask(MyTask *ptask) pthread_mutex_lock(&m_queueMutex); m_taskQueue.push_back(ptask); if (m_waitingThreadCount > 0) pthread_mutex_unlock(&m_queueMutex); |
|
MyTask * MyThreadPool::getNextTask() pthread_mutex_lock(&m_queueMutex); while (m_taskQueue.begin() == m_taskQueue.end()) pthread_cond_wait(&n_cond, &m_queueMutex); --m_waitingThreadCount; pTask = m_taskQueue.front(); m_taskQueue.pop_front(); pthread_mutex_unlock(&m_queueMutex); return pTask; |
其中每一个任务的执行由MyTask负责,其主要方法如下:
|
void MyTask::process() { //用read从客户端读取指令 //对指令进行处理 //用write向客户端写入结果 } |
四、连接池的实现
每个连接池保存一个链表保存已经建立的连接:list<MyConnection *> * m_connections
当然这个链表也需要锁来进行多线程保护:pthread_mutex_t m_connectionMutex;
此处一个MyConnection也是一个MyTask,由一个线程来负责。
线程池也作为连接池的成员变量:MyThreadPool * m_threadPool
连接池由类MyConnectionPool负责,其主要函数如下:
|
void MyConnectionPool::addConnection(MyConnection * pConn) pthread_mutex_lock(&m_connectionMutex); m_connections->push_back(pConn); pthread_mutex_unlock(&m_connectionMutex); m_threadPool->addTask(pConn); |
MyConnectionPool也要启动一个背后的线程,来管理这些连接,移除结束的连接和错误的连接。
|
void MyConnectionPool::managePool() pthread_mutex_lock(&m_connectionMutex); for (list<MyConnection *>::iterator itr = m_connections->begin(); itr!=m_connections->end(); ) //处理错误的连接 pthread_mutex_unlock(&m_connectionMutex); } |
五、监听线程的实现
监听线程需要有一个MySocketServer来监听客户端的连接,每当形成一个新的连接,查看是否超过设置的最大连接数,如果超过则关闭连接,如果未超过设置的最大连接数,则形成一个新的MyConnection,将其加入连接池和线程池。
|
MySocketServer *pServer = new MySocketServer(port); MyConnectionPool *pPool = new MyConnectionPool(); while (!stopFlag) { MySocket * sock = pServer->acceptConnection(5); if(sock != null) { if(m_connections.size > maxConnectionSize) { sock.close(); } MyTask *pTask = new MyConnection(); pPool->addConnection(pTask); } } |
面向连接的Socket Server的简单实现(简明易懂)的更多相关文章
- Windows socket之最简单的socket程序
原文:Windows socket之最简单的socket程序 最简单的服务器的socket程序流程如下(面向连接的TCP连接 ): 1. WSAStartup(); 初始化网络库的使用. 2. soc ...
- python socket server源码学习
原文请见:http://www.cnblogs.com/wupeiqi/articles/5040823.html 这里就是自己简单整理一下: #!/usr/bin/env python # -*- ...
- Java Socket Server的演进 (一)
最近在看一些网络服务器的设计, 本文就从起源的角度介绍一下现代网络服务器处理并发连接的思路, 例子就用java提供的API. 1.单线程同步阻塞式服务器及操作系统API 此种是最简单的socket服务 ...
- 面向连接的socket数据处理过程以及非阻塞connect问题
对于面向连接的socket类型(SOCK_STREAM,SOCK_SEQPACKET)在读写数据之前必须建立连接,首先服务器端socket必须在一个客户端知道的地址进行监听,也就是创建socket之后 ...
- socket编程,简单多线程服务端测试程序
socket编程,简单多线程服务端测试程序 前些天重温了MSDN关于socket编程的WSAStartup.WSACleanup.socket.closesocket.bind.listen.acce ...
- python socket 实现的简单http服务器
预备知识: 关于http 协议的基础请参考这里. 关于socket 基础函数请参考这里. 关于python 网络编程基础请参考这里. 一.python socket 实现的简单http服务器 废话 ...
- 用python socket模块实现简单的文件下载
server端: # ftp server端 import socket, os, time server = socket.socket() server.bind(("localhost ...
- python之路 socket、socket server
一.socket socket的英文原义是“孔”或“插座”.作为BSD UNIX的进程通信机制,取后一种意思.通常也 称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可 ...
- Socket编程的简单实现
关于socket编程的简单实现,主要分成客户端.服务端两个部分.实现如下: 1.服务端代码如下,注意:server端要优先于client端启动 2.client端代码,以及启动后客户端和服务端之间的简 ...
随机推荐
- [saiku] 源码整合[maven整合]
saiku源码的整合分为[普通web项目整合]和[maven整合]两种 本节主要是讲解如何整合为maven项目 转载自:http://blog.csdn.net/gsying1474/article/ ...
- python与unicode
Unicode是一种在计算机上使用的字符编码,是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言.跨平台进行文本转换.处理的要求. Uni ...
- linux下如何优雅的挂载一个外界设备(比如优盘)
最近从事linux,实验室一个破服务器,能连上网,但是输入这样的命令: yum -y install gcc yum -y install gcc-c++ ,居然说是没有这样的镜像,也罢 ...
- Merge k Sorted Lists [LeetCode]
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. S ...
- IntelliSense: 应输入声明的解决方案
出现问题的原因暂时没搞清楚,只是找到了解决方案,方案如下: 工具-->选项-->文本编辑器-->C/C++-->高级-->禁用自动更新-->True
- MessageDigest简介
一.概述 MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法.信息摘要是安全的单向哈希函数,它接收任意大小的数据,并输出固定长度的哈希值. MessageDi ...
- 3.1 关系数据库标准语言SQL综述
一.SQL语言的特点 SQL结构查询语言 1.综合统一: 2.高度非过程化:不需要指定存储路径 3.面向集合的操作方式 4.以同一种语法提供两种使用方式:独立语言.嵌入式语言 5.语言简单,易学易用 ...
- 《Pro AngularJS》学习小结-02
上一篇的项目只有一个单独的模板页面,加入了相应的controller,filter,使得页面上的数据能够动态的变化.现在我们开始建立并整合多个模板,加入购物车模块和结账checkout模块. 一.在页 ...
- 使用Matrix控制图像或组件变换的步骤
1.获取Matrix对象,该Matrix对象既可新创建,也可直接获取其他对象内封装的Matrix(例如Transformation对象内部) 2.调用Matrix的方法进行平移.旋转.缩放.倾斜等. ...
- 转: 详解css中的display属性
在一般的CSS布局制作时候,我们常常会用到display对应值有block.none.inline这三个值.下面我们来分别来认识和学习什么时候用什么值.这里通过CSS display知识加实例讲解方法 ...