Socket是指在一个特定编程模型下,进程间通信链路的端点。因为这个特定编程模型的流行,Socket这个名字在其他领域得到了复用,包括Java叫技术。
如果要建立连接,一台机器必须运行一个进程来等待连接,而另一台机器必须试图到达第一台机器。这个电话系统类似:一方必须发起呼叫,而另一方在此时必须等待电话呼叫。
java网络模型图
下面通过一个有“回显”功能的服务器和客户端来介绍应用java.net包编写网络应用程序。
这个例子主要功能是服务器端的程序等待客户的输入,然后将读取到的信息回显给客户端,同时在服务器端的控制台输出。而客户端从控制台接收信息后,向客户端发送输入,并接收服务器的回显数据,然后显示在控制台。
客户端程序代码如下:
package com.javapp.ch11;
import java.io.*;
import java.net.*;
/**
* Description: 具有“回显”功能的服务器端和客户端程序
*/
public class EchoClientDemo {
// 服务器端的服务端口。
public static final int SERVERPORT = 990;
public static void main(String[] args) {
try {
// 建立连接套接字。
Socket s = new Socket("localhost",SERVERPORT);
System.out.println("socket = " + s);
// 新建网络连接的输入流。
BufferedReader in = new BufferedReader(new InputStreamReader(s
.getInputStream()));
// 新建网络连接的自动刷新的输出流。
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(s.getOutputStream())),true);
// 先使用System.in构造InputStreamReader,再构造BufferedReader。
BufferedReader stdin = new BufferedReader(
new InputStreamReader(System.in));
System.out.println("Enter a string, Enter BYE to exit! ");
while (true) {
// 读取从控制台输入的字符串,并向网络连接输出,即向服务器端发送数据。
out.println(stdin.readLine());
// 从网络连接读取一行,即接收服务器端的数据。
String str = in.readLine();
// 如果接收到的数据为空(如果直接按Enter,不是空数据),则退出循环,关闭连接。
if (str == null) {
break;
}
System.out.println(str);
}
s.close();
} catch (IOException e) {
System.err.println("IOException" + e.getMessage());
}
}
}
上面客户端程序中。首先用java.net包中的Socket类建立一个连接套接字,其后应用的Socket对象的getInputStream方法从服务器接收数据,并且应用Socket对象的getOuputStream方法发送数据到服务器。创建完输入输出流,就可以像读写文件的方式来读写数据。支持多客户端的“回显”服务器端程序代码如下:
package com.javapp.ch11;
import java.io.*;
import java.net.*;
/**
* Description:支持多客户端的“回显”服务器端程序
*/
public class EchoServerThreadDemo {
// 服务器端的服务端口。
public static final int SERVERPORT = 990;
public static void main(String[] args) {
try {
// 已经连接上的客户端的序号。
int number = 1;
// 建立服务器端倾听套接字。
ServerSocket s = new ServerSocket(SERVERPORT);
System.out.println("Started: " + s);
while (true) {
// 等待并接收请求,建立连接套接字。
Socket incoming = s.accept();
System.out.println("Connection " + number + " accepted: ");
System.out.println(incoming);
// 启动一个线程来进行服务器端和客户端的数据传输。
// 主程序继续监听是否有请求到来。
Thread t = new EchoThread(incoming,number);
t.start();
number++;
}
} catch (IOException e) {
System.err.println("IOException");
}
}
}
class EchoThread extends Thread {
private Socket s;
int n;
public EchoThread(Socket incoming,int number) {
s = incoming;
n = number;
}
public void run() {
try {
// 新建网络连接的输入流。
BufferedReader in = new BufferedReader(new InputStreamReader(s
.getInputStream()));
// 新建网络连接的自动刷新的输出流。
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(s.getOutputStream())),true);
System.out.println("Hello! Enter BYE to exit.");
// 回显客户端的输入。
while (true) {
// 从网络连接读取一行,即接收客户端的数据。
String line = in.readLine();
// 如果接收到的数据为空(如果直接按Enter,不是空数据),则退出循环,关闭连接。
if (line == null) {
break;
} else {
if (line.trim().equals("BYE")) {
System.out.println("The client " + n + " entered BYE!");
System.out.println("Connection " + n + " will be closed!");
break;
}
System.out.println("Echo " + n + ": " + line);
// 向网络连接输出一行,即向客户端发送数据。
out.println("Echo " + n + ": " + line);
}
}
// 关闭套接字。
s.close();
} catch (IOException e) {
System.err.println("IOException");
}
}
}
在服务器端程序中,首先用java.net包中的ServerSocket类创建一个服务器端侦听套接字。其后应用ServerSocket类的accept方法等待并接收用户请求。当服务器每接收到一个连接请求后,就启动一个线程来单独处理服务器和客户端的数据传输。服务器端数据的接收和发送与上面介绍的客户端数据的发送和介绍相同。