第一节 第二节 第三节 第四节 第五节 第六节 第七节
第五节   UDP数据报
TCP是一种可靠的、基于连接的网络协议。网络上两进程采用Socket方式通信时,服务器构建ServerSocket对象,并侦听来自网络的客户连接请求。另一方为客户通过Socket主动向网络上的服务器发送连接请求,服务器接收客户连接请求,同时建立一个新的Socket对象,新的Socket对象负责与客户方继续交互。原服务器构建ServerSocket对象继续侦听来自网络的客户连接请求。图10-7描述基于连接的服务者、客户程序流程图。

例10-4 利用Socket实现服务方和客户一对一的通信。
图10-7是服务方类TcpServer的类图。它描述了类TcpServer继承了Object类(可默认),以及它依赖的类,如:ServerSocket、Socket、DataInputStream、InputStream、IOException、OutputStream、PrintStream等,其中主要的是java.net包的Socket、ServerSocket类(见图10-8)。

1)服务方源程序
package char10;
import java.io.*;
import java.net.*;
public class TcpServer
{
static public void main(String args[]) throws IOException{
ServerSocket svrsoc=null;
Socket soc=null;
InputStream is=null;
OutputStream os=null;
//DataInputStream为InputStream 的子类。
DataInputStream in=null;
//PrintStream为OutputStream的子类。
PrintStream out=null;
try{
//构造ServerSockets对象,端口为8000,
svrsoc=new ServerSocket(8000);
/*让服务者永远等待,直到客户连接到该端口。服务者等待一个
*连接,返回新套接口soc,新套接口soc实现了与客户原建立的套接口
*的连接。
*/
soc=svrsoc.accept();
//获得新套接口soc绑定的输入流InputStream对象
is=soc.getInputStream();
//构造数据输入流DataInputStream对象in
in=new DataInputStream(is);
//获得新套接口soc绑定的输出流OnputStream对象
os=soc.getOutputStream();
//构造输出流PrintStream 对象out
out=new PrintStream(os);
/*这里创建了数据输入流类对象in和输出流类对象out,服务者利用它们从客户接收输入信息(in)和向客户发送信息(out),同样,在客户端也应该建立这两个对象,用来与服务者进行双向通信。服务者向输出流发送的所有信息都成为客户的输入信息,而客户程序的输出的信息都送入服务者的输入流。
*/
//获到客户方的IP地址,soc绑定了与它连接的客户地址
InetAddress clientIP=soc.getInetAddress();
//在服务方显示客户方的IP地址
System.out.println("Client's IP address:"+clientIP);
int port;
port=soc.getPort(); //得到客户方的端口
//在服务方显示客户方的端口
System.out.println("Client's port:"+port);
//向客户发送…
out.println("Welcome!...");
/*readLine()是数据输入流类中的一个方法,用于从输入流中读入对方发送的一行信息。*/
String str=in.readLine(); //在in上读一行
/*当输入流读入的不是”quit”时,将输入的数据写入输出流中,在屏幕上显示,然后继续从输入流中读入,不断循环,直到输入”quit”为止
*/
while(!str.equals("quit")){ //如读出的不是“quit”,继续读

System.out.println("Client said:"+str);
str=in.readLine();
}
System.out.println("Client want to leave.");
}
catch(Exception e) {
System.out.println("Error:"+e);
}
/*不断循环以上代码,直到客户输入”quit”或者str为null为止。最后在退出前,
*关闭输入输出流及Socket
*/

finally{
is.close(); //关闭输入流
os.close(); //关闭输出流
soc.close(); //关闭socket
System.exit(0);
}
}
}
(2)客户方源程序
1)服务方源程序
package char10;
import java.io.*;
import java.net.*;
public class TcpServer
{
static public void main(String args[]) throws IOException{
ServerSocket svrsoc=null;
Socket soc=null;
InputStream is=null;
OutputStream os=null;
//DataInputStream为InputStream 的子类。
DataInputStream in=null;
//PrintStream为OutputStream的子类。
PrintStream out=null;
try{
//构造ServerSockets对象,端口为8000,
svrsoc=new ServerSocket(8000);
/*让服务者永远等待,直到客户连接到该端口。服务者等待一个
*连接,返回新套接口soc,新套接口soc实现了与客户原建立的套接口
*的连接。
*/
soc=svrsoc.accept();
//获得新套接口soc绑定的输入流InputStream对象
is=soc.getInputStream();
//构造数据输入流DataInputStream对象in
in=new DataInputStream(is);
//获得新套接口soc绑定的输出流OnputStream对象
os=soc.getOutputStream();
//构造输出流PrintStream 对象out
out=new PrintStream(os);
/*这里创建了数据输入流类对象in和输出流类对象out,服务者利用它们从客户接收输入信息(in)和向客户发送信息(out),同样,在客户端也应该建立这两个对象,用来与服务者进行双向通信。服务者向输出流发送的所有信息都成为客户的输入信息,而客户程序的输出的信息都送入服务者的输入流。
*/
//获到客户方的IP地址,soc绑定了与它连接的客户地址
InetAddress clientIP=soc.getInetAddress();
//在服务方显示客户方的IP地址
System.out.println("Client's IP address:"+clientIP);
int port;
port=soc.getPort(); //得到客户方的端口
//在服务方显示客户方的端口
System.out.println("Client's port:"+port);
//向客户发送…
out.println("Welcome!...");
/*readLine()是数据输入流类中的一个方法,用于从输入流中读入对方发送的一行信息。*/
String str=in.readLine(); //在in上读一行
/*当输入流读入的不是”quit”时,将输入的数据写入输出流中,在屏幕上显示,然后继续从输入流中读入,不断循环,直到输入”quit”为止
*/
while(!str.equals("quit")){ //如读出的不是“quit”,继续读

System.out.println("Client said:"+str);
str=in.readLine();
}
System.out.println("Client want to leave.");
}
catch(Exception e) {
System.out.println("Error:"+e);
}
/*不断循环以上代码,直到客户输入”quit”或者str为null为止。最后在退出前,
*关闭输入输出流及Socket
*/

finally{
is.close(); //关闭输入流
os.close(); //关闭输出流
soc.close(); //关闭socket
System.exit(0);
}
}
}
(2)客户方源程序

