在学习完socket之后我们已经可以实现服务端与客户端通过socket获取输入输出流的方法。完成服务端与客户端的单向对话,但是如何完成服务端与客户端的交流呢,你一句我一句的交流,甚至是类似于QQ聊天室的功能。也可以通过服务器转发信息到各个服务端,在学习过多线程之后便可以完成这些功能;
首先完成简单的服务端与客户端的交流对话
思路:服务端或者客户端要同时具有读数据以及写数据的功能,可以编写一个专门用来读取数据的线程和写数据的线程,在服务端和客户端同时启动这两个线程,便可以完成对话
package day1.socket;
import java.io.IOException;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
final String Clname="客户端";
try {
Socket socket = new Socket("localhost",9999);
System.out.println("客户端开启");
new WriterThread(socket,Clname).start();
new ReaderThread(socket).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//服务端
package day1.socket;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
final String Sername="服务端:";
try {
ServerSocket serverSocke = new ServerSocket(9999);
Socket socket = serverSocke.accept();
System.out.println("服务端开启");
new WriterThread(socket,Sername).start();
new ReaderThread(socket).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//以下为线程类的编写
package day1.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class ReaderThread extends Thread{
private Socket socket;
private BufferedReader BufferedReader;
public ReaderThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
while (true){
try {
BufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String s = BufferedReader.readLine();
System.out.println(s);
//System.out.println(BufferedReader.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package day1.socket;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class WriterThread extends Thread{
private Socket socket;
private Scanner scanner;
public BufferedWriter bufferedWriter;
public String name;
private boolean flag=true;
public WriterThread(Socket socket,String name){
this.socket=socket;
this.name=name;
this.scanner=new Scanner(System.in);
}
public void setFlag() {
this.flag=false;
}
@Override
public void run() {
while (flag){
try {
bufferedWriter=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String content=scanner.nextLine();
if(("bye").equals(content)){//入锅输入bye就flag=false结束连接,客户端也会自然断开
setFlag();
}else {
bufferedWriter.write(name+content+"\t\n");
//bufferedWriter.newLine();//====name+content+"\t\n"
bufferedWriter.flush();
// System.out.println(content);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
完成以上代码编写后,先启动服务端,再启动客户端即可完成一对一的对话
那么如何完成聊天室的功能呢;
思路:客户端的代码编写只是客户端的名称不同,其余都一样,主要是服务端如何实现收到客户端的信息,并且发送到多个客户端呢???
服务端一次只能和一个客户端完成操作之后,才能继续连接下一个客户端socket.accept()
我们可以 通过while(true)对这个socket.accept()进行循环,对每一次新的客户端进行操作,并且通过一个集合,循环保存客户端的writer操作,再对这个集合进行遍历,将客户端收到的消息,再次循环转发给所有客户端,具体代码如下
以下代码为编写服务端的代码
package day2.Thread.ChatRoom;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class Service {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket=new ServerSocket(6666);
System.out.println("服务端开启");
ArrayList<BufferedWriter> list=new ArrayList<>();
BufferedWriter bw;
BufferedReader br;
while (true){
/*一次又一次的循环连接客户端
* 当新的客户端进行连接
* 将客户端的bw操作写入集合
* 在线程类中调用这个集合,遍历里面不同的客户端
* 把read到的信息writer给客户端*/
Socket socket = serverSocket.accept();
System.out.println("a client linked");
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取字符缓冲写出流
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
list.add(bw);
new ServericeReaderAndWriter(br,bw,list).start();
}
}
}
以下为服务端读写线程类的编写方法1
package day2.Thread.ChatRoom;
import java.io.*;
import java.util.ArrayList;
public class ServericeReaderAndWriter extends Thread{
private BufferedReader br;
private BufferedWriter bw;
private ArrayList<BufferedWriter> list;
public ServericeReaderAndWriter(BufferedReader br, BufferedWriter bw, ArrayList<BufferedWriter> list) {
this.br=br;
this.bw=bw;
this.list=list;
}
@Override
public void run() {
System.out.println("run启动");
//每次新的客户端连接时,线程启动执行这个方法
String name=null;
//读取客户端接收过来的信息,先请客户端输入自己的名字
try {
bw.write("请输入客户端名称");
bw.newLine();
bw.flush();
name=br.readLine();//服务端接收客户端的名字
System.out.println(name);
//逐个发送给客户端,告诉有新用户来了
for (BufferedWriter wr : list) {//遍历已经存在与集合的客户端,将信息发给每个客户端
wr.write(name+"进入聊天室");
wr.newLine();
wr.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
while (true){
try {
String message=br.readLine();
System.out.println(name+": "+message);
//逐个发送给客户端
for (BufferedWriter writer : list) {
writer.write(name+" 说 : "+message);
writer.newLine();
writer.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
以下代码为编写三个客户端与服务端进行连接
package day2.Thread.ChatRoom;
import java.io.*;
import java.net.Socket;
public class Client1 {
public static void main(String[] args) throws IOException {
final String name="客户端1";
Socket socket=new Socket("localhost",6666);
BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("客户端1开启");
new ClientReadeThread(reader).start();
new Thread(new ClientWriterThreaf(writer)).start();
}
}
package day2.Thread.ChatRoom;
import java.io.*;
import java.net.Socket;
public class Client2 {
public static void main(String[] args) throws IOException {
final String name="客户端2";
Socket socket=new Socket("localhost",6666);
BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("客户端2开启");
new ClientReadeThread(reader).start();
new Thread(new ClientWriterThreaf(writer)).start();
}
}
package day2.Thread.ChatRoom;
import java.io.*;
import java.net.Socket;
public class Client3 {
public static void main(String[] args) throws IOException {
final String name="客户端3";
Socket socket=new Socket("localhost",6666);
BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("客户端3开启");
new ClientReadeThread(reader).start();
new Thread(new ClientWriterThreaf(writer)).start();
}
}
以下为客户端读数据线程和写数据线程代码的编写
package day2.Thread.ChatRoom;
import java.io.BufferedReader;
import java.io.IOException;
public class ClientReadeThread extends Thread{
private BufferedReader reader;
public ClientReadeThread(BufferedReader reader) {
this.reader = reader;
}
@Override
public void run() {
while (true){
try {
String s=reader.readLine();
System.out.println(s);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package day2.Thread.ChatRoom;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Scanner;
public class ClientWriterThreaf implements Runnable{
private BufferedWriter writer;
private Scanner scanner;
public ClientWriterThreaf(BufferedWriter writer) {
this.writer = writer;
this.scanner = new Scanner(System.in);
}
@Override
public void run() {
while (true){
String s = scanner.nextLine();
try {
writer.write(s);
writer.newLine();
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端的线程类或则服务端线程的编写的共性就是客户端或者服务端传入一个Socket对象,然后再在线程类中去获取输入输出流socket.inputstream,或者socket.output,也可以直接在服务端或者客户端直接获取流对象,然后再通过读写线程的有参构造传入线程类中调用
以下提供socket简要聊天室另外一种Server端线程编写和调用
package day2.Thread.ChatRoom;
import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
public class SreverScoketInput extends Thread{
private Socket socket;
private BufferedWriter bw;
private BufferedReader br;
private ArrayList<BufferedWriter> list;
public SreverScoketInput(Socket socket,ArrayList<BufferedWriter> list) throws IOException {
this.socket=socket;
this.list=list;//server 中的list赋值给这个list,通过这个list进行对writer的反复保存
this.br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
/*通过暴露一个socket 和list*/
//一个客户端连接调用一次构造方法,传入socket,和list
//调用一次run方法对list进行一次bw的增加
}
@Override
public void run() {
//ArrayList<BufferedWriter> list=new ArrayList<>();
list.add(bw);
System.out.println(list);
String name = null;
//读取客户端接收过来的信息,先请客户端输入自己的名字
try {
bw.write("请输入客户端名称");
bw.newLine();
bw.flush();
name = br.readLine();//服务端接收客户端的名字
System.out.println(name);
//逐个发送给客户端,告诉有新用户来了
for (BufferedWriter wr : list) {//遍历已经存在与集合的客户端,将信息发给每个客户端
wr.write(name + "进入聊天室");
wr.newLine();
wr.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
while (true) {
try {
String message = br.readLine();
System.out.println(name + ": " + message);
//逐个发送给客户端
for (BufferedWriter writer : list) {
writer.write(name + " 说 : " + message);
writer.newLine();
writer.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//服务端代码
package day2.Thread.ChatRoom;
import org.w3c.dom.ls.LSInput;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class Service2 {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket=new ServerSocket(6666);
System.out.println("服务开");
ArrayList<BufferedWriter> list=new ArrayList<>();//将socket.outputstream add到list中,不会被覆盖掉,
//每次线程启动,run方法运行,都是一次新的操作
//无参构造进行对象的创建也是新的操作,不会保留前一次线程的信息
System.out.println(list);
while (true){
Socket s = serverSocket.accept();
new SreverScoketInput(s, list).start();
}
}
}