2019独角兽企业重金招聘Python工程师标准>>>
这几天看书看得java网络编程,看到一个不错的,适合新手的,部分代码借鉴书上的,可能有地方还不是很成熟,不过可以借鉴一下,分为客户端和服务端,话不多说,贴代码,很多都在注释中给出
客户端程序:
YeekuProtocol类,是一个帮助类,用来定义协议字符串 /**
*
*<description>用来定义的协议字符</description>
* @author Administrator
* @date 2013-11-26
* @file CrazyitProtocol.java
* @category com.inspur.net.MultiThread.chat.server
* @version 1.0
*
*/
public interface YeekuProtocol {
//定义协议字符串长度为2
int PROROCLO_LEN = 2;
//下面是一些协议字符串,服务器和客户端交换的信息
//都应该在前、后添加这种特殊字符串。
String MSG_ROUND = "§γ";
String USER_ROUND = "∏∑";
String LOGIN_SUCCESS = "1";
String NAME_REP = "-1";
String PRIVATE_ROUND = "★【";
String SPLIT_SIGN = "※";
}
clientThread线程类是客户端处理输入流的几个类
import java.io.BufferedReader;
import java.io.IOException;
/**
*<description>客户端增加了让用户输入用户名的代码,并且不允许用户名重复
*除此之外,还可以根据用户的键盘输入来判断用户是否想发送私聊信息</description>
* @author Administrator
* @date 2013-11-26
* @file clientThread.java
* @category com.inspur.net.MultiThread.chat.client
* @version 1.0
*
*/
public class clientThread extends Thread{
//该线程负责处理输入流
BufferedReader br = null;
public clientThread(BufferedReader br){
this.br = br;
}
public void run(){
//不断从输入流读取数据,并且打印输出
String line = null;
try {
while((line=br.readLine())!=null){
System.out.println(line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(br!=null){
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
client类,主要负责客户端的一些操作
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JOptionPane;
import com.inspur.net.MultiThread.chat.interfacefolder.YeekuProtocol;
/**
*<description>客户端增加了让用户输入用户名的代码,并且不允许用户名重复
*除此之外,还可以根据用户的键盘输入来判断用户是否想发送私聊信息</description>
* @author Administrator
* @date 2013-11-26
* @file client.java
* @category com.inspur.net.MultiThread.chat.client
* @version 1.0
*
*/
public class Client {
private static final int SERVER_PORT = 40001;
private static final String ip = "192.168.1.123";
private Socket socket;
private PrintStream ps ;
private BufferedReader brServer;
private BufferedReader keyIn;
public void init(){
keyIn = new BufferedReader(new InputStreamReader(System.in));
try {
socket = new Socket(ip,SERVER_PORT);
ps = new PrintStream(socket.getOutputStream());
brServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String tip = "";
//采用循环不断弹出对话框要求输入用户名
while(true){
String userName = JOptionPane.showInputDialog(tip+"输入用户名");
//将用户名前后增加协议字符串
ps.println(YeekuProtocol.USER_ROUND+userName+YeekuProtocol.USER_ROUND);
//读取服务器的相应
String read = brServer.readLine();
//如果用户重复,开始下一次循环
if(read.equals(YeekuProtocol.NAME_REP)){
tip = "用户名重复,请刷新";
continue;
}
//如果服务器返回成功,结束循环
if(read.equals(YeekuProtocol.LOGIN_SUCCESS)){
break;
}
}
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
System.out.println("找不到远程服务器,确认服务已经启动");
closeRs();
System.exit(1);
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("网络异常,请重新登录");
e.printStackTrace();
}
new Thread(new clientThread(brServer)).run();
}
//定义一个从键盘读入,并向网络发送的方法
public void readAndSend(){
//不断从键盘读入
String line = null;
try {
while((line=keyIn.readLine())!=null){
if(line.indexOf(":")>0 && line.startsWith("//")){//如果以//开头,则默认为私聊信息,后面必须输入要私聊的对象
line = line.substring(2);
ps.println(YeekuProtocol.PRIVATE_ROUND+line.split(":")[0]+YeekuProtocol.SPLIT_SIGN
+line.split(":")[1]+YeekuProtocol.PRIVATE_ROUND);
}
else{
ps.println(YeekuProtocol.MSG_ROUND+line+YeekuProtocol.MSG_ROUND);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void closeRs(){//关闭释放IO资源
try {
if(keyIn!=null){
keyIn.close();
}
if(brServer!=null){
brServer.close();
}
if(ps!=null){
ps.close();
}
if(socket!=null){
socket.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {//程序入口
Client client = new Client();
client.init();
client.readAndSend();
}
}
服务器端:服务器端主要有YeekuMap类和server,serverThred类
YeekuMap类是服务端用来存储用户名和输入输出流之间的映射关系,这样服务端就可以根据用户名来寻找相对应的socket,实现私密通信,利用map这一数据结构存储
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
/**
*
*<description>服务器端用来存储用户名和输入输出流之间的映射关系,这样服务器就可以根据用户名来
*寻找相应的socket,实现客户端之间的通信,服务器只要获取gia用户名对应的输出流即可,提供的这个HashMap类
*可以根据value获取key值,或者是通过value删除key值,而且不允许value的重复</description>
* @author Administrator
* @date 2013-11-26
* @file YeekuMap.java
* @category com.inspur.net.MultiThread.chat.server
* @version 1.0
*
*/
public class YeekuMap<K,V> extends HashMap<K,V>{
/**
*
*/
private static final long serialVersionUID = 1L;
//根据value值来删除指定项
public void remonveByValue(Object value){
for(Object key : keySet()){
if(get(key)==value){
remove(key);
break;
}
}
}
//获取所有value组成的集合
public Set<V> valueSet(){
Set<V> result = new HashSet<V>();
for(K key : keySet()){
//将每个key对应的value添加到result集合中
result.add(get(key));
}
return result;
}
//通过value查找key
public K getKeyByValue(V val){
//便利所有key组成的集合
for(K key : keySet()){
//如果指定key对应的value与被搜索的value相同
//则返回该key的值
if(get(key).equals(val) &&get(key)==val){
return key;
}
}
return null;
}
//重写HashMap的put方法,该方法允许value的重复
public V put(K key , V value){
for(V val : valueSet()){
//如果指定的value与试图放入集合的value相同
//则抛出一个RuntimeException
if(val == get(key) && val.equals(get(key))){
throw new RuntimeException("MyMap实例中不允许重复");
}
}
return super.put(key, value);
}
}
server类,
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
*
*<description>服务器端程序,建立ServerSocket监听客户端,增加异常处理</description>
* @author Administrator
* @date 2013-11-26
* @file Server.java
* @category com.inspur.net.MultiThread.chat.server
* @version 1.0
*
*/
public class Server {
//监听端口
private static final int SERVER_PORT = 40001;
//使用MyMap对象来存储每个客户名字和对应的输出流之间的关系
public static YeekuMap<String ,PrintStream> clients = new YeekuMap<String,PrintStream>();
private Socket socket;
public void init(){
try {
ServerSocket serverSocket = new ServerSocket(SERVER_PORT);
while(true){
socket = serverSocket.accept();
new Thread(new ServerThread(socket)).start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("服务器已经启动,是否端口"+SERVER_PORT+"被占用");
e.printStackTrace();
}finally{
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.exit(1);
}
}
}
public static void main(String[] args) {
Server server = new Server();
server.init();
}
}
serverThraead类
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import com.inspur.net.MultiThread.chat.interfacefolder.YeekuProtocol;
/**
*
*<description>服务器端程序,处理客户端请求的实体类</description>
* @author Administrator
* @date 2013-11-26
* @file ServerThread.java
* @category com.inspur.net.MultiThread.chat.server
* @version 1.0
*
*/
public class ServerThread implements Runnable{
private Socket socket;
private BufferedReader br = null;
private PrintStream ps = null;
public ServerThread(Socket socket){
this.socket = socket;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
ps = new PrintStream(socket.getOutputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run() {
// TODO Auto-generated method stub
String line = null;
try {
while((line=br.readLine())!=null){
//如果读到以MyProtocol.USER ROUND开始和结束
//可以确定读到的是用户的用户名
if(line.startsWith(YeekuProtocol.USER_ROUND) && line.endsWith(YeekuProtocol.USER_ROUND)){
String userName = getRealMsg(line);
if(Server.clients.containsKey(userName)){
System.out.println("重复");
ps.println(YeekuProtocol.NAME_REP);
}else{
System.out.println("成功");
ps.println(YeekuProtocol.LOGIN_SUCCESS);
Server.clients.put(userName, ps);
}
}
//如果读到的行以YeekuProtocol.PRIVATE_ROUND开始结束
//可以确定是私聊信息,私聊信息都以特定的流输出
else if(line.startsWith(YeekuProtocol.PRIVATE_ROUND) && line.endsWith(YeekuProtocol.PRIVATE_ROUND)){
//得到真实信息
String userAndMsg = getRealMsg(line);
String User = userAndMsg.split(YeekuProtocol.SPLIT_SIGN)[0];
String Msg = userAndMsg.split(YeekuProtocol.SPLIT_SIGN)[1];
//获取私聊用户的输出流,并发送私聊信息
Server.clients.get(User).println(
Server.clients.getKeyByValue(ps)+"悄悄对你说:"+Msg);
}
//公聊的话向每个socket都发送
else{
//得到真实信息
String msg = getRealMsg(line);
//便利clients的每个输出流
for(PrintStream clientsPs : Server.clients.valueSet()){
clientsPs.println(Server.clients.getKeyByValue(ps)+"说"+msg);
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
Server.clients.remonveByValue(ps);
System.out.println(Server.clients.size());
//关闭io资源
try {
if(br != null){
br.close();
}
if(ps != null){
ps.close();
}
if(socket !=null){
socket.close();
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
}
}
//将读到的内容去掉前后的字符串,恢复成真实数据
public String getRealMsg(String line){
return line.substring(YeekuProtocol.PROROCLO_LEN, line.length()-YeekuProtocol.PROROCLO_LEN);
}
}
以上就是这个简单实现,我测试的时候时将他们导出成jar文件,然后再cmd窗口运行jar文件,此处在