图10-9描述TcpClient类图。它描述了类TcpClient继承了Object类(可默认),以及它依赖的类。
代码如下:
package char10;
import java.net.*;
import java.io.*;
public class TcpClient{
static public void main(String args[]) throws IOException{
Socket soc=null;
InputStream is=null;
OutputStream os=null;
DataInputStream in=null;
PrintStream out=null;
String strin=null;
try{
/* 客户先创建一个要与指定的服务者固定端口连接的Socket,假如上
*服务者程序在本机”localhost”上,则以下语句。
*/
soc=new Socket("localhost",8000);
System.out.println("Connecting to the Server...");
is=soc.getInputStream();
os=soc.getOutputStream();
in=new DataInputStream(is);
out=new PrintStream(os);
strin=in.readLine();
System.out.println("Server said:"+strin);
byte bmsg[] = new byte[20];
System.in.read(bmsg);
String msg=new String(bmsg,0);
msg=msg.trim();
/*当从键盘读入的不是”quit”时,将键入的数据写入输出流中,然后继续从键盘读入,不断循环,直到输入”quit”为止
*/
while(!msg.equals("quit")){
out.println(msg);
System.in.read(bmsg);
msg=new String(bmsg,0);
msg=msg.trim();
}
out.println(msg);
}
catch (Exception e) {
System.out.println("Error: "+e);
}
finally{
is.close();
os.close();
soc.close();
System.exit(0);
}
}
}

当运行服务器端程序时,开始没有任何显示,运行客户端程序后,将会显示下图结果的前两行,即显示客户端的IP地址和端口号,当客户端输入字符串后,服务器端的运行结果会显示相应的字符串,当客户端输入的字符串为“quit”时,服务器端显示“Client want to leave.”。
服务器端运行结果如图10-10所示:

客户端运行后,首先与服务器连接,然后显示服务器端发送过来的问候语“Welcome!”,接着,可以任意输入字符串,此时,服务器端会有相应的输出,直到输入“quit”,这时,客户端和服务器端程序都将结束。客户端运行结果如图10-11所示:

图10-12显示利用控制台视图的工具栏可以切换显示服务器一端或客户一端。

图10-12 客户端运行结果