原文地址:http://tldp.org/LDP/LG/issue74/tougher.html
Linux Socket Programming In C++
内容提要
1. 简介
2. CS 通信概览
3. 简单的CS实现
3.1 Server - 建立监听Socket
3.2 Client - 连接到服务器
3.3 Server - 接受客户端连接测试
3.4 Client 和 Server - 发送和接收数据
4 编译和测试我们的CS
4.1 文件列表
4.2 编译和测试
5. 结论
1. 简介
Socket是进程之间交换数据的机制。这些进程即可以是同一台机器上的,也可以是通过网络连接起来的不同机器。一旦一个Socket连接建立,那么数据就能够双向传输,直到其中一端关闭连接。
由于我过去要在一个进行中的项目中使用Socket,所以我开发并提炼了几个C++类来封装原始Socket API调用。通常,请求数据的应用程序叫做客户端Client,而为请求服务叫做服务器Server。我创建了两个主要的类 ClientSocket 和 ServerSocket,用他们,客户端和服务器端就可以交换数据了。
本文的目标就是教会你如何再你的程序中使用 ClientSocket 和 ServerSocket。我们首先简要探讨一下cs通信,接着我们会编写一个简单且使用着两个类的server和client。
2. CS通信概览
再开始讨论具体代码之前,我们应该简要看一下一个典型的CS连接。如下这张表格给出了这些步骤的大概情况:
Server |
Client |
1. 建立一个监听端口,等待来自客户端的连接 |
|
2. 创建一个客户端端口,并且尝试链接到服务器。 |
|
3. 接受客户端的连接请求。 |
|
4. 发送和接受数据。 |
4. 发送和接受数据。 |
5. 关闭连接 |
5. 关闭连接 |
基本上说,首先,服务器监听一个端口,并且等待来自客户端的连接。之后客户端创建一个,并且尝试连接服务器。接着,服务器接受了来自客户端的连接,并且开始交换数据。一旦所有的数据都已经通过socket连接传输完毕,那么任意一方都可以关闭连接了。
3. 简单的CS实现
现在我们开始讨论一下代码了。那么在接下来的章节里面,我们回逐步建立CS双边代码来执行所有再前文中提到的功能。我们回实现一些典型的功能,例如创建服务器端口监听用的socket,接着创建客户端连接服务器的socket等等。所有这些代码可以在simple_server_main.cpp 和 simple_client_main.cpp 找到。
如果你仅仅想检验看看这些源码是否好用,那么请直接跳到这一节。它列出来项目中所用的所有的文件,并且讨论了如何编译和测试他们。
3.1 Server - 建立监听Socket
我们要坐的第一件事情就是要创建一个简单的服务器,用来监听来自客户端的请求。如下是用来建立一个服务器socket的代码:
列表1 : 创建 server socket ( 部分 simple_server_main.cpp 的代码)
#include "ServerSocket.h"
#include "SocketException.h"
#include <string>
int main ( int argc, int argv[] )
{
try
{
// 创建 server socket
ServerSocket server ( 30000 );
// 其余的代码是接受连接,处理请求等等
}
catch ( SocketException& e )
{
std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
}
return 0;
}
这就是创建一个server socket所需的一切了。ServerSocket 类的构造函数调用了一个必须的socket API来设置监听socket。它隐藏了很多细节,所以对你来说,所有要做的就是创建一个类的实例开始监听本地端口。
注意 try/catch 语句块。ServerSocket 和 ClientSocket 类使用了C++的异常处理。如果一个类方法因为某些原因调用失败,那么它就抛出SocketException 类型的异常,这个异常被定义再SocketException.h中。如果不处理这个异常,就是直接导致程序结束,所以最好处理一下。你可以通过调用SocketException 的 description() 方法来获取错误信息。
3.2 Client – 链接到服务器
CS连接的第二部是客户端的责任的,要尝试去连接server。代码跟刚才服务器的那个有些类似:
列表 2 : 创建一个client socket (simple_client_main.cpp 部分代码)
#include "ClientSocket.h"
#include "SocketException.h"
#include <iostream>
#include <string>
int main ( int argc, int argv[] )
{
try
{
// 创建一个 client socket
ClientSocket client_socket ( "localhost", 30000 );
// 其余的代码是发送请求,解析相应等等。
}
catch ( SocketException& e )
{
std::cout << "Exception was caught:" << e.description() << "\n";
}
return 0;
}
通过创建一个 ClientSocket 类的实例, 你创建了一个linux的socket,并把它链接到主机的port上。类似于 ServerSocket 类,如果构造函数因为某些原因出现异常,那么就要抛出异常。
3.3 Server – 接受客户端连接
下一步的CS连接活动再server端。Server有责任接受来自client的连接请求,并且再两个socket之间打开通信的通道。
我们把这个功能添加到这个简单的server中,就是如下升级的版本:
列表 3 : 接受客户端连接 ( 部分 simple_server_main.cpp )
#include "ServerSocket.h"
#include "SocketException.h"
#include <string>
int main ( int argc, int argv[] )
{
try
{
// 创建socket
ServerSocket server ( 30000 );
while ( true )
{
ServerSocket new_sock;
server.accept ( new_sock );
// 其余代码,读取请求,发送回复等等。
}
}
catch ( SocketException& e )
{
std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
}
return 0;
}
接受一个连接请求,就是简单调用accept 方法。这个方法接受client的连接请求,并且给new_sock 赋予有关连接的信息。下一节我们回看到new_sock 是如何工作的。
3.4 Client 和 Server – 发送和接受数据
建立起连接之后,就开始发送和接受数据。
C++的高级特性之一就是运算符重载,或者简单的说,就是让某个运算符执行特定的功能。在ClientSocket 和 ServerSocket 类中,我重载了 << 和 >> 运算符,所以当使用的时候,他们就从socket里面写入或者读出数据。这里,是进一步更新过的server版本:
列表 4 : 简单server的实现 ( simple_server_main.cpp )
#include "ServerSocket.h"
#include "SocketException.h"
#include <string>
int main ( int argc, int argv[] )
{
try
{
// Create the socket
ServerSocket server ( 30000 );
while ( true )
{
ServerSocket new_sock;
server.accept ( new_sock );
try
{
while ( true )
{
std::string data;
new_sock >> data;
new_sock << data;
}
}
catch ( SocketException& ) {}
}
}
catch ( SocketException& e )
{
std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
}
return 0;
}
new_sock 变量包含所有socket的信息,所以我们可以使用它来于客户端交换数据。"new_sock >> data;" 一行应该被解读为 "从 new_sock读数据,并且把他们放到我们的变量'data'中"。类似的,下一行就是把'data'中的数据通过连接发送回client。
如果你注意,你会发现我们已经创建了一个可以相应的服务器,但是没有任何数据从客户端发送过来。这里我们就来写一个client,它向server发数据,并且打印出server的响应。
列表 5 : 简单客户端的实现 ( simple_client_main.cpp )
#include "ClientSocket.h"
#include "SocketException.h"
#include <iostream>
#include <string>
int main ( int argc, int argv[] )
{
try
{
ClientSocket client_socket ( "localhost", 30000 );
std::string reply;
try
{
client_socket << "Test message.";
client_socket >> reply;
}
catch ( SocketException& e) {}
std::cout << "We received this response from the server:\n\"" << reply << "\"\n";;
}
catch ( SocketException& e )
{
std::cout << "Exception was caught:" << e.description() << "\n";
}
return 0;
}
我们发送字符串是"Test Message." 到服务器,然后从服务器那里读取响应,并且通过标准输出输出。
4. 编译和测试我们的cs
现在我们要看一遍ClientSocket 和 ServerSocket 的基本使用方法,我们可以建立一个项目来测试一下。
4.1 文件列表
如下文件是我们的例子:
多方面的:
Makefile – 项目的makefile文件
Socket.h, Socket.cpp – Socket类,实现的原生的socket API调用。
SocketException.h - SocketException 类
Server:
simple_server_main.cpp – 主文件
ServerSocket.h, ServerSocket.cpp - ServerSocket 类
Client:
simple_client_main.cpp – 主文件
ClientSocket.h, ClientSocket.cpp - ClientSocket
4.2 编译和测试
编译简单。首先吧所有的项目文件保存到一个目录下,之后再你的命令提示符下键入:
prompt$ cd directory_you_just_created
prompt$ make
这样就编译了所有的文件,并且创建了simple_server 和 simple_client 输出文件。要测试着两个输入文件,运行如下命令:
first prompt:
prompt$ ./simple_server
running....
second prompt:
prompt$ ./simple_client
We received this response from the server:
"Test message."
prompt$
之后客户端回发送文件到服务器,读取并且相应,之后把相应输出到标准输出上去。你可以运行任意多次客户端,服务器会相应每个请求。
5. 总结
Socket是进程间交换数据的简单且高校的途径。再这个文章里面,我们已经实现了socket通行,并且编写了一个简单的CS例子。现在你应该能在你的程序里面添加socket通信了,恭喜你!
Rob Tougher
Rob 是一位再纽约市中心区域工作的C++软件工程师。当不能在他最喜爱的平台上工作的时候,你能发现Rob正在沙滩上散步,和他的女友Nicole和他们的宠物狗Halley在一起。
CSDN下载源码:http://download.csdn.net/detail/engrossment/4470879
本文转自百度空间:http://hi.baidu.com/shywyz/blog/item/ffc55890ad1e2f84a977a48f.html
2012-08-02