一个简单的基于socket的通讯处理程序

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

这几天看书看得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文件,此处在

MANIFEST.MF文件中应该加上一句Main-Class: 包名+类名,注意:后面必须有个空格,而且在结束之后要以换行结束,shift+enter,可能也有失误的地方,本人新手,勿喷

 

转载于:https://my.oschina.net/u/1034481/blog/179457

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461831.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++远航之封装篇——构造函数

1、为什么需要构造函数&#xff1f; 见博客http://blog.csdn.net/zhhymh/article/details/6236317 2、c中的内存分区 &#xff08;1&#xff09;栈区 int x0&#xff1b;int *pNULL&#xff1b; &#xff08;2&#xff09;堆区 int *p new int[20]; &#xff08;3&#xff0…

安装win_server_2012的方法

1、从微软官网下载评估版。 2、查看你的当前版本。以管理员身份运行cmd&#xff0c;然后输入“DISM /online /Get-CurrentEdition”。如果是评估版&#xff0c;例如Standard&#xff0c;把“ServerStandardEval”中的Eval这四个字母去掉&#xff0c;就是你的当前版本。下图表明…

DHCP中继处理办法

这两天一直在客户这边测试DHCP&#xff0c;由于客户的网络是现成的server 2008 是后来加上去的&#xff0c;所以没有多的IP地址用于测试&#xff0c;只好拿客户的楼层网段来测试&#xff0c;由于需要跨VLAN实行DHCP地址分配&#xff0c;所有需要做DHCP中继。废话不多说&#xf…

C++远航之封装篇——默认构造函数、初始化列表、拷贝构造函数

1、默认构造函数 没有参数&#xff1b;若有参数&#xff0c;则一定全部都有默认的参数值。 2、初始化列表 &#xff08;1&#xff09;概念 &#xff08;2&#xff09;特性 建议用初始化列表来初始化数据成员。初始化列表先于构造函数执行&#xff1b;初始化列表只能用于构造函…

C++远航之封装篇——析构函数

一、为什么需要析构函数&#xff1f; 见博客&#xff1a;http://blog.csdn.net/zhhymh/article/details/6239832 二、析构函数的理解 1、格式 2、必要性 释放资源。 3、什么时候调用&#xff1f; delet p时会调用析构函数&#xff1b;程序返回时&#xff0c;也会调用析构函数…

生成的数据库脚本没有注释?

1,选择Database->Generate Database 选中Generate name in empty comment就可以了转载于:https://www.cnblogs.com/hongjiumu/archive/2012/08/02/2620379.html

HDU 1312

View Code 1 #include<stdio.h>2 #include<stdlib.h>3 #include<string.h>4 以前用BFS写过&#xff0c;这次改用DFS写发现DFS掌握的不好&#xff0c;挑了好久才过&#xff0c;今下午才AC了两道题&#xff0c;还要加紧练习DFS&#xff01;5 int row,col,coun…

C++远征之封装篇——对象数组,对象成员

一、对象数组 &#xff08;1&#xff09;如下&#xff0c;delet [ ]p时&#xff0c;会调用三次析构函数 &#xff08;2&#xff09;细节 实例化对象数组时&#xff0c;每一个对象的构造函数都会被执行。销毁对象数组时&#xff0c;每一个对象的析构函数都会被执行。 二、对象成…

ehcache memcache redis 三大缓存男高音

2019独角兽企业重金招聘Python工程师标准>>> 最近项目组有用到这三个缓存&#xff0c;去各自的官方看了下,觉得还真的各有千秋&#xff01;今天特意归纳下各个缓存的优缺点&#xff0c;仅供参考&#xff01; Ehcache 在java项目广泛的使用。它是一个开源的、设计于…

C++远航之封装篇——深拷贝、浅拷贝

1、浅拷贝 如下&#xff0c;此时如果同一块内存释放两次&#xff0c;会报错 2、深拷贝

