1
//client::
public class Client {public static void main(String[] args) throws IOException {//多次发送数据//创建socket对象,填写服务器的ip以及端口Socket s=new Socket("127.0.0.1",10000);//获取输出流OutputStream op = s.getOutputStream();//因为要不断发送数据,所以可以使用Sacnner录入数据,结合循环将数据发出去Scanner sc=new Scanner(System.in);while (true) {//键盘录入System.out.println("请输入一个数据");String str = sc.next();//如果输入886就退出发送if ("886".equals(str)){break;}op.write(str.getBytes());}//关流s.close();}
}
//Server:
public class Server {public static void main(String[] args) throws IOException {//不断接收//创建ServerSocket对象ServerSocket ss = new ServerSocket(10000);//等待客户端来连接Socket socket = ss.accept();//创建一个输入流对象InputStream is = socket.getInputStream();//转换流:字节转字符InputStreamReader isr = new InputStreamReader(is);int b;while ((b = isr.read()) != -1) {System.out.print((char) b);}//关流socket.close();//关闭通道ss.close();//关闭服务器}
}
先启动服务器,再启动客户端
控制台:
2
//client
public class Client {public static void main(String[] args) throws IOException {//客户端:发送一条数据,接收服务端反馈的消息并打印//服务器:接收数据并打印,再给客户端反馈消息//1.创建Socket对象并连接服务端Socket socket = new Socket("127.0.0.1",10000);//2.写出数据String str = "见到你很高兴!";OutputStream os = socket.getOutputStream();os.write(str.getBytes());//写出一个结束标记,(结束输出流)socket.shutdownOutput();//3.接收服务端回写的数据InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);int b;while ((b = isr.read()) != -1){System.out.print((char)b);}//释放资源socket.close();}
}//Server
public class Server {public static void main(String[] args) throws IOException {//客户端:发送一条数据,接收服务端反馈的消息并打印//服务器:接收数据并打印,再给客户端反馈消息//1.创建对象并绑定10000端口ServerSocket ss = new ServerSocket(10000);//2.等待客户端连接Socket socket = ss.accept();//3.socket中获取输入流读取数据InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);int b;//细节://read方法会从连接通道中读取数据//但是,需要有一个结束标记,此处的循环才会停止//否则,程序就会一直停在read方法这里,等待读取下面的数据while ((b = isr.read()) != -1){System.out.println((char)b);}//4.回写数据String str = "到底有多开心?";OutputStream os = socket.getOutputStream();os.write(str.getBytes());//释放资源socket.close();ss.close();}
}
3
下面的例子是传输图片,所以使用字节流
//Client
public class Client {public static void main(String[] args) throws IOException {//创建Socket对象连接服务器Socket s = new Socket("127.0.0.1", 10000);//读取图片文件:使用字节流//图片较大:使用缓冲流BufferedInputStream bis = new BufferedInputStream(new FileInputStream("..\\netcode\\clientdir\\a.jpg"));//在通道中获取字节流,输入数据到服务端BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());//边读边写byte[] bytes = new byte[1024];int len;while ((len = bis.read(bytes)) != -1) {bos.write(bytes, 0, len);}bos.flush();//结束标识s.shutdownOutput();//接受服务端回馈:字节流-->字符流---->缓冲流BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));int b;while((b=br.read())!=-1){System.out.print((char) b);}//关流s.close();}
}//server
public class Server {public static void main(String[] args) throws IOException {//创建ServerSocket对象ServerSocket ss = new ServerSocket(10000);//等待客户端连接Socket socket = ss.accept();//获取通道中传来的数据//字节流读取字节//缓冲流提升效率BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());//输出到本地BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("..\\netcode\\serverdir\\a.jpg"));byte[] bytes = new byte[1024];int len;while ((len = bis.read(bytes)) != -1) {bos.write(bytes, 0, len);}bos.flush();//返回信息:字节流-->字符流---->缓冲流BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write("上传成功");bw.newLine();bw.flush();//关流socket.close();ss.close();}}
4.
//client
public class Client {public static void main(String[] args) throws IOException {//创建Socket对象连接服务器Socket s = new Socket("127.0.0.1", 10000);//读取图片文件:使用字节流//图片较大:使用缓冲流BufferedInputStream bis = new BufferedInputStream(new FileInputStream("..\\netcode\\clientdir\\a.jpg"));//在通道中获取字节流,输入数据到服务端BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());//边读边写byte[] bytes = new byte[1024];int len;while ((len = bis.read(bytes)) != -1) {bos.write(bytes, 0, len);}bos.flush();//结束标识s.shutdownOutput();//接受服务端回馈:字节流-->字符流---->缓冲流BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));int b;while((b=br.read())!=-1){System.out.print((char) b);}//关流s.close();}
}//Server
public class Server {public static void main(String[] args) throws IOException {//创建ServerSocket对象ServerSocket ss = new ServerSocket(10000);//等待客户端连接Socket socket = ss.accept();//获取通道中传来的数据//字节流读取字节//缓冲流提升效率BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());//随机uuid、String str = UUID.randomUUID().toString().replace("-", "");//输出到本地BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("..\\netcode\\serverdir\\" + str + ".jpg"));byte[] bytes = new byte[1024];int len;while ((len = bis.read(bytes)) != -1) {bos.write(bytes, 0, len);}bos.flush();//返回信息:字节流-->字符流---->缓冲流BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write("上传成功");bw.newLine();bw.flush();//关流socket.close();ss.close();}}
5.
、
服务端不停止,用户端一直传
思路:可以在服务端使用循环嵌套:
如下:
public class Server {public static void main(String[] args) throws IOException {//创建ServerSocket对象ServerSocket ss = new ServerSocket(10000);while (true) {//等待客户端连接Socket socket = ss.accept();//获取通道中传来的数据//字节流读取字节//缓冲流提升效率BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());//随机uuid、String str = UUID.randomUUID().toString().replace("-", "");//输出到本地BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("..\\netcode\\serverdir\\" + str + ".jpg"));byte[] bytes = new byte[1024];int len;while ((len = bis.read(bytes)) != -1) {bos.write(bytes, 0, len);//当第一个用户还在传输时,服务端代码还会在这不断循环}bos.flush();//返回信息:字节流-->字符流---->缓冲流BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write("上传成功");bw.newLine();bw.flush();//关流socket.close();//断开当前通道//ss.close();//关闭服务器 ----这里不可以让服务器关闭}}
}
但是循环有弊端,它是一种单线程,如果说此时要传输的文件很大,当第一个用户还在传输时,服务端代码还会停止在第25行,此时就无法和第二个用户产生连接(只有完成一次循环后,才能和下一个用户进行数据连接),所以我们可以用多线程来改进。使多个用户可以同时传输数据,服务端也可以同时读取多个用户的数据
//多线程
public class MyThread extends Thread{Socket socket;public MyThread (Socket socket){this.socket=socket;}@Overridepublic void run() {//获取通道中传来的数据//字节流读取字节//缓冲流提升效率try {BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());//随机uuid、String str = UUID.randomUUID().toString().replace("-", "");//输出到本地BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("..\\netcode\\serverdir\\" + str + ".jpg"));byte[] bytes = new byte[1024];int len;while ((len = bis.read(bytes)) != -1) {bos.write(bytes, 0, len);}bos.flush();//返回信息:字节流-->字符流---->缓冲流BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write("上传成功");bw.newLine();bw.flush();} catch (IOException e) {throw new RuntimeException(e);}finally {//5.释放资源if (socket!=null){try {socket.close();} catch (IOException e) {e.printStackTrace();}}}}
}//Server
public class Server {public static void main(String[] args) throws IOException {//创建ServerSocket对象ServerSocket ss = new ServerSocket(10000);while (true) {//等待客户端连接Socket socket = ss.accept();new MyThread(socket).start();//ss.close();}}
}//client....
6.
使用自定义线程池
//Server
public class Server {public static void main(String[] args) throws IOException {//1创建线程池对象ThreadPoolExecutor pool = new ThreadPoolExecutor(3,//核心线程数量16,//线程池总大小60,//空闲时间TimeUnit.SECONDS,//单位new ArrayBlockingQueue<>(2),//队列Executors.defaultThreadFactory(),//线程工厂,让线程池如何创建线程对象new ThreadPoolExecutor.AbortPolicy()//拒绝策略);//2创建ServerSocket对象ServerSocket ss = new ServerSocket(10000);while (true) {//3等待客户端连接Socket socket = ss.accept();//开启一条线程//一个用户就对应服务端的一条线程//调用submit方法传入myRunnable对象pool.submit((new MyRunnable(socket)));//ss.close();}}
}
//MyRunnable
public class MyRunnable implements Runnable {Socket socket;public MyRunnable(Socket socket){this.socket=socket;}@Overridepublic void run() {//获取通道中传来的数据//字节流读取字节//缓冲流提升效率try {BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());//随机uuid、String str = UUID.randomUUID().toString().replace("-", "");//输出到本地BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("..\\netcode\\serverdir\\" + str + ".jpg"));byte[] bytes = new byte[1024];int len;while ((len = bis.read(bytes)) != -1) {bos.write(bytes, 0, len);}bos.flush();//返回信息:字节流-->字符流---->缓冲流BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write("上传成功");bw.newLine();bw.flush();} catch (IOException e) {throw new RuntimeException(e);}finally {//5.释放资源if (socket!=null){try {socket.close();} catch (IOException e) {e.printStackTrace();}}}}
}
//client.....
7.
public class Server {public static void main(String[] args) throws IOException {//客户端:多次发送数据//服务器:接收多次接收数据,并打印//1.创建对象绑定10000端口ServerSocket ss = new ServerSocket(10000);//2.等待客户端来连接Socket socket = ss.accept();//3.读取数据InputStreamReader isr = new InputStreamReader(socket.getInputStream());int b;while ((b = isr.read()) != -1){System.out.print((char)b);}//4.释放资源socket.close();ss.close();}
}
首先启动服务端:
再在浏览器输入:
获得到数据:
8.
client
package A;import java.io.*;
import java.net.Socket;
import java.util.Scanner;public class Client1 {public static void main(String[] args) throws IOException {/*** 每一个客户端都是一个线程*/while (true) {//1.与服务端建立连接Socket socket = new Socket("127.0.0.1", 10000);System.out.println("服务器已连接");//2生成聊天界面System.out.println("==============欢迎来到黑马聊天室================");System.out.println("1登录");System.out.println("2注册");System.out.println("请输入您的选择:");//3键盘录入Scanner sc = new Scanner(System.in);String choose = sc.nextLine();//判断:switch (choose) {case "1"://登录逻辑login(socket);break;case "2"://注册逻辑break;default:System.out.println("没有该选项");}}}/*** //登录** @param socket* @throws IOException*/private static void login(Socket socket) throws IOException {//1输入账户和密码Scanner sc = new Scanner(System.in);System.out.println("请输入用户名");String username = sc.nextLine();System.out.println("请输入密码");String password = sc.nextLine();//***登录要求使用username=zhangsan&password=123这种格式发给服务端//2拼接StringBuilder sb = new StringBuilder();sb.append("username=").append(username).append("&password=").append(password);//3提交给服务器验证BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));//第一次是告诉服务器是此时登陆操作是什么bw.write("login");bw.newLine();//这两个要配套使用bw.flush();//第二次告诉服务器用户端输入的账号密码bw.write(sb.toString());bw.newLine();bw.flush();//接收服务端的回馈消息BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));String message = br.readLine();if ("1".equals(message)) {System.out.println("登陆成功,可以开始聊天");//开一条单独的线程,专门用来接收服务端转发过来的聊天记录new Thread(new ClientMyRunnable(socket)).start();//将要说的话传给服务器,交给服务器转发给其他客户端talk2All(bw);} else if ("2".equals(message)) {System.out.println("密码不正确");} else {System.out.println("用户名不存在,请先注册");}}private static void talk2All(BufferedWriter bw) throws IOException {Scanner sc = new Scanner(System.in);while (true) {System.out.println("请输入你要说的话");String message = sc.nextLine();bw.write(message);bw.newLine();bw.flush();}}
}class ClientMyRunnable implements Runnable {Socket socket;//构造public ClientMyRunnable(Socket socket) {this.socket = socket;}@Overridepublic void run() {//循环包裹,不断读取服务端发过来的信息(接受群发消息)while (true) {try {BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));String message = br.readLine();System.out.println(message);} catch (IOException e) {throw new RuntimeException(e);}}}
}
server
package A;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.Buffer;
import java.nio.file.attribute.UserPrincipal;
import java.util.ArrayList;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class Server {//成员位置方便调用static ArrayList<Socket>list=new ArrayList<>();public static void main(String[] args) throws IOException {//1创建ServerSocket对象,并连接10000端口ServerSocket ss = new ServerSocket(10000);//2把本地文件中的正确用户名和密码都获取到Properties prop = new Properties();FileInputStream fis = new FileInputStream("..\\Chat\\account.txt");prop.load(fis);fis.close();//只要来了一个客户端,就开一个条线程while (true) {//等待服务端连接Socket socket = ss.accept();System.out.println("有客户端来连接");//开始处理线程任务new Thread(new MyRunnable(socket, prop)).start();}}
}//-----------------------------------------------------------------------
class MyRunnable implements Runnable {Socket socket;Properties prop;//构造public MyRunnable(Socket socket, Properties prop) {this.socket = socket;this.prop = prop;}@Overridepublic void run() {/*** 每一个线程要做的事情*/try {//1读取客户端传来的信息BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));//第一次读取的是用户的操作String choose = br.readLine();while (true) {switch (choose) {case "login" -> login(br);case "register" -> register();}}} catch (IOException e) {throw new RuntimeException(e);}}/*** 1.获取用户端输入的帐号、密码* 2.与正确的账号密码比较* 3.写出不同情况的返回信息*/public void login(BufferedReader br) throws IOException {System.out.println("用户执行了登陆操作");//第二次读取的是用户端传递过来的拼接信息:username=zhangsan&password=123String userInfo = br.readLine();//获取真正的账号密码:切割String s1 = userInfo.split("&")[0];String s2 = userInfo.split("&")[1];String usernameInput = s1.split("=")[1];String passwordInput = s2.split("=")[1];System.out.println("账号是:" + usernameInput);System.out.println("密码是:" + passwordInput);//比较if (prop.containsKey(usernameInput)) {//用户名一致,就比较密码//先获取当前正确密码String rellyPassword = (String) prop.get(usernameInput);if (passwordInput.equals(rellyPassword)) {//登陆成功,给用户端返回信息messageToClient("1");//登陆成功,可以开始聊天//登陆成功后需要将当前socket对象存储起来Server.list.add(socket);//接收客户端发送的信息并打印在控制台talk2All(br,usernameInput);} else {//密码不正确,给用户端返回信息messageToClient("2");//密码不正确}} else {//用户名不存在,给用户端返回信息:messageToClient("3");//用户名不存在,请先注册}}private void talk2All(BufferedReader br, String usernameInput) throws IOException {//聊天死循环while(true){//接收客户端发送的信息String message = br.readLine();System.out.println(usernameInput+"发送过来了消息:"+message);//群发for (Socket s : Server.list) {//s以此表示每一个socket对象messageToClient(s,usernameInput+"发送过来了消息:"+message);}}}/***因为三种情况:登陆成功、密码不正确、用户名不存在都要返回信息给客户端,所以干脆抽取成方法* @param message*/public void messageToClient(String message) throws IOException {//获取输出流BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write(message);bw.newLine();bw.flush();}/*** 重载的messageToClient* @param socket* @param message*/public void messageToClient(Socket socket,String message) throws IOException {//获取输出流,将数据写给当前的socket通道对象BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write(message);bw.newLine();bw.flush();}/*** 注册逻辑*/private static void register() {System.out.println("用户执行了注册操作");}}