废话不多说,直接进入正题。
1 计算机网络,分组报文和协议
计算机网络由一组通过通信信道相互连接的机器组成。我们把这些机器称为主机(hosts)和路由器(routers)。
主机是指运行应用程序的计算机,这些应用程序包括网络浏览器(Web browser),即时通讯代理(IM agent),或者是文件共享程序。运行在主机上的应用程序才是计算机网络的真正"用户"。
路由器的作用是将信息从一个通信信道传递或转发(forward)到另一个通信信道。他只是通信信道(communication channel)就是将字节序列从一个主机传输到另一个主机的一种手段,可能是有线电缆,如以太网(Ethernet),也可能是无线的,如 WiFi[ ],或是其他方式的连接。路由器非常重要,因为要想直接将所有不同主机连接起来是不可行的。相反,一些主机先得连接到路由器,路由器再连接到其他路由器,这样就形成了网络。
这里的信息(information)是指由程序创建和解释的字节序列。在计算机网络环境中,这些字节序列被称为分组报(packets)。
协议(protocol)相当于是相互通信的程序间达成的一种约定,它规定了分组报文的交换方式和它们包含的意义。要实现一个有用的网络,必须解决大量各种各样的问题。为了使这些问题可管理和模块化,人们设计了不同的协议来解决不同类型的问题。其中TCP和UDP就是其中的两种。
协议分层组织是一种非常有用的措施,TCP/IP 协议族和UDP/IP协议族,实际上其他所有协议族都是按这种方式组织的。
Application:应用程序;Socket:套接字;Host:主机;Channel:通信信道;Router:路由器;Network Layer:网络层;Transport Layer:传输层。
从上面可以看出无论是哪种通讯协议的底层都是IP协议层,上面再提供出我们熟悉的udp和tcp供我们选择。
套接字
上面我们说到一个东西叫套接字,Socket(套接字)是一种抽象层,应用程序通过它来发送和接收数据,就像应用程序打开一个文件句柄,将数据读写到稳定的存储器上一样。一个 socket 允许应用程序添加到网络中,并与处于同一个网络中的其他应用程序进行通信。一台计算机上的应用程序向 socket写入的信息能够被另一台计算机上的另一个应用程序读取,反之亦然。
TCP的套接字
Java 为 TCP 协议提供了两个类:Socket 类和 ServerSocket 类。一个 Socket 实例代表了TCP 连接的一端。一个 TCP 连接(TCP connection)是一条抽象的双向信道(可靠地),两端分别由 IP地址和端口号确定。在开始通信之前,要建立一个 TCP 连接,这需要先由客户端 TCP 向服务器端 TCP 发送连接请求。ServerSocket 实例则监听 TCP 连接请求,并为每个请求创建新的 Socket 实例。也就是说,服务器端要同时处理 ServerSocket 实例和 Socket 实例,而客户端只需要使用 Socket 实例。
下面我们来做个实例:
1.创建一个 Socket 实例:构造器向指定的远程主机和端口建立一个 TCP 连接。
2. 通过套接字的输入输出流(I/O streams)进行通信:一个 Socket 连接实例包括一个
InputStream 和一个 OutputStream,它们的用法同于其他 Java 输入输出流。
3. 使用 Socket 类的 close()方法关闭连接。
TCP 客户端
import java.net.Socket;
1 import java.net.SocketException;
2 import java.io.IOException;
3 import java.io.InputStream;
4 import java.io.OutputStream;
5
6 public class TCPEchoClient {
7
8 public static void main(String[] args) throws
IOException {
9
10 if ((args.length < 2) || (args.length > 3)) // Test
for correct # of args
11 throw new IllegalArgumentException("Parameter(s):
<Server> <Word> [<Port>]");
12
13 String server = args[0]; // Server name or IP address
14 // Convert argument String to bytes using the default
character encoding
15 byte[] data = args[1].getBytes();
16
17 int servPort = (args.length == 3) ?
Integer.parseInt(args[2]) : 7;
18
19 // Create socket that is connected to server on
specified port
20 Socket socket = new Socket(server, servPort);
21 System.out.println("Connected to server...sending
echo string");
22
23 InputStream in = socket.getInputStream();
24 OutputStream out = socket.getOutputStream();
25
26 out.write(data); // Send the encoded string to the
server
27
28 // Receive the same string back from the server
29 int totalBytesRcvd = 0; // Total bytes received so
far
30 int bytesRcvd; // Bytes received in last read
31 while (totalBytesRcvd < data.length) {
32 if ((bytesRcvd = in.read(data, totalBytesRcvd,
33 data.length - totalBytesRcvd)) == -1)
34 throw new SocketException("Connection closed
prematurely");
35 totalBytesRcvd += bytesRcvd;
36 } // data array is full
37
38 System.out.println("Received: " + new String(data));
39
40 socket.close(); // Close the socket and its streams
41 }
42 }
TCP 服务器端
1. 创建一个 ServerSocket 实例并指定本地端口。此套接字的功能是侦听该指定端口收
到的连接。
2. 重复执行:
a. 调用 ServerSocket 的 accept()方法以获取下一个客户端连接。基于新建立的客户端连
接,创建一个 Socket 实例,并由 accept()方法返回。
b. 使用所返回的 Socket 实例的 InputStream 和 OutputStream 与客户端进行通信。
c. 通信完成后,使用 Socket 类的 close()方法关闭该客户端套接字连接。
下面的例子,TCPEchoServer.java,为我们前面的客户端程序实现了一个回馈服务器。
这个服务器程序非常简单,它将一直运行,反复接受连接请求,接收并返回字节信息。直到
客户端关闭了连接,它才关闭客户端套接字。
0 import java.net.*; // for Socket, ServerSocket, andInetAddress
1 import java.io.*; // for IOException and
Input/OutputStream
2
3 public class TCPEchoServer {
4
5 private static final int BUFSIZE = 32; // Size of receive
buffer
6
7 public static void main(String[] args) throws
IOException {
8
9 if (args.length != 1) // Test for correct # of args
10 throw new IllegalArgumentException("Parameter(s):
<Port>");
11
12 int servPort = Integer.parseInt(args[0]);
13
14 // Create a server socket to accept client connection
requests
15 ServerSocket servSock = new ServerSocket(servPort);
16
17 int recvMsgSize; // Size of received message
18 byte[] receiveBuf = new byte[BUFSIZE]; // Receive
buffer
19
20 while (true) { // Run forever, accepting and servicing
connections
21 Socket clntSock = servSock.accept(); // Get client
connection
22
23 SocketAddress clientAddress =
clntSock.getRemoteSocketAddress();
24 System.out.println("Handling client at " +
clientAddress);
25
26 InputStream in = clntSock.getInputStream();
27 OutputStream out = clntSock.getOutputStream();
28
29 // Receive until client closes connection, indicated
by -1 return
30 while ((recvMsgSize = in.read(receiveBuf)) != -1)
{
31 out.write(receiveBuf, 0, recvMsgSize);
32 }
33 clntSock.close(); // Close the socket. We are done
with this client!
34 }
35 /* NOT REACHED */
36 }
37 }
这样我们的客户端和服务端就建立了连接,并且通过套接字输入输出(以流的形式 stream)
UDP的套接字
java中为我们提供 DatagramPacket 类和 DatagramSocket 类来使用 UDP 套接字。客户端
和服务器端都使用 DatagramSockets 来发送数据,使用 DatagramPackets 来接收数据。
UDP 客户端
UDP 客户端首先向被动等待联系的服务器端发送一个数据报文。一个典型的 UDP 客户
端主要执行以下三步:
1. 创建一个 DatagramSocket 实例,可以选择对本地地址和端口号进行设置。
2. 使用 DatagramSocket 类的 send() 和 receive()方法来发送和接收 DatagramPacket 实
例,进行通信。
下载自:http://www.javaxxz.com 最方便的Java学习社区
3. 通信完成后,使用 DatagramSocket 类的 close()方法来销毁该套接字。
0 import java.net.DatagramSocket;
1 import java.net.DatagramPacket;
2 import java.net.InetAddress;
3 import java.io.IOException;
4 import java.io.InterruptedIOException;
5
6 public class UDPEchoClientTimeout {
7
8 private static final int TIMEOUT = 3000; // Resend timeout
(milliseconds)
9 private static final int MAXTRIES = 5; // Maximum
retransmissions
10
11 public static void main(String[] args) throws
IOException {
12
13 if ((args.length < 2) || (args.length > 3)) { // Test
for correct # of args
14 throw new IllegalArgumentException("Parameter(s):
<Server> <Word> [<Port>]");
15 }
16 InetAddress serverAddress =
InetAddress.getByName(args[0]); // Server address
17 // Convert the argument String to bytes using the default
encoding
18 byte[] bytesToSend = args[1].getBytes();
19
20 int servPort = (args.length == 3) ?
Integer.parseInt(args[2]) : 7;
21
22 DatagramSocket socket = new DatagramSocket();
23
24 socket.setSoTimeout(TIMEOUT); // Maximum receive
blocking time (milliseconds)
25
26 DatagramPacket sendPacket = new
DatagramPacket(bytesToSend, // Sending packet
27 bytesToSend.length, serverAddress, servPort);
28
29 DatagramPacket receivePacket = // Receiving packet
30 new DatagramPacket(new byte[bytesToSend.length],
bytesToSend.length);
31
32 int tries = 0; // Packets may be lost, so we have to
keep trying
33 boolean receivedResponse = false;
34 do {
35 socket.send(sendPacket); // Send the echo string
36 try {
37 socket.receive(receivePacket); // Attempt echo reply
reception
38
39 if
(!receivePacket.getAddress().equals(serverAddress)) {//
Check source
40 throw new IOException("Received packet from an unknown
source");
41 }
42 receivedResponse = true;
43 } catch (InterruptedIOException e) { // We did not get
anything
44 tries += 1;
45 System.out.println("Timed out, " + (MAXTRIES - tries)
+ " more tries...");
46 }
47 } while ((!receivedResponse) && (tries < MAXTRIES));
48
49 if (receivedResponse) {
50 System.out.println("Received: " + new
String(receivePacket.getData()));
51 } else {
52 System.out.println("No response -- giving up.");
53 }
54 socket.close();
55 }
56 }
UDP 服务器
与 TCP 服务器一样,UDP 服务器的工作是建立一个通信终端,并被动等待客户端发起
连接。但由于 UDP 是无连接的,UDP 通信通过客户端的数据报文初始化,并没有 TCP 中
建立连接那一步。典型的 UDP 服务器要执行以下三步:
1. 创建一个 DatagramSocket 实例,指定本地端口号,并可以选择指定本地地址。此时,
服务器已经准备好从任何客户端接收数据报文。
2. 使用 DatagramSocket 类的 receive()方法来接收一个 DatagramPacket 实例。当 receive()
方法返回时,数据报文就包含了客户端的地址,这样我们就知道了回复信息应该发送到什么
地方。
3. 使用 DatagramSocket 类的 send() 和 receive()方法来发送和接收 DatagramPackets 实
例,进行通信。
0 import java.io.IOException;
1 import java.net.DatagramPacket;
2 import java.net.DatagramSocket;
3
4 public class UDPEchoServer {
5
6 private static final int ECHOMAX = 255; // Maximum size
of echo datagram
7
8 public static void main(String[] args) throws
IOException {
9
10 if (args.length != 1) { // Test for correct argument
list
11 throw new IllegalArgumentException("Parameter(s):
<Port>");
12 }
13
14 int servPort = Integer.parseInt(args[0]);
15
16 DatagramSocket socket = new DatagramSocket(servPort);
17 DatagramPacket packet = new DatagramPacket(new
byte[ECHOMAX], ECHOMAX);
18
19 while (true) { // Run forever, receiving and echoing
datagrams
20 socket.receive(packet); // Receive packet from client
21 System.out.println("Handling client at " +
packet.getAddress().getHostAddress()
22 + " on port " + packet.getPort());
23 socket.send(packet); // Send the same packet back to
client
24 packet.setLength(ECHOMAX); // Reset length to avoid
shrinking buffer
25 }
26 /* NOT REACHED */
27 }
28 }
这样udp的服务端和客户端就建立完成了,以上是个人学习过程中的一些总结和记录有不正确的地方还望指正。