您的位置:首页 > 理论基础 > 计算机网络

Java网络编程-代理2

2015-01-15 22:01 316 查看

1.Socket与ServerSocket介绍

(1)客户端使用Socket与服务方通话

getInputStream() 即可获得服务方发送的消息,getOutputStream即可发送消息给服务方

(2)服务方使用 ServerSocket 来监听客户端的接入

ServerSocket.accept() 即监听入站请求,当有客户端接入时,得到一个Socket

这时服务方可以拿着这个 Socket 对话

此时你把这个服务方这个Socket看成和客户端Socket一样,双方可以平等的对话了

2.服务方与客户端通信简单程序

(1)编写服务方主程序

package com.yli.socket;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
* 启动ServerSocke,并与客户端通信
*
* @author yli
*
* @see InputThread
* @see OutputThread
* @see StartClientTest
*/
public class StartServerTest {

public static void main(String[] args) {
ServerSocket server = null;
try {
// 绑定本机7000号端口,启动 socket服务
server = new ServerSocket();
// server.setSoTimeout(300000);
server.bind(new InetSocketAddress(InetAddress.getLocalHost(), 7000));

System.out.println("start server...");

// 此处只接受一个监听,然后跟接入方对话
// 所以把while注释掉,不注释的话,就允许接入多个客户端
// (但Socket的队列长长度可设置,一般不能超过操作系统默认的,比如50个)
// while (true) {
try {
// 监听客户端连接
Socket socket = server.accept();

// 启动一个线程处理:客户端的输入
String user = "server";
Thread in = new InputThread(socket.getInputStream());
in.start();

// 启动一个线程处理:服务端的响应(本例服务端也从控制台输入一些消息,作为对客户端的响应)
Thread out = new OutputThread(socket.getOutputStream(), user);
out.start();

try {
in.join();
out.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

} catch (IOException e) {
e.printStackTrace();
}
// }

} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != server) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("-------- stop server --------");
}
}

}


(2)编写客户端主程序
package com.yli.socket;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

/**
* 启动一个客户端来测试
*
* @author yli
*
*
* @see StartServerTest
*/
public class StartClientTest {

public static boolean isClosed = false;

public static void main(String[] args) {
// Socket 在Input、Output 两个流中任意一个被关闭
// 或者应用程序执行完毕,或者在抛出异常之后,Socket都会自动关闭
// 但是关闭输入流、输出流的效果不一样,可以测试得出结论
/**
* 1. 客户端主动输入 bye ,客户端的输出流主动关闭, OutputThread 主动正常退出!(此时客户端Socket其实已被关闭)
* 这将触发客户端的输入流也会被关闭,它是由于捕获到socket被关闭的异常,导致InputThread线程也退出
* 这时客户端 main 主线程里面的 in.join() 和 out.join() 也只行完毕,主线程因此执行完毕
* 这才导致 客户端主动结束了整个应用程序
*
* 客户端输入 bye,由于服务端是输入流获取到这个信息,这时服务端的InputThread 主动正常退出!(此时服务端用于和客户端保持连接的Socket已被关闭)
* 但是服务端 main 主线程却没有结束!!是因为服务端 OutputThread 即输出流的线程未结束
* 这时在服务端,你仍然可以在控制台写消息,只是发送的时候,输出流才捕获到Socket已被关闭
* 这才导致 OutputThread会退出,最终main主线程主动退出
*
* 2.反过来测试:服务端主动输入 bye ,过程其实是一样的,服务端由于主动关闭输出流,将导致输入流也主动关闭,最终整个main主线程退出
* 但是客户端是因为输入流收到 bye ,主动关闭输入流,但这也不会导致整个客户端线程退出!
*
*/

Socket client = new Socket();
try {
// 等待服务端响应 30秒钟超时(本例服务端也需要人从控制台输入...等待时间设置长一些)
client.setSoTimeout(300000);
// 连接到本机7000号端口的server-socket服务
client.connect(new InetSocketAddress(InetAddress.getLocalHost(), 7000));

System.out.println("已经连上远程主机,开始对话吧:");

String user = "client";
InputThread in = new InputThread(client.getInputStream());
in.start();

Thread out = new OutputThread(client.getOutputStream(), user);
out.start();

in.join();
out.join();

} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (null != client) {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

}
}

class HandlerClientInput {

}


(3)编写服务方和客户端都依赖的输入输出处理程序
package com.yli.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
* 启动一个线程:不停的监听来自对方输入的消息!
* 当对方输入 bye 时,认为要结束对话,主动关闭输入流
* @author yli
*
*/
public class InputThread extends Thread {

private BufferedReader reader;

public InputThread(InputStream in) {
reader = new BufferedReader(new InputStreamReader(in));
}

@Override
public void run() {

// 读取对方的输入:服务端和客户端都可以输入一些信息
String line = null;
try {
while (true) {
line = reader.readLine();
if (line == null || "".equals(line.trim())) {
// 如果客户端输入空消息,则继续监听输入
continue;
}
// 打印对方发送的消息
System.out.println(line);

// 如果读取到bye,那么结束对话!
if (line.endsWith("bye")) {
System.out.println("**********主动结束对话 **************");
break;
}
}

} catch (IOException e) {
System.out.println("************* In
4000
put **************");
e.printStackTrace();
System.out.println("************* Input **************");
} finally {
if (null != reader) {
try {
// 运行结束后:关闭输入流
reader.close();
System.out.println("输入流主动-正常关闭!");
} catch (IOException e) {
e.printStackTrace();
}
}
}

}

}

package com.yli.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

/**
* 启动一个线程:这里通过控制台来输入消息,并把消息写出去
* 当你输入 bye 时,认为是主动结束对话,程序将主动关闭输出流
*
* @author yli
*
*/
public class OutputThread extends Thread {

private OutputStreamWriter writer;
private String user;

public OutputThread(OutputStream out, String user) {
writer = new OutputStreamWriter(out);
this.user = user;
}

@Override
public void run() {
// 从控制台录入:如果是客户端录入,在发给服务端;反之则是服务端录入发给客户端!
BufferedReader reader = null;
try {
String line;
reader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
line = reader.readLine();
if (null == line || "".equals(line.trim())) {
System.out.println("请输入内容...");
} else {
// 把内容输出到客户端!
writer.write(user + ":" + line + "\r\n");
writer.flush();
}
// 如果输入 bye 说明主动终止对话,服务方和客户端都可以主动终止!
if ("bye".equals(line)) {
break;
}
}
} catch (IOException e) {
System.out.println("************* Output **************");
e.printStackTrace();
System.out.println("************* Output **************");
} finally {
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != writer) {
try {
writer.close();
System.out.println("输出流主动-正常关闭!");
} catch (IOException e) {
e.printStackTrace();
}
}
}

}

}


启动主程序和客户端程序,可以在 cmd 命令行使用 java 命令启动,或者直接在eclipse中运行
开始客户度和服务端的对话吧,测试结果如下



3.需要关注的一些问题

(1)服务方如果允许多个客户端接入,那么会产生多个线程

线程数量太多,系统会崩溃,可以借助 ServerSocketChannel类

它提供基于通道(而非阻塞IO)的多路复用技术,这样一个线程可处理多个连接,效率更高

(2)实例化ServerSocket时可以显示指定queue即队列深度

即允许多少个客户端同时连接,但这个连接不会超过操作系统默认的允许最大值,一般是50

(3)如果客户端非正常退出,比如网络中断,服务方如何能主动感知到

比如服务方实时记录在线人数,客户端异常退出,服务方怎么能捕获到,一般可以采用心跳检测
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息