目标,做一个简单的服务器与客户端的交互,并实现登录注册。
这个需要进行网络通信,用到ip和端口号
ip用来定位电脑的位置上,而端口号则用来定位软件在电脑上的位置。
首先创建一个服务器
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
//声明一个服务器变量
ServerSocket ss=null;
//声明一个套接字用来接收客户端对象
Socket s=null;
try {
//创建服务器对象,并设置端口号8989,服务器地址则是本地服务器,并把服务器对象赋给变量。
ss=new ServerSocket(8989);
//用while循环可以接收多个客户端
while(true) {
System.out.println("等待接收客户端");
s=ss.accept();//服务器会一直运行并在此处等待,直到有客户端连接。
//getInetAddress()这个函数用来获取客户端的一些信息,其中包括ip地址以及客户端的名字。getHostAddress()获取到地址,getHostName()获取到名字。
System.out.println("接收到的客户端ip为:"+s.getInetAddress().getHostAddress()+"客户端名字为:"+s.getInetAddress().getHostName());//在服务器端显示客户端的ip与名字。
//创建服务器线程,让线程去应付客户端
ServerThread st=new ServerThread(s);
st.start();//线程启动
//服务器线程启动之后,本类则继续等待下一个客户端
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//本类服务器接口的作用是接收客户端、创建线程。
服务器真正的工作线程如下:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class ServerThread extends Thread {
Socket s = null;//客户端属性,由前台服务器传入
BufferedReader br = null;//字符缓冲流
BufferedWriter bw = null;//字符缓冲流
public ServerThread(Socket s) {
//前台服务器在创建线程时把客户端对象传入,然后在此处赋值给客户端属性
super();
this.s = s;
}
@Override
public void run() {
try {
//getInputStream(),一个接收客户端消息的字节流
//InputStreamReader 将字节流转换为字符流
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//getOutputStream(),一个传值给客户端的字节流
bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
while (true) {
String str = br.readLine();//服务器在此处等待客户端传值进入,(设置客户端在此处传值有三种,zhuce,denglu,tuichu,然后让它分别进入注册、登录和退出)
System.out.println("线程开始选择登录或者注册"+str);
switch (str) {
case "zhuce":
System.out.println("已进入注册");
useZhuce();
System.out.println("已完成注册");
break;
case "denglu":
System.out.println("已进入登录");
useDenglu();
System.out.println("已完成登录");
break;
case "tuichu":
return;
default:
System.out.println("可能是接收错误");;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
bw.close();
br.close();
s.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//此函数调用后台类的注册方法,并启动注册线程与客户端交互完成注册
public synchronized void useZhuce() {
ZhuceServer zs = new ZhuceServer(s, br, bw);
zs.start();
try {
//这里使用线程的join方法,就是让主线程在此等待,让注册线程和客户端交互完毕才继续向下运行,否则主线程与子线程的接收信息方法会重叠然后导致程序运行错误。
zs.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//此函数调用后台类的登录方法,并启动登录线程与客户端交互完成登录
public synchronized void useDenglu() {
DengluServer ds = new DengluServer(s, br, bw);
ds.start();
try {
//此处和注册一样
ds.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//用来和客户端交互、根据客户端需要调用注册或者登录线程
配置文件内部的信息存储方式
等号前面是key值,等号后面是value值
后台数据处理类
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class UsePropery {
//这里使用静态常量方法,设置固定的文件路径(配置文件路径)
public static final String PATH="F:/openfile/com/xitong/user.properties";
public static boolean doZhuce(String key,String value) {
//创建一个配置文件类
Properties pro=new Properties();
try {
//load()方法是用来浏览配置文件,把配置文件里的信息放入Pro中,Pro存储信息的方式则是和map一样信息以key-value方式存储
pro.load(new FileInputStream(PATH));
if (pro.containsKey(key)) {//用containskey的方法查看Pro里面是否已经有该key值,如果有就代表已经注册过了(假设注册的诸多信息中id必须唯一,如果没有则可以注册)
return false;
}else {
//设置准备好写入的注册信息,注册信息以key-value值的方式写入
pro.setProperty(key, value);
//store()方法则是把设置好的key-value值添加到文件中,参数中第一个是一个文件流,后一个是标记信息(没啥大用)
pro.store(new FileOutputStream(PATH), "user");
return true;//注册成功,过程中没有异常则返回true
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
public static boolean doDenglu(String useInfor) {
//此时的useInfor应该为:id:pwd;
Properties pro=new Properties();
try {
//登录主要做的操作是吧客户端传来的id:password账号密码与配置文件中的注册信息进行对比,如果有一样的则登录成功,没有则登录失败
pro.load(new FileInputStream(PATH));
//split字符串切割函数
String id=useInfor.split(":")[0];
String pwd=useInfor.split(":")[1];
if(pro.containsKey(id)) {
String str=pro.getProperty(id);
//{id:id,name:name,password:password,age:age,score:score}
if(pwd.equals(str.split(",")[2].split(":")[1])) {
return true;
}else {
System.out.println("密码不正确");
}
}else {
System.out.println("没有该用户");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
}
//功能:把服务器传来的登录注册信息进行处理、保存和读取配置文件。
当用户注册时,前台服务器调用的注册服务线程
public class ZhuceServer extends Thread{
Socket s=null;
BufferedReader br=null;
BufferedWriter bw=null;
public ZhuceServer(Socket s,BufferedReader br,BufferedWriter bw) {
super();
this.s = s;
this.br=br;
this.bw=bw;
}
@Override
public void run() {
try {
String str=br.readLine();//接收客户端传来的注册信息,信息的形式为//{id:id,name:name,password:password,age:age,score:score}
String key=str.substring(1, str.length()-1).split(",")[0].split(":")[1];
//把key和注册信息传给后台注册函数,得到注册的成功与失败的结果
boolean ifZhuce=UsePropery.doZhuce(key, str);
//注册成功则给客户端发送注册成功的消息,否则注册失败消息。
if(ifZhuce) {
bw.write("注册成功");
bw.newLine();
bw.flush();
}else {
bw.write("注册失败");
bw.newLine();
bw.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//功能:接收客户端的注册信息、调用后台注册
当用户登录时,前台服务器调用的登录服务线程
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.Socket;
public class DengluServer extends Thread {
Socket s=null;
BufferedReader br=null;
BufferedWriter bw=null;
public DengluServer(Socket s,BufferedReader br,BufferedWriter bw) {
super();
this.s = s;
this.br=br;
this.bw=bw;
}
@Override
public void run() {
try {
String str = br.readLine();
//此时的数据为id:pwd
boolean ifDenglu=UsePropery.doDenglu(str);
if(ifDenglu) {
bw.write("登录成功");
bw.newLine();
bw.flush();
}else {
bw.write("登录失败");
bw.newLine();
bw.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
客户端
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
Socket s=null;
BufferedReader br=null;
BufferedWriter bw=null;
try {
//创建客户端套接字,参数为前一个是服务器地址,也可以写ip,后一个为服务器端口号,得与服务器相同。
s=new Socket("localhost",8989);
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//以下就是简单的人机交互实现
while(true) {
System.out.println("-----欢迎来到kaiven的私人注册登录系统-----");
System.out.println("请选择你需要的操作");
System.out.println("1、注册 2、登录 3、退出");
int choose=input.nextInt();
System.out.println("你输入的选择是:"+choose);
switch (choose) {
case 1:
bw.write("zhuce");
bw.newLine();
bw.flush();
String str=zhuce();
bw.write(str);
bw.newLine();
bw.flush();
String str1=br.readLine();
System.out.println("服务器发来注册信息为:"+str1);
break;
case 2:
bw.write("denglu");
bw.newLine();
bw.flush();
String str2=denglu();
bw.write(str2);
bw.newLine();
bw.flush();
String str3=br.readLine();
System.out.println("服务器发来登录信息为:"+str3);
break;
case 3:
bw.write("tuichu");
return ;
default:
System.out.println("没有该选项,如果要退出请输入3");
break;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
bw.close();
br.close();
s.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static String zhuce() {
Scanner input=new Scanner(System.in);
System.out.println("请输入id");
String id=input.next();
System.out.println("请输入姓名");
String name=input.next();
System.out.println("请输入密码");
String password=input.next();
System.out.println("请输入年龄");
int age=input.nextInt();
System.out.println("请输入分数");
double score=input.nextDouble();
Student st=new Student(id, name, password, age, score);
return st.toString();
}
public static String denglu() {
Scanner input=new Scanner(System.in);
System.out.println("请输入id");
String id=input.next();
System.out.println("请输入密码");
String password=input.next();
String useInfo=id+":"+password;
return useInfo;
}
}
在注册的时候为了方便调用了一个学生类用来传值
public class Student {
private String id;
private String name;
private String password;
private int age;
private double score;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public Student(String id, String name, String password, int age, double score) {
super();
this.id = id;
this.name = name;
this.password = password;
this.age = age;
this.score = score;
}
public Student() {
super();
}
@Override
public String toString() {
return "{id:" + id + ", name:" + name + ", password:" + password + ", age:" + age + ", score:" + score
+ "}";
}
}
如果出现端口被占用可以更换端口数字
在启动客户端时,可以多创建几个,服务器交互都是调用的线程,互不干扰。
登录注册系统就此完结。