通信基础小结

通信基础小结

通信连接实现

(服务器创建,客户端连接,文字流发送接收,线程实现群聊,客户端界面)

1.ServerSocket和Socket类

使用ServerSocket server=new ServerSocket(port)语句在指定端口创建服务器,等待客户机的连接。通过cmd输入服务器的IP和端口号即连上服务器。

若要实现群聊,即多个客户端同时连上服务器,即避免accept()方法的阻塞,可以为每位客户端创建单独的线程。

2.文字传输

由Socket对象得到Input和Output流,以回车作为一句聊天语句的结束标志,“Bye“为结束聊天的标志对流进行写入和读取操作。

3.客户端界面实现:代替cmd,创建登录和聊天界面,可视化。

扫描二维码关注公众号,回复: 762521 查看本文章

经分析,客户端与服务器基本功能既不能一致,即创建Socket对象,连接上上服务器,按照服务器收发信息流的方法同样接收于发送文本。


协议初识:(协议概念提出,实现xmpp协议)

1.协议概念:

让不同地理位置的通信系统,协同工作实现信息交换和资源共享的一种共同语言。它定义了一个让交流双方共同遵守的规则。这就是协议。简单说,交流什么,怎样交流。怎么样把计算机最底层的数据流分割,按照怎样的协议把一串串无规律的0,1传翻译成信息。

2.xmpp协议:

上面我们自定义的简单协议可以实现简单的文本聊天功能,但明显存在很多缺陷。所以便提出xmpp协议。

所谓xmpp协议,即The Extensible Messaging and Presence Protocol(可扩展通讯和表示协议).它是基于xml(可扩展标记语言)的。

其核心就是把每条信息用<></>这样成对出现的括号把信心封装起来,读取时解析即可。

如:

1.登录请求:

                  <msg><type>login</type><name>user</name><psw>password</psw></msg>

2.登录应答:

                 <msg><type>loginResp</type><state>登录结果</state></msg>

3.聊天信息:

<msg><type>chat</type><sender>sender</sender></receiver>receiver</receiver><content>消息内容</content></msg>

4.注册消息

5.注册应答

6.在线用户列表

7.上线消息

8.下线消息

9.传送文件

10.传送图片

等等。。。。

这种方式的扩展性很强,不论是发送什么类型的信息,都可以把标记值按照此方式封装。不会丢失,处理方式很简便统一。

解析方法包括重要的两部分:

1.从流中读取并解析出一条xml消息

private String readString() throws IOException {
		String msg = "";
		int i;
		i = ins.read();
		StringBuffer stb = new StringBuffer();
		boolean end = false;
		while (!end) {
			char c = (char) i;
			stb.append(c);
			msg = stb.toString().trim();
			if (msg.endsWith("</msg>")) {
				break;
			}
			i = ins.read();
		}
		// 在此处,转化时必须使用GBK编码,即读到的消息编码为中文编格式,否则会乱码
		msg = new String(msg.getBytes("ISO-8859-1"), "GBK").trim();
		return msg;
	}

 2.解析标记后的内容:

static String getXMLValue(String flagName, String xmlMsg) throws Exception {
		try {
			// 1.<标记>头出现的位置
			int start = xmlMsg.indexOf("<" + flagName + ">");
			start += flagName.length() + 2;
			// 2.</标记>结束符出现的位置
			int end = xmlMsg.indexOf("</" + flagName + ">");
			// 截取标记所代表的消息的值
			String value = xmlMsg.substring(start, end).trim();
			return value;
		} catch (Exception ef) {
			throw new Exception("解析" + flagName + "失败:" + xmlMsg);
		}

	}
 


功能扩充:(文件传输,网络画板,整合)

功能扩充就是传送不同形式的信息,如图片,文件,或者远程控制等,其本质还是流的相互收发,只是流封装的内容不同,如最简单的传送直线,一次性发送起始和终点的坐标,画笔粗细颜色等。接收发按照协议接收解析找一样的顺序画出即可。

这里举传送的文件的例子,需要强调的是,为了缓解大文件传输速度慢的问题,用包装后的Data流可以适当加快传输速率。


1.客户端向服务器发送文件:

public void sendFile(String sender, String receiver, String path,String FileName) {
		DataOutputStream dous = new DataOutputStream(out);
		try {
			// 构造输入流
			InputStream ins = new FileInputStream(path);
			// 文件数据长度
			int fileDataLen = ins.available();
			// 定义存储文件内容的byte数组
			byte[] fileData = new byte[fileDataLen];
			// 读入文件数据
			ins.read(fileData);
			// 文件头信息
			String fileHeadXml = "<msg><type>"+"file"+"</type>" + "<sender>" + sender
					+ "</sender><receiver>" + receiver
					+ "</receiver><FileName>" + FileName
					+ "</FileName><FileLen>" + fileDataLen + "</FileLen></msg>";
			System.out.println("写入时文件总长度为:" + fileDataLen);
			// 以字符串形式写入服务器的流中
			dous.write(fileHeadXml.getBytes());
			// 文件内容,与文件头信息分开传送。
			dous.write(fileData);
			// dous.flush();
		} catch (Exception ef) {
			ef.printStackTrace();
		}
	}
 

2.服务器转发给某客户端

private void processChat(Socket client) throws Exception {
		if(checkLogin()){//登录成功
			ChatTools.addPT(this.userName,this);//加入到集合中
			while(connect){
				String msg=readServer();//读取客户端发来的一条信息
				String type=getXMLValue("type",msg);
				if(type.equals("chat")){//聊天消息
					................
				}else if(type.equals("file")){//发送的是文本信息
				   String senderName=getXMLValue("sender",msg);//解析发送者
				   String destUserName=getXMLValue("receiver",msg);//j解析接受者
				   int dataNum=Integer.parseInt(getXMLValue("FileLen",msg));//文件内容大小
				   DataInputStream dins=new DataInputStream(ins);//Data输入流
				   byte[] data=new byte[dataNum];//定义byte数组
				   dins.readFully(data);//一次性读取
				   String content=new String(data);//转化为String类型
					
					//截取用户的名字发送,先发送文件头,再发送文件内容
					ChatTools.castFile(senderName, msg, destUserName);
					ChatTools.castFile(senderName, content, destUserName);
				}
			}
		}
	}

给某用户发送文件信息:

  public static void castFile(String userName,String msg,String destUserName){
    	for(int i=0;i<map.size();i++){
			ServerThread st=map.get(destUserName);//取出队列中一个线程
			if(null!=st&&st.getMyUserName().equals(destUserName)){
				st.sendToServer(msg);
				break;
			}
			
		}
 

3.客户端接收到文本解析:

public void run() {// 解析服务器转发来的流中的信息,不同工具解析不同的信息
		try {
			while (connOK) {// 连接上服务器
				String xmlMsg = readString();// 接受一条信息
				String type = getXMLValue("type", xmlMsg);// 分析出type值
				if (type.equals("chat")) {// 聊天信息
					..............
				}
				if (type.equals("file")) {// 发送文件信息
					String sender = getXMLValue("sender", xmlMsg);// 发送者名字
					JOptionPane.showConfirmDialog(null, "收到来自" + sender
							+ "的文件,是否接收?", "Option", 0);
					int state = JOptionPane.YES_NO_OPTION;
					if (state == 1) {// 不接受

					} else if (state == 0) {// 接收文件传输
						//弹出保存文件选择器
						JFileChooser jfc=new JFileChooser();
						jfc.setDialogTitle("保存文件");
						jfc.setApproveButtonText("保存");
						jfc.showOpenDialog(null);
						
						//将要保存的文件名和路径
						String fileName=jfc.getSelectedFile().getName();
						String path=jfc.getSelectedFile().getAbsolutePath();
						int fileDataLen = Integer.parseInt(ClientConn
								.getXMLValue("FileLen", xmlMsg)); 
						
						readFileContent(fileDataLen, fileName,path);
						
						
					}
				}

 读取文件内容的方法:

private void readFileContent(int fileDataLen, String fileName,String path) {
	       // 创建新文件
		File newFile = new File(path);
               //包装流
		DataInputStream dins = new DataInputStream(ins);
		byte[] data = new byte[fileDataLen];
		try {
			// 一次性读取并写入到数组中
			dins.readFully(data);
			// 得到文件输出流
			FileOutputStream fous = new FileOutputStream(newFile);
			fous.write(data);// 写入文件内容数据
			fous.flush();
			fous.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
 


深入了解:(阻塞,线程异步问题,NIO模式——MINA框架)


项目开发: ——————————未完待续!

猜你喜欢

转载自csu-ningman.iteye.com/blog/1705683