C++远航之封装篇——对象指针、this指针

1、使用new会调用构造函数&#xff0c;使用malloc不会使用构造函数。 2、加不加括号都行&#xff1f;YES 3、对象成员指针&#xff08;指针指向对象&#xff0c;而这对象是数据成员&#xff09; 4、this指针 下面这个没有歧义&#xff1a; 而下面这个有歧义&#xff1a; 为此&a…

poj 1274 The Perfect Stall

2019独角兽企业重金招聘Python工程师标准>>> The Perfect Stall Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 16888 Accepted: 7721 Description Farmer John completed his new barn just last week, complete with all the latest milkin…

C++远征之封装篇——常对象成员、常成员函数

一、常对象成员 二、常成员函数 &#xff08;1&#xff09;常成员函数中不能修改数据成员的值 &#xff08;2&#xff09;不能修改的原因 每个函数都默认带了一个this指针的&#xff0c;该指针指向该类的实例。当使用const来修饰时&#xff0c;其实是const Coordinate* this。这…

PHP建站环境搭建:汇总网上常见的1键安装包

PHP的程序很火&#xff0c;像博客&#xff1a;Wordpress、论坛&#xff1a;Discuz!、SNS&#xff1a;UCenter Home、CMS&#xff1a;DedeCMS等等都是比较流行的PHP程序&#xff0c;大家都在使用PHP类的程序&#xff0c;但对于很多初学者来说&#xff0c;直接上传到虚拟空间上一…

最长公共子序列|最长公共子串|最长重复子串|最长不重复子串|最长回文子串|最长递增子序列|最大子数组和...

最长公共子序列|最长公共子串|最长重复子串|最长不重复子串|最长回文子串|最长递增子序列|最大子数组和文章作者&#xff1a;Yx.Ac 文章来源&#xff1a;勇幸|Thinking (http://www.ahathinking.com) 转载请注明&#xff0c;谢谢合作。 以后新博客写的文章挑一些粘过来&#…

JEECMS编辑漏洞及随便拿webshell,啊哈哈

JEECMS是基于java技术开发&#xff0c;继承其强大、稳定、安全、高效、跨平台等多方面的优点 采用SpringMVC3Spring3Hibernate3Freemarker主流技术架构 安全性做得非常变态&#xff0c;当网站安装完成后就不再允许执行任何目录下的jsp文件了&#xff08;web.xml配置了过滤器禁…

C++之内存管理

1.申请内存&#xff1a;int *p new int;释放内存&#xff1a;delete p; 2.申请块内存&#xff1a;int *arrnew int[10];释放:delete []arr; 3.申请内存需要判断是否成功&#xff0c;释放内存需要设空指针 4.new与delete配套使用 转载于:https://www.cnblogs.com/xjyxp/p/11236…

内核通识——linux内核源码(2.6.35.7版本)的目录结构

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、文件 &#xff08;1&#xff09;Kbuild文件 Kbuild是kernel build的缩写&#xff0c;就是内核编译的意思。这个文件就是linux内核特有的内核编译体系需要用到的文件。 &#xff08;2&#xff09…

【原创】自制PCB经验教训之一:版图打印篇

看了篇帖子《用感光电路板自制PCB攻略&#xff08;网友电子白菜撰写&#xff09;》&#xff0c;于是前几天在淘宝上买了一批PCB DIY工具和材料回来&#xff0c;想自己弄个试试&#xff0c;岂料障碍重重&#xff0c;甚至于酿成大祸... 首先是PCB打印的软件设置问题&#xff0c;我…

分享一套非常不错的bootstarp3.0.2响应式模板

2019独角兽企业重金招聘Python工程师标准>>> 这是销量第一的bootstrap 响应式后台管理模板&#xff0c;每套模板都是我用20美金买回来的话不多说&#xff0c;下面是五个模板的演示地址&#xff0c;你看到的&#xff0c;就是你可以下载到的。都是收费模板。 官方演示…