`
19941021
  • 浏览: 5376 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
最近访客 更多访客>>
社区版块
存档分类
最新评论
  • ayaome: ...
    java
  • 19941021:     明白了!谢谢了!
    java
  • ayaome: head属于LinkList类的属性你加个static关键字, ...
    java
  • 19941021:      这是因为在主函数中调用遍历链表的方法时要传入链表头的 ...
    java
  • ayaome: public static LinkNode head=nul ...
    java

Java SE通信开发小结(1)

阅读更多
[size=medium]
  
   通信项目做了好长时间,今天回过头,重新温习一下学过的知识,并总结一下知识。
   既然讲到通信,那么首先就要提到一个问题:
   什么是通信?
   我在未学习通信之前的理解:
        通信就是联网,不联网何来通信,联网之后进行数据传递就行了。
        对服务器,客户端都只是一个模糊的概念,只知大概,不知全貌。
        最可悲的是不知道数据是如何传递的。
   学习后对通信的理解:
        首先解释几个名词:
        服务器:在几台电脑之间建立连接之前,等待连接的机器。
        客户端:主动连接其他机器的机器。
        每台机器都有一个ip地址,和0~65535端口(0~1024已有其他用途,自己连接时最好不要用)
        网址与服务器地址的关系:服务器地址由9~10位数字组成,网址是一个域名,
                                两者是映射关系
        常用的几个与网络有关的dos命令
        1.ping netjava.cn   查看网络是否通畅
        2.telnet [服务器地址] [端口号]   连接服务器的指定端口
        3.netstat -an  查看和自己的机器通信的网络
        4.tracert [网址]  跟踪你的机器与连接目标地址间的路由器

        通信的途径就是:(核心就是协议)
        1.服务器和客户端建立连接
        2.获得两者的输入流,输出流
        3.制定协议
        4.根据服务器与客户端间制定的协议进行数据的读,写解析

        那么什么叫做协议呢?通俗的说就是两个人之间定义的一个口号,或一个约定
        你们按照自己的约定可以明白对方的意思,机器之间明确自己进行的操作。
       
         下面用代码简单实现以下发送聊天消息和文件:
         制定协议:
         消息:消息总长(int)+消息类型(byte)+消息内容(byte[])+目标客户账号(int)
        文件:文件数据总长(int)+数据类型(byte)+目标客户账号(int)+文件名(byte[256])
             +文件内容(byte[])

/**
 *界面等代码就省略了,只写下服务器和客户端的传输数据的功能实现
 *Client:客户端
 *Server:服务器
 */
package 通信;

import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {

	// 启动主函数
	public static void main(String[] args) {
		Server se = new Server();
		se.setServer(8732);
	}

	public void setServer(int port) {
		try {
			// 建立绑定在指定端口上的服务器对象
			ServerSocket server = new ServerSocket(port);
			while (true) { // 让服务器进处循环等待状态
				Socket client = server.accept();				
				// 调用处理连接对象的方法去处理连接
				calChat(client);
			}
		} catch (Exception ef) {
			ef.printStackTrace();
		}
	}

	// 处理客户机进入的连接对象
	private void calChat(Socket client) {
		try {
			// 得到一个输出/输入流对象
			OutputStream out = client.getOutputStream();
			InputStream ins = client.getInputStream();
			// 将输入流包装为DataInputStream方便读取原始类型的流
			DataInputStream dins = new DataInputStream(ins);
			while (true) {
				// 开始读取数据:每一条消息
				// 1.读消息长度
				int totalLen = dins.readInt();
				// 2.读取消息类型标识
				byte flag = dins.readByte();
				// 3.读取目标客户账号
				int destNum = dins.readInt(); // 消息头解析完毕
				// 根据消息的类型,读取消息体部分
				if (flag == 1) {// 类型为1,是文本聊天消息,按其规则读取
					// 创建对应消息体部分字节的长度的数据
					byte[] data = new byte[totalLen - 4 - 1 - 4];
					// 从流中读取data.length个字节放入数组中
					dins.readFully(data);
					String msg = new String(data);// 转成字符串
				} else if (flag == 2) {// 文件数据包体解析
					System.out.println("发送文件给:" + destNum);
					byte[] data = new byte[256];
					dins.readFully(data);// 读取256个字节做文件名字
					// 解析出了文件名字,并去除末尾空格
					String fileName = new String(data).trim();
					// 余下的字节就是文件内容
					data = new byte[totalLen - 4 - 1 - 4 - 256];// 文件字节数据总长
					dins.readFully(data);// 读入文件的字节
					// 保存文件到当前目录下:
					FileOutputStream fous = new FileOutputStream(fileName);
					fous.write(data);
					fous.flush();  //将缓冲数据全部输出
					fous.close();  //关闭文件输出流
				} else {
					client.close();
				}
			}
		} catch (Exception ef) {
			ef.printStackTrace();
		}
	}

}

//客户端
package 通信;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class Client {
	private DataOutputStream dous;// 输出流对象

	//发送字符串
	private void writeString(DataOutputStream out, String str,int length) {
		try {
			byte[] data = str.getBytes();  //将字符串转化为字符数组
			out.write(data);               //向服务器发送数据
			while(length>data.length){
				out.writeByte('\0');//补二进制0
				length--;
			}
		} catch (Exception ef) {
			ef.printStackTrace();
		}
	}

	//发送文本消息给目标账户
	private void sendTextMsg(String msg, int account) {
		try {
			byte[] sb = msg.getBytes();// 得到消息的字节数
			//消息总长=自身长度(int)+消息类型(byte)+账号(int)+消息内容(byte[])
			int totalLen = 4 + 1 + 4 + sb.length;
			dous.writeInt(totalLen); // 总长
			dous.writeByte(1); // 类型:1为文本消息
			dous.writeInt(account);// 写入目标用户号
			dous.write(sb);// 写入消息内容
			dous.flush();
		} catch (Exception ef) {
			ef.printStackTrace();
		}

	}

	//发送指定的文件给目标用户
	private void sendFile(String fileName, int destNum) {
		try {
			// 根据文件名创建文件对象
			File file = new File(fileName);
			// 根据文件对象,构造一个输入流
			InputStream ins = new FileInputStream(file);
			int fileDataLen = ins.available();// 文件数据总长
			int totalLen = 4 + 1 + 4 + 256 + fileDataLen; // 得到了要发送数据包的总长
			dous.writeInt(totalLen);
			dous.writeByte(2);// 类型是2,即文件数据包
			// 写入目标用户号
			dous.writeInt(destNum);
			// 文件名:得到文件的短名字
			String shortFileName = file.getName();
			// 写入文件名
			writeString(dous, shortFileName,256);
			byte[] fileData = new byte[fileDataLen];
			ins.read(fileData);// 读入文件数据
			dous.write(fileData);// 写出到服务器的流中
			dous.flush(); // 刷新流
		} catch (Exception ef) {
			ef.printStackTrace();
		}
	}

	//根据制定的服务器地址和端口连接服务器
	public void LinkServer(String ip, int port) {
		// 创建一个到服务器端的Socket对象
		try {
			Socket client = new Socket(ip, port);
			// 得到输入输出流对象
			InputStream ins = client.getInputStream();
			OutputStream ous = client.getOutputStream();
			// 将输出流包装为DataOutputStream对象
			dous = new DataOutputStream(ous);
			int num = 0;
			while (true) {
				System.out.println("登录服务器成功,请选择你要发的类型(1:聊天 2:文件:");

				// 让用户从命令行输入要发送的文件名字
				Scanner sc = new java.util.Scanner(System.in);
				int type = sc.nextInt();
				if (type == 1) {// 发文本
					sendTextMsg("我要发送问文本了" +num++, 8380);
				}
				if (type == 2) {// 发文件,注这个文件必须存在
					sendFile("d:\\wjh.txt", 8380);
				}
				num++;
			}
		} catch (Exception ef) {
			ef.printStackTrace();
		}
	}

	// 主函数
	public static void main(String[] args) {
		Client client = new Client();
		client.LinkServer("localhost", 8732);
	}
}



其实除了这种发送方式还有其他方式:
你把文件(MyFile)和消息(MyMessage)用类分装,将前面发送的东西设置为属性,在属性里面定义,set和get方法
发送时,先创建输入,输出流对象ins,dous
写入对象类型标识flag(byte)
然后用对象输入流封装ObjectInputStream input=new ObjectInputStream(ins);
调用input.ReadObject();
根据flag的值用相应类的对象接收
我以前也是用这种方法
最后调用该类的get方法输出,就ok了




该方法代码实现随后见通信(2)




[/size]
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics