XY

没有任何借口!!!
posts - 213, comments - 193, trackbacks - 3, articles - 1
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

【译】C/S 多线程Socket类

Posted on 2008-04-12 00:26  路缘  阅读(...)  评论(...编辑  收藏
 

Multi-threaded Client/Server Socket Class

C/S 多线程Socket

·         Download ServerSocket demo project - 47.2 KB
原文地址:http://www.codeproject.com/KB/IP/serversocket.aspx

Screenshots

屏幕截图

Note: The demo can be started in Client or Server mode, executed with "/C" (or "/CLIENT") or "/S" (or "/SERVER", which is the default).

说明:示例可以以客户端或服务器模式启动,默认用“/C”(或“/CLIENT”)或“/S(或“/SERVER)执行。

Introduction

介绍

This article is about a client/server multi-threaded socket class. The thread is optional since the developer/designer is still responsible for deciding if he/she needs it. There are other Socket classes here and other places over the Internet, but none of them can provide feedback (event detection) to your application like this one does. It provides you with the following events detection: connection established, connection dropped, connection failed and data reception (including 0 byte packet).

本文内容主要是关于一个C/S的多线程Socket类。线程是可选择的,开发设计人员可根据他们的需求来决定是否使用。在互联网上,也有一些其他的Socket类,但它们都没有为你的应用程序提供反馈(事件探测),而本示例却提供了,且提供了如下几种事件探测:连接建立,连接断开,连接失败和数据接收(包括0比特的数据包)。

Description

描述

This article presents a new socket class which supports both TCP and UDP communication. It provides some advantages compared to other classes that you may find here or on some other Socket Programming articles. First of all, this class doesn't have any limitation like the need to provide a window handle to be used. This limitation is bad if all you want is a simple console application. So, this library doesn't have such a limitation. It also provides threading support automatically for you, which handles the socket connection and disconnection to a peer. It also features some options not yet found in any socket classes that I have seen so far. It supports both client and server sockets. A server socket can be referred as to a socket that can accept many connections. A client socket is a socket that is connected to a server socket. You may still use this class to communicate between two applications without establishing a connection. In the latter case, you will want to create two UDP server sockets (one for each application). This class also helps reduce coding needed to create chat-like applications and IPC (Inter-Process Communication) between two or more applications (processes). Reliable communication between two peers is also supported with TCP/IP with error handling. You may want to use the smart addressing operation to control the destination of the data being transmitted (UDP only). TCP operation of this class deals only with communication between two peers.

Now for those not familiar with IP Socket, the following section will give some details on how it works. This is also the goal with this article: to explain the basic functionality behind socket objects.

本文介绍的Socket类同时支持TCPUDP通讯。相对于你从其他Socket编程文章中找到的Socket类,本文介绍的类有一些优点。首先,该类没有任何限制,比如没有要提供窗口才能使用的限制,若你需要在控制台程序中使用时,这样的限制是相当糟糕的。因此,这儿的库就没有对此进行限制。其次它会自动提供对线程的支持,以处理同另一端的连接和断开连接。它还有一些我在其他Socket类中从未见到过的特色选项。它同时提供对客户端和服务端Socket的支持。一个服务器端的套接字可以被许多接受连接的套接字引用。一个客户端套接字可以用于与服务端套接字的连接。你可以不建立连接,仅使用该类在两个应用程序之间进行通信,对于后一种情况,你需要创建2UDP服务器套接字(一个应用程序一个)。在你创建一些类似聊天的程序和一些进程间通信(两个或多个应用进程间的通信)时,该类会减少需要编写的代码量。通过TCP/IP错误处理,还提供了双方的可靠通信。你可能还想通过智能寻址操作来控制数据传输的目标(仅UDP)。该类的TCP操作仅处理双方之间的通信。对于那些对IP Socket 还不是很熟悉的朋友,

下面这节就会给出其如何工作的一些细节。这也是本文的目的之一:解释
Socket对象背后的基本功能。

TCP/IP Stack

TCP/IP

The TCP/IP stack is shorter than the OSI one:

TCP/IP所分层数要比OSI模型的层数少

TCP is a connection-oriented protocol, while UDP (User Datagram Protocol) is a connectionless protocol.

TCP是面向连接的协议,而UDP(用户自寻址协议)则是无连接协议。

IP Datagrams

IP数据包

The IP layer provides a connectionless and unreliable delivery system. It considers each datagram independently of the others. Any association between datagrams must be supplied by the higher layers. The IP layer supplies a checksum that includes its own header. The header includes the source and destination addresses. The IP layer handles routing through the Internet. It is also responsible for breaking up large datagrams into smaller ones for transmission and reassembling them at the other end.

在网络层提供了无连接的不可靠传输系统。它认为每个数据包对于其他数据包都是独立的。数据包之间的任何联系都需要靠更高的层来代理。网络层提供包含自身报头的检查和。报头又包含了源地址和目的地址。网络层还对互联网的路由进行处理,负责对大数据包进行分解,并在另一端重新进行组合。

UDP

UDP is also connectionless and unreliable. What it adds to IP is a checksum for the contents of the datagram and port numbers. These are used to give a client/server model: see later.

UDP(用户数据报协议)是无连接和不可靠的,它被作为数据包内容和端口号的检查和加到IP中,它将被用来给出C/S模型,稍后会进行描述。

TCP

TCP supplies logic to give a reliable connection-oriented protocol above IP. It provides a virtual circuit that two processes can use to communicate.

TCP为网络层之上的逻辑提供可靠的面向连接的协议。它提供一个虚拟电路以用于两个进程之间的通信。

Internet Addresses

In order to use a service, you must be able to find it. The Internet uses an address scheme for machines so that they can be located. The address is a 32-bit integer which gives the IP address. This encodes a network ID and more addressing. The network ID falls into various classes according to the size of the network address.

为了能使用服务,你必须找到它。互联网对计算机使用地址摘要来标识计算机的位置。IP地址是一个32位的整数。它是通过对网络ID和更多的寻址信息加密得到的。根据IP地址的大小,网络ID分成了不同的几类。

Network Address

Class A uses 8 bits for the network address with 24 bits left over for other addressing. Class B uses 16-bit network addressing; class C uses 24-bit network addressing and class D uses all 32.

A类地址用8位来表示网络寻址,24位来表示其他寻址。B类地址用16位来表示网络寻址,C类用24位来表示网络寻址,D类则用全部的32位来表示网络寻址。

Subnet Address

Internally, the Unix network is divided into subnetworks. Building 11 is currently on one subnetwork and uses 10-bit addressing, allowing 1024 different hosts.

在内部,Unix的网络划分为若干子网。用11来表示当前的一个子网,用10位来寻址,则允许有1024台不同的主机。

Host Address

8 bits are finally used for host addresses within our subnet. This places a limit of 256 machines that can be on the subnet.

在我们的子网中,有8位用于主机寻址。则该子网中的主机不能超过256台。

Total Address

The 32-bit address is usually written as 4 integers separated by dots.

32位的地址常常写成用逗号分隔的4个整数。

Port Addresses

A service exists on a host and is identified by its port. This is a 16-bit number. To send a message to a server, you send it to the port for that service of the host that it is running on. This is not location transparency! Certain of these ports are "well known." For example:

一个主机上存在的服务通过其端口号来标识。它是一个16位的数字。向服务器发送消息,你就是向那台主机上运行的服务的端口号发送消息。没有哪个位置是透明的!当然这些端口号都是“清楚”的。例如:

tcpmux

1

TCP

echo

7

UDP

echo

7

TCP

systat

11

TCP

netstat

15

TCP

ftp-data

20

TCP File Transfer Protocol (data)

ftp

21

TCP File Transfer Protocol

smtp

25

TCP Simple Mail Transfer Protocol

time

37

TCP Time Server

time

37

UDP Time Server

name

42

UDP Name Server

whois

43

TCP nicname

domain

53

UDP

domain

53

TCP

tftp

69

UDP

rje

77

TCP

finger

79

TCP

link

87

TCP ttylink

supdup

95

TCP

hostname

101

TCP hostname

pop-2

109

TCP Post Office Protocol

uucp-path

117

TCP

nntp

119

TCP Network News Transfer Protocol

ntp

123

TCP Network Time Protocol

Ports in the region 1-255 are reserved by TCP/IP. The system may reserve more. User processes may have their own ports above 1023. The function getservbyname can be used to find the port for a service that is registered.

1-255之间的端口号是被TCP/IP保留的。系统可能保留的端口号更多。用户自己使用的端口号应该在1023之上。Getservbyname函数用于找到注册服务的端口号。

Sockets

套接字

A socket is a data structure maintained by the system to handle network connections. A socket is created using the call socket. It returns an integer that is like a file descriptor. In fact, under Windows, this handle can be used with the ReadFile and WriteFile functions.

一个套接字是系统用于处理网络连接所保持的数据结构。可以通过调用socket方法来创建套接字。结果是返回一个如文件描述符之类的整数。实际上,在Windows环境中,这种操作可以结合着ReadFileWriteFile函数来使用。

#include <sys/types.h>

#include <sys/socket.h>

int socket(int family, int type, int protocol);

Here, "family" will be AF_INET for IP communications, protocol will be zero and type will depend on whether TCP or UDP is used. Two processes wishing to communicate over a network create a socket each. These are similar to two ends of a pipe, but the actual pipe does not yet exist.

此处,“family”参数可以用AF_INET表示IP通信,protocol参数为0type参数则要根据是用TCP还是UDP来定。两个进程之间通过创建各自的套接字来进行通信,这和管道很类似,然而并不存在什么管道。

Connection Oriented (TCP)

面向连接(TCP

One process (server) makes its socket known to the system using bind. This will allow other sockets to find it. It then "listens" on this socket to "accept" any incoming messages. The other process (client) establishes a network connection to it and then the two exchange messages. As many messages as needed may be sent along this channel, in either direction.

服务端进程通过bind方法将其套接字告知系统,以使其他的套接字能找到它。它可通过套接字的“侦听(listen)”来“接收(accept)”发过来的信息。客户端的进程同服务端套接字建立连接然后交换信息。需要的信息都可以从该通道向任一端进行发送。

Server:

服务器:

  • Create endpoint (socket())
  • Bind address (bind())
  • Specify queue (listen())
  • Wait for connection (accept())
  • Transfer data (read()/write())
  • 创建端点 (socket())
  • 绑定地址(bind())
  • 指定队列(listen())
  • 等待连接 (accept())
  • 传输数据 (read()/write())

Client:

客户端:

  • Create endpoint (socket())
  • Connect to server (connect())
  • Transfer data (read()/write())
  • 创建端点 (socket())
  • 链接服务器 (connect())
  • 传输数据(read()/write())

Connectionless (UDP)

无连接(UDP

In a connectionless protocol, both sockets have to make their existence known to the system using bind. This is because each message is treated separately, so the client has to find the server each time it sends a message and vice versa. When bind is called, it binds to a new port. It cannot bind to one already in use. If you specify the port as zero, the system gives you a currently unused port. Because of this extra task on each message send, the processes do not use read/write, but recvfrom/sendto. These functions take as parameters the socket to write to and the address of the service on the remote machine.

用无连接协议,双方的套接字都需要用bind方法来告知系统。这是因为每方的信息都会单独处理,所以每次服务端发信息过来时,客户端都需要找到它,反之亦然。每次调用bind方法,都绑定了一个新的端口。当然,如果端口已经被使用了,则是不能被绑定的。如果你指定的端口为0,则系统会把当前可用的端口自动给你一个。由于发送信息的额外任务,进程不会使用read/write方法,而是使用recvfrom/sendto方法。这两个方法的参数一个是要写入的套接字,另一个则是远程计算机上服务的地址。

Server:

服务端:

  • Create endpoint (socket())
  • Bind address (bind())
  • Transfer data (sendto()/recvfrom())
  • 创建端点(socket())
  • 绑定地址 (bind())
  • 传输数据(sendto()/recvfrom())

Client:

客户端:

  • Create endpoint (socket())
  • Bind address (bind()) (optional if connect is called)
  • Connect to server (connect())
  • Transfer data (sendto()/recvfrom())
  • 创建端点 (socket())
  • 绑定地址(bind()) (connect方法可选择调用)
  • 连接服务端(connect())
  • 传输数据(sendto()/recvfrom())

Version History

版本历史

////////////////////////////////////////////////////////////////////////

//    File:        SocketComm.cpp

//    Version:     1.2

//

// 1.0 - Initial release.

// 1.1 - Added support for Smart Addressing mode

// 1.2 - Fixed various issues with address list (in UDP mode)

////////////////////////////////////////////////////////////////////////

How to Use

如何使用

This class can be used to create a TCP or UDP socket. Its use is very simple. First of all, the CSocketComm class is not completed by itself for server operation. This class must be derived. Fortunately, only two functions need to be created, OnDataReceived and OnEvent. The default functions don't do anything. Now to create and start a server socket, do the following:

提供的类可以用来创建TCPUDP套接字。使用方式非常简单。首先,对于服务端的操作,CSocketComm类并没有自身就完成。这个类必须被继承。幸运的是,仅仅只需要创建OnDataReceivedOnEvent两个方法。基类中的这两个方法是空方法,什么事也不干。好,现在按如下的方式来创建一个服务端的套接字:

// To use TCP socket

// no smart addressing - we use connection oriented

m_SocketObject.SetSmartAddressing( false );

m_SocketObject.CreateSocket( m_strPort, AF_INET, SOCK_STREAM,0); // TCP

// To use UDP socket

m_SocketObject.SetSmartAddressing( true );

m_SocketObject.CreateSocket( m_strPort,

   AF_INET, SOCK_DGRAM, SO_BROADCAST); // UDP

// Now you may start the server/client thread to do the work for you...

m_SocketObject.WatchComm();

To create and start a client socket, do the following:

接着创建客户端套接字,如下所示:

// To use TCP socket

m_SocketObject.ConnectTo( strServer, m_strPort, AF_INET, SOCK_STREAM); // TCP

// To use UDP socket

m_SocketObject.ConnectTo( strServer, m_strPort, AF_INET, SOCK_DGRAM); // UDP

// Now you may start the server/client thread to do the work for you...

m_SocketObject.WatchComm();

References

参考

History

版本历史

  • 31 Aug 2002: Updated source code
  • 01 Mar 2004: Updated source code
  • 02 Apr 2004: Fixed bug when sending message to broadcast address
  • 2002831日:更新源代码
  • 200431日:更新源代码
  • 200442日:修改了向广播地址发送信息的Bug

License

许可

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Ernest Laurentin



Ernest is a multi-discipline software engineer.
Skilled at software design and development for all Windows platforms (starting Windows 3.11),
Windows CE and cross-platform UI development.
-
MCSD (C#, .NET)
Interests: User Interface, GDI/GDI+, Scripting, Windows CE .NET.
Programming Skills: C/C++, C#, Java, VB and ASP.NET.

I hope you will enjoy my contributions.

Occupation:

Software Developer (Senior)

Location:

United States