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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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=123
String 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("用户执行了注册操作");
}
}