通常,套接字采用客户机-服务器架构。服务器通过监听指定端口,来等待客户请求。服务器在收到请求后,接受来自客户套接字的连接,从而完成连接。
实现特定服务(如 telnet、ftp 和 http)的服务器监听众所周知的端口(telnet 服务器监听端口 23,ftp 服务器监听端口 21,Web 或 http 服务器监听端口 80)。所有低于 1024 的端口都是众所周知的,我们可以用它们来实现标准服务。
当客户进程发出连接请求时,它的主机为它分配一个端口。这个端口为大于1024的某个数字。例如,当 IP 地址为 146.86.5.20 的主机 X 的客户希望与 IP 地址为 161.25.19.8 的 Web 服务器(监听端口 80)建立连接时,它所分配的端口可为1625。该连接由一对套接字组成:主机 X 上的(146.86.5.20:1625), Web服务器上的(161.25.19.8:80)。这种情况如图 1 所示。根据目的端口号码,主机之间传输的分组可以发送到适当的进程。
图 1 采用套接字的通信
所有连接必须是唯一的。因此,当主机 X 的另一个进程希望与同样的 Web 服务器建立另一个连接时,它会分配到另一个大于 1024 但不等于 1625 的端口号。这确保了所有连接都由唯一的一对套接字组成。
为了演示套接字,本节我们会用 Java 语言,因为 Java 提供一个更加简单的套接字接口而且提供丰富的网络工具库。
Java 提供三种不同类型的套接字:
- 面向连接的 TCP 套接字是用 Socket 类实现的;
- 无连接的 UDP 套接字使用 DatagramSocket 类。
- MulticastSocket 类为 DatagramSocket 类的子类,多播套接字允许数据发送到多个接收者。
下面的例子,即日期服务器,采用面向连接的 TCP 套接字。这个操作允许客户机向服务器请求当前的日期和时间。服务器监听端口 6013,当然端口号可以是任何大于 1024 的数字。在接收到连接时,服务器将日期和时间返回给客户机。
import java.net.*; import java.io.*; public class DateServer { public static void main(String[] args) { try { ServerSocket sock = new ServerSocket(6013); /* now listen for connections */ while (true) { Socket client = sock.accept(); PrintWriter pout = new PrintWriter(client.getOutputStream(),true); /* write the Date to the socket */ pout. println (new java.util.Date().toString()); /* close the socket and resume */ /* listening for connections */ client.close(); } } catch (IOException ioe) { System.err.println(ioe); } } }
此程序为日期服务器程序。服务器创建一个 ServerSocket,监听端口号 6013,然后调用 accept() 方法,开始监听端口。服务器阻塞在方法 accept() 上,等待客户请求连接。当接收到连接请求时,accept() 返回一个套接字,以供服务器与客户进程进行通信。
服务器如何与套接字通信的有关细节如下。服务器首先建立 PrintWriter 对象,以便与客户进行通信。PrintWriter 对象允许服务器通过输出方法 print() 和 printin() 写到套接字。服务器通过方法 println() 发送日期到客户机。服务器在将日期写到套接字后,就关闭与客户相连的套接字,并且重新监听更多请求。
为与服务器通信,客户创建一个套接字,并且连到服务器监听的端口。以下的 Java 程序为客户机的实现。
import java.net.*; import java.io.*; public class DateClient { public static void main(String[] args) { try { /* make connection to server socket */ Socket sock = new Socket("127.0.0.1",6013); InputStream in = sock.getInputStreamO ; BufferedReader bin = new BufferedReader(new InputStreamReader(in)); /* read the date from the socket */ String line; while ( (line = bin.readLine()) != null) System.out.printIn(line); /* close the socket connection*/ sock.close() ; } catch (IOException ioe) { System.err.printIn(ioe); } } }
客户创建一个 Socket,并与 IP 地址为 127.0.0.1 的服务器端口 6013 建立连接。连接建立后,客户就通过普通的流 I/O 语句来从套接字进行读取。在收到服务器的日期后,客户就关闭端口,并退出。
IP 地址 127.0.0.1 为特殊 IP 地址,称为回送。当计算机采用地址 127.0.0.1 时,它引用自己。这一机制允许同一主机的客户机和服务器可以通过 TCP/IP 协议进行通信。IP 地址 127.0.0.1 可以换成运行日期服务器的另一主机的 IP 地址。除 IP 地址外,也可采用如 www.westminstercollege.edu
这样实际的主机名。
使用套接字的通信,虽然常用和高效,但是属于分布式进程之间的一种低级形式的通信。原因是套接字只允许在通信线程之间交换无结构的字节流。客户机或服务器程序需要自己加上数据结构。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/21189.html