本节目标:
了解Socket与ServerSocket类的作用
掌握ECHO程序的开发
将多线程应用在TCP程序上实现多用户操作
1.TCP程序设计
在java中使用Socket(套接字)完成TCP程序的开发,使用此类可以方便的建立可靠的、双向的、持续的、点对点的通讯连接。
在Socket的程序开发中,服务器端使用ServerSocket等待客户端的连接,对于java的网络程序而言,每一个客户端都使用一个Socket对象表示。
在java的网络程序中,客户端只要符合连接的通讯协议,那么服务器端都可以进行接收。
ServerSocket类主要用在服务器端程序的开发上,用于接收客户端的连接请求。
ServerSocket(int port)
创建绑定到特定端口的服务器套接字。
在服务器端每次运行时都要使用accept()方法等待客户端连接,此方法执行后服务器端进入阻塞状态,直到客户端连接后程序才会向下继续执行,此方法返回值类型是Socket,每一个Socket代表一个客户端对象。
Socket(String host, int port)
创建一个流套接字并将其连接到指定主机上的指定端口号。
服务器端与客户端的通讯:
public class HelloServer {
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(8888); // 创建绑定到8888端口的服务器套接字
Socket client = server.accept(); //客户端
System.out.println("服务器运行,等待连接...");
PrintStream out = new PrintStream(client.getOutputStream());
String str = "hello world";
out.print(str);
client.close();
server.close();
}
}
telnet localhost 8888
结果运行:
服务器将内容发送到了客户端后,客户端断开与服务器的连接。
public class HelloClient {
public static void main(String[] args) throws Exception, IOException {
Socket client = new Socket("localhost", 8888);
BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
String str = buf.readLine();
System.out.println("客户端接收的内容:" + str);
buf.close();
client.close();
}
}
运行结果:
客户端接收的内容:hello world
从以上可以发现,该架构为C/S架构,即客户端/服务器架构,需要编写和维护两套代码代码。另外一种B/S架构,即浏览器/服务器架构,只需维护一套代码即可。
案例:Echo程序
服务器端程序
public class HelloServer {
public static void main(String[] args) throws Exception {
ServerSocket server = null; //声明ServerSocket类
Socket client = null; //客户端
BufferedReader buf = null;//接收输入流
PrintStream out = null; //打印流输出
server = new ServerSocket(8888);//服务器监听8888端口
boolean f = true;
while(f){
System.out.println("服务器正在运行,等待客户端连接。");
client = server.accept();//得到连接,进入阻塞状态
out = new PrintStream(client.getOutputStream());//接收客户端的输入信息
buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
boolean flag = true; //标志位,可以一直接收和回应
while(flag){
String str = buf.readLine();
if (str == null || "".equals(str)) {
flag = false;
}else{
if("bye".equals(str)){
flag = false;
}else{
out.print("ECHO:" + str);
}
}
}
client.close();
}
server.close();
}
}
客户端程序
public class HelloClient {
public static void main(String[] args) throws Exception, IOException {
Socket client = null; //客户端
client = new Socket("localhost", 8888);
BufferedReader buf = null;//一次性接收
PrintStream out = null; //发送数据
BufferedReader input = null; //接收键盘数据
input = new BufferedReader(new InputStreamReader(System.in));
buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
out = new PrintStream(client.getOutputStream());
boolean flag = true;
while(flag){
System.out.println("输入信息:");
String str = input.readLine();
out.print(str);
if ("bye".equals(str)) {
flag = false;
}else{
String echo = buf.readLine();
System.out.println(echo);
}
}
buf.close();
client.close();
}
}
加入多线程:
public class EchoServer {
public static void main(String[] args) throws Exception {
ServerSocket server = null; //声明ServerSocket类
Socket client = null; //客户端
server = new ServerSocket(8888);//服务器监听8888端口
boolean f = true;
while(f){
System.out.println("服务器正在运行,等待客户端连接。");
client = server.accept();//得到连接,进入阻塞状态
new Thread(new EchoThread(client)).start(); //启动多线程
}
server.close();
}
}
public class EchoThread implements Runnable {
private Socket client = null;
public EchoThread(Socket client) {
this.client = client;
}
@Override
public void run() {
BufferedReader buf = null;// 接收输入流
PrintStream out = null; // 打印流输出
try {
out = new PrintStream(client.getOutputStream());// 接收客户端的输入信息
buf = new BufferedReader(new InputStreamReader(
client.getInputStream()));
boolean flag = true; // 标志位,可以一直接收和回应
while (flag) {
String str = buf.readLine();
if (str == null || "".equals(str)) {
flag = false;
} else {
if ("bye".equals(str)) {
flag = false;
} else {
out.print("ECHO:" + str);
}
}
}
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class EchoClient {
public static void main(String[] args) throws Exception, IOException {
Socket client = null; //客户端
client = new Socket("localhost", 8888);
BufferedReader buf = null;//一次性接收
PrintStream out = null; //发送数据
BufferedReader input = null; //接收键盘数据
input = new BufferedReader(new InputStreamReader(System.in));
buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
out = new PrintStream(client.getOutputStream());
boolean flag = true;
while(flag){
System.out.println("输入信息:");
String str = input.readLine();
out.print(str);
if ("bye".equals(str)) {
flag = false;
}else{
String echo = buf.readLine();
System.out.println(echo);
}
}
buf.close();
client.close();
}
}
2.UDP设计
TCP虽然可靠,但是浪费系统性能,为了减少这种开销,使用UDP,服务器端发送的消息,客户端不一定接收到,在聊天工具中被广泛使用。
使用DatagramPacket包装一条要发送的消息, DatagramSocket用于完成信息的发送操作。
public class UDPServer{
public static void main(String args[]) throws Exception{ // 所有异常抛出
DatagramSocket ds = null ; // 定义发送数据报的对象
DatagramPacket dp = null ; // 声明DatagramPacket对象
ds = new DatagramSocket(3000) ; // 服务端在3000端口上等待服务器发送信息\
String str = "hello World!!!" ;
dp = new DatagramPacket(str.getBytes(),str.length(),InetAddress.getByName("localhost"),9000) ; // 所有的信息使用buf保存
System.out.println("发送信息。") ;
ds.send(dp); // 发送信息出去
ds.close() ;
}
}
public class UDPServer{
public static void main(String args[]) throws Exception{ // 所有异常抛出
DatagramSocket ds = null ; // 定义发送数据报的对象
DatagramPacket dp = null ; // 声明DatagramPacket对象
ds = new DatagramSocket(3000) ; // 服务端在3000端口上等待服务器发送信息\
String str = "hello World!!!" ;
dp = new DatagramPacket(str.getBytes(),str.length(),InetAddress.getByName("localhost"),9000) ; // 所有的信息使用buf保存
System.out.println("发送信息。") ;
ds.send(dp); // 发送信息出去
ds.close() ;
}
}