首先这是一个很初级,但是很好懂的、基于java-socket实现的聊天室。但是代码还未封装成型,以及其他待改善的地方,先记录一波。
记一次尝试多次的socket聊天室
网络编程的本质和现状
首先java的网络编程基本已经没有市场了。。。但是这是一个很基础的,也为了理解计网或者是后期项目打的基础的。总体也有点锻炼业务思路。
网络编程这块,在计网中,你首先得了解 IP 与 端口这两个名词,好比你家地址和具体的屋里的门,这样到你家以后才能找到你,而不是你爸或者隔壁老王。
socket的思路就c/s模式,客户端模式里面我没有使用线程,服务端我们使用多线程(实际操作系统和实际项目也是这样的,因为你不能只是服务一个程序)(说到操作系统,我觉得理解一下网络编程对于系统和计算机网络都有好处)(这块还涉及到osi5层模型,socket在传输层与应用层之间)(先说到这,到时候我联系一下计网笔记再聊)
写的过程中的思路过程
当我知道这个需求的时候,一开始我是上来就想多进程的。我理解的过于简单,就知道来一个客户端就建立一个线程,too young。。后面的话,我先实现单线程的连接,然后了解到其实客户端的socket才是一个桥梁。理解了这个东西以后,在哪里多线程就清楚多了。另外注意的点是输入输出流的细节,一定要flush,请注意我说的是flush不是close
因为你close的时候会顺带着把从客户端的socket的连接一并断掉,显然你不想。。下面是直接close报的错误
java.net.SocketException: Socket is closed
一、实现多线程的过程
我找了很多视频,代码发现确实功能都太多了,不利于理解本质。然后我自己就从单线程开始搞。搞这部分主要的理解在于,理解不管是客户端还是服务端 我们都要通过与客户端相连的socket来发送消息(好像是废话)
//向服务端发送消息
OutputStream out=clientSocket.getOutputStream();
// 接受客户端发来的消息
in = clientSocket.getInputStream();
所以我们从这个角度分析,我们接到一个accept监听来的客户端的socket就建立一个线程。有了这个想法以后我们就准备把socket放进多线程里面
我们知道run()不能让我们直接调用。我们实现一个线程类,我们把这个socket放进有参构造器里面,实现传参调用。
class ServerThread implements Runnable {
//记录--客户端序号
private int num;
private Socket clientSocket;
public ServerThread(Socket clinet,int num) {
this.clientSocket = clinet;
this.num=num;
}
二、具体的输入输出流
详情见代码思路,多试试才是王道
三、代码分析和后续优化
发现,客户端和服务端的接受与发送代码冗余度100%,后期准备封装方法。
三、上代码
客户端
package Homework11;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;
import java.util.zip.InflaterInputStream;
public class Client {
public static void main(String[] args) throws Exception{
Socket clientSocket=new Socket("localhost", 8889);
//1.---------客户端发送消息部分
while (true) {
Scanner scanner=new Scanner(System.in);
String string="我是客户端::::::";
//输入端的自定义的消息
System.out.println("请客户端输入消息");
String DIYdata=scanner.next();
//向服务端发送消息
OutputStream out=clientSocket.getOutputStream();
//转换流
OutputStreamWriter osw=new OutputStreamWriter(out);
osw.write(string);
osw.write(DIYdata);
//必须关闭不然消息带不走
osw.close();
//2.---------客户端接受消息部分
InputStream in = null;
// 准备输出流一会接受客户端发来的消息
try {
in = clientSocket.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
char[] data = new char[1024];
InputStreamReader isr = new InputStreamReader(in);
try {
isr.read(data);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
StringBuffer sb = new StringBuffer();
sb.append(data);
// 输出服务端数据
System.out.println(sb.toString());
}
}
}
服务端
package Homework11;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
//字节流变成字符流
public class SeverSocket {
//记录--客户端序号
static int num = 0;
public static void main(String[] args) throws Exception {
// 客户端数目
ServerSocket serverSocket = new ServerSocket(8889);
System.out.println("服务端已启动");
// sock---客户端的socket
while (true) {
Socket clientSocket = serverSocket.accept();
num++;
System.out.println("第" + num + "个客户端已经启动");
Thread thread = new Thread(new ServerThread(clientSocket,num));
thread.start();
}
}
}
class ServerThread implements Runnable {
//记录--客户端序号
private int num;
private Socket clientSocket;
public ServerThread(Socket clinet,int num) {
this.clientSocket = clinet;
this.num=num;
}
@Override
public void run() {
// 客户端进来的消息
while (true) {
InputStream in = null;
// 准备输出流一会接受客户端发来的消息
try {
in = clientSocket.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
char[] data = new char[1024];
InputStreamReader isr = new InputStreamReader(in);
try {
isr.read(data);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
StringBuffer sb = new StringBuffer();
sb.append(data);
// 输出客户端数据
System.out.println("第" + num + "个客户端输出");
System.out.println(sb.toString());
// 服务端发给客户端的消息
//准备好输出流
OutputStream out = null;
System.out.println("服务端请输入:::");
try {
out = clientSocket.getOutputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//转换成字符流
OutputStreamWriter osw=new OutputStreamWriter(out);
Scanner scanner=new Scanner(System.in);
String vlog="我是服务端::::::";
String serverdata=scanner.next();
try {
osw.write(vlog);
osw.write(serverdata);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
osw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}