网络编程之TCP通信
聊天室的实现,多发多收
Server端
主线程定义了循环负责接收客户端Socket管道连接
每接收到一个Socket通信管道后分配一个独立的线程负责处理它。
处理的方式为:
1、从socket通信管道得到一个字节输入流
2、把字节输入流包装成缓冲字符输入流进行消息的接收
3、按照行读取消息
Client端
1、创建Socket通信管道请求有服务端的连接
2、从socket通信管道中得到一个字节输出流 负责发送数据
Server
/**
目标:实现服务端可以同时处理多个客户端的消息。
*/
public class ServerDemo2 {
public static void main(String[] args) {
try {
System.out.println("===服务端启动成功===");
// 1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
// a.定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。
while (true) {
// 2、每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress()+ "它来了,上线了!");
// 3、开始创建独立线程处理socket
new ServerReaderThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Client
public class Server{
public static void main(String[] args) throws IOException {
System.out.println("服务端启动");
//1、创建发送端对象
DatagramSocket socket = new DatagramSocket(6666);
//2、创建一个数据包对象封装数据
/**
public DatagramPacket(byte buf[], int offset, int length,
InetAddress address, int port)
**/
byte[] buffer = "厦大百年生日快乐".getBytes();
DatagramPacket packet = new DatagramPacket(buffer,buffer.length,
InetAddress.getLocalHost(),8888);
//3、发送数据
socket.send(packet);
socket.close();
}
}
ServerReaderThread类:处理线程
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
// 3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
// 4、把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 5、按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了!!!");
}
}
}
改进:使用线程池优化
客户端与服务端的线程模型是: N-N的关系。
客户端并发越多,系统瘫痪的越快。
使用线程池,适合局域网,但不适合并发。
Server端
/**
目标:实现服务端可以同时处理多个客户端的消息。
*/
public class ServerDemo2 {
// 使用静态变量记住一个线程池对象
private static ExecutorService pool = new ThreadPoolExecutor(300,
1500, 6, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2)
, Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
try {
System.out.println("===服务端启动成功===");
// 1、注册端口
ServerSocket serverSocket = new ServerSocket(6666);
// a.定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。
while (true) {
// 2、每接收到一个客户端的Socket管道,
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress()+ "它来了,上线了!");
// 任务对象负责读取消息。
Runnable target = new ServerReaderRunnable(socket);
pool.execute(target);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}