在Windows环境下进行套接字编程时,经常会遇到一些挑战。幸运的是,有一些类库可以帮助简化这个过程。本文将介绍如何使用这些类库来构建一个简单的TCP服务器。
TCP服务器通过调用listen
函数来监听套接字。当客户端连接到服务器时,accept
函数会返回一个新的套接字,服务器可以通过这个新创建的套接字与客户端通信。一个表现良好的服务器可能会继续以某种方式服务原始套接字。如果不是这样,其他尝试连接的客户端将不会得到及时的服务。一种流行的架构(虽然不是唯一的)是使用一个线程来监听主套接字,并分派其他线程来处理与每个客户端的通信。tcpserver
类就使用了这种架构。
以下是tcpserver
类声明的开始部分:
class tcpserver : public sock, public thread {
public:
tcpserver(unsigned int max_conn = 0, DWORD idle_time = INFINITE, const char *name = 0);
~tcpserver();
...
};
这个类继承自sock
,C++套接字包装器,以及thread
,一个封装Windows线程的类。由于它继承自sock
,可以使用所有可用的函数来控制这个套接字的行为。特别是,可能需要将它绑定到一个接口和端口号。
本文附带的代码中包含了一个小型示例,实现了一个回显服务器。这个服务器简单地等待客户端发送的行,并将它们连同新行一起回显。以下是如何构建回显服务器的示例代码:
tcpserver srv;
srv.bind(inaddr(INADDR_LOOPBACK, 12321));
srv.start();
要了解客户端连接到主服务器线程监听的端口时发生了什么,需要查看tcpserv
实现(在文件tcpserver.cpp
中)。每个thread
对象,tcpserver
是一个线程,都有一个run()
函数,它完成了大部分工作。
这个类是一系列封装基本Windows同步对象的包装器的一部分。知道现在有很多同步对象(包括线程)作为标准C++库的一部分。在写包装器的时候,还没有这些便利的工具。即使在今天,包装器仍然提供了紧密模仿Windows API函数的优势。所有这些类都继承自一个抽象基类syncbase
,它封装了一个Windows句柄,无论是线程句柄、事件句柄、信号量、互斥锁等。