Socket类的使用

目标,做一个简单的服务器与客户端的交互,并实现登录注册。

这个需要进行网络通信,用到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();
		}

	}
}

//用来和客户端交互、根据客户端需要调用注册或者登录线程

配置文件内部的信息存储方式Socket类的使用
等号前面是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
				+ "}";
	}
	
}

如果出现端口被占用可以更换端口数字
在启动客户端时,可以多创建几个,服务器交互都是调用的线程,互不干扰。
登录注册系统就此完结。

上一篇:聊聊我理解的ANSI C、ISO C、GNU C、POSIX C


下一篇:缓冲字符流