java客户端与服务端建立连接 线程 ServerSocket
程序员文章站
2024-03-23 10:18:04
...
package chat;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
/*
* 聊天客户端
*/
public class Client {
/*
* java.net.Scoket 套接字
* 封装了TCP通讯,使用它可以通过网络连接到服务端并进行数据交换(通讯)
*/
private Socket socket;
/*
* 构造方法
* 用于初始化客户端
*/
public Client() throws Exception{
try {
/*
* 实例化Scoket的过程就是与远端计算机建立连接的过程
* 实际上建立连接是客户端应用程序与服务端应用程序建立的连接
* 那么这里实例化Scoket需要传入两个参数
* 1:服务器端计算机的IP地址(用于在网络中找到服务器端电脑)
* 2:端口号(通过端口号可以找到运行在服务器中的服务端应用程序)
*/
System.out.println("正在连接服务器。。。");
socket = new Socket("localhost",8080);
System.out.println("已建立连接!");
} catch (Exception e) {
throw e;
}
}
/*
* 客户端的启动方法
*/
public void start(){
try {
/*
* Socket 提供方法:
* OutputStream getOutputStream
* 该方法可以获取一个字节输出流,通过该流写出的数据都会发送给远端计算机
* 对于客户而言,远端就是服务端
*/
Scanner scan = new Scanner(System.in);
//客户端输出
OutputStream out = socket.getOutputStream();
//转换指定字符集
OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
//缓冲 字符输出流
PrintWriter pw = new PrintWriter(osw,true);
//启动读取服务端发送过来消息的线程
ServerHandler handler = new ServerHandler();
Thread t = new Thread(handler);
t.start();
System.out.println("开始聊天");
String message = null;
while(true){
message = scan.nextLine();
pw.println(message);
// //读取服务端发送回来的内容
// message = br.readLine() ;
// System.out.println(message);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 该线程专门用来接收服务端发送过来的消息并输出到控制台
*/
private class ServerHandler implements Runnable{
public void run(){
try {
//客户端输入
InputStream in = socket.getInputStream();
//字符
InputStreamReader isr = new InputStreamReader(in,"utf-8");
//字符
BufferedReader br = new BufferedReader(isr);
String message = null;
while((message = br.readLine())!= null){
System.out.println(message);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
Client client = new Client();
client.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package chat;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/*
* 服务器端
*
*/
public class Server {
/*
* java.net.ServerSocket; 运行在服务端的ServerScoket主要职责:
* 1:向OS申请服务端口(客户端就是同过这个端口与服务端建立连接的)
* 2:监听服务端口并建立与客户端的连接
*/
private ServerSocket server;
//存放所有客户端的输出流,用于广播消息
private List<PrintWriter> allOut;
/*
* 服务端构造方法,用来初始化服务端
*/
public Server() throws Exception {
try {
/*
* 初始化ServerSocket的同时向系统申请服务器端口, 若该端口已经被其他程序占用则会抛出异常
*/
server = new ServerSocket(8080);
allOut = new ArrayList<PrintWriter>();
} catch (Exception e) {
// 将来在这里可以记录日志
// 异常若不应当在这里处理,继续抛出
throw e;
}
}
/*
* 服务端开始工作的方法
*/
public void start() {
try {
/*
* ServerSocket提供方法: Socket accept()
* 该方法是一个阻塞方法,作用是监听服务端口,等待客户端的连接一旦一个客户端请求连接
* 那么accept方法会解除阻塞,并返回一个Socket实例,服务器就可以通过这个Socket
* 实例与客户端交互
*/
while(true){
System.out.println("等待一个客户端连接。。");
Socket socket = server.accept();
System.out.println("一个客户端连接了");
//启动一个线程来负责与该客户端交互
ClientHandler handler = new ClientHandler(socket);
Thread t = new Thread(handler);
t.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private class ClientHandler implements Runnable {
/*
* 当前线程就是通过这个Socket与指定客户端交互的
*
*/
private Socket socket;
//当前客户端的地址信息
private String host;
public ClientHandler(Socket socket) {
this.socket = socket;
//获取该客户端地址信息(远端计算机地址信息)
InetAddress address = socket.getInetAddress();
host = address.getHostAddress();
}
public void run() {
PrintWriter pw = null;
try {
/*
* Socket 提供方法: InputStream getInputStream()
* 通过这个流读取到的数据就是远端发送过来的数据
*/
// 服务端输入
// 字节
InputStream in = socket.getInputStream();
// 字符
InputStreamReader isr = new InputStreamReader(in, "utf-8");
// 字符
BufferedReader br = new BufferedReader(isr);
// 服务端输出
OutputStream out = socket.getOutputStream();
// 转换指定字符集
OutputStreamWriter osw = new OutputStreamWriter(out, "UTF-8");
// 缓冲 字符输出流
pw = new PrintWriter(osw, true);
//将该客户端的输出流存入共享集合
synchronized(allOut){//锁集合 保证多个线程不能同时调用
allOut.add(pw);
}
//广播当前客户端上线
sendMessage(host+"上线了");
String message = null;
while ((message = br.readLine()) != null) {
Thread t = Thread.currentThread();
System.out.println("客户" +t.getName()+":"+ message);
// // 发送回给客户端
// pw.println("客户端说:" + message);
//遍历集合,发送给所有客户端
sendMessage(host+":"+message);
}
} catch (Exception e) {
//e.printStackTrace();
}finally{
//处理客户端断开连接后的操作
//将该客户端的输出流从共享集合中删除
synchronized(allOut){
allOut.remove(pw);
}
sendMessage(host+"下线了");
try{
/*
* 网络通讯完毕后关闭socket
*/
socket.close();
}catch(Exception e){
}
}
}
/*
* 将消息广播给所有客户端
*/
private void sendMessage(String message){
synchronized(allOut){
for(PrintWriter o : allOut){
o.println(message);
}
}
}
}
public static void main(String[] args) {
try {
Server server = new Server();
server.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
上一篇: 依赖性任务处理
下一篇: 洛谷 p1089 津津的储蓄计划