一步一步学通信

        学习通信有将近一个月的时间了,从创建一个简单的服务器到今天用XMPP协议与字节流协议实现服务器与客户机的交互这段过程中,不仅感受到了自己技术上的薄弱,更体会到了作总结的必要性——一方面为自己的学习进度做一个记录,一方面对技术上做一些查漏补缺,更重要的是作总结对我来说还是一件强力而为的事。
一、创建简单服务器
        每台机器都有自己的IP地址,每个服务器都有自己的端口号,只要提供一个没被占用的端口号,就可以创建服务器了。得到服务器的Socket对象,取得输入输出流,然后用telnet软件与服务器进行连接。(每台电脑有0-65535个端口号,我们一般会用1024之后的端口号,0-1024之间的端口号都是知名端口号,一般已被占用!)
/**
	 * 创建服务器
	 */
	public static void setUpServer(int port){
		try{
			//创建服务器
			ServerSocket sc = new ServerSocket(port);
			while(true){
				//服务器阻塞等待客户机的接入
				Socket client = sc.accept();
				//从连接对象上获取输入输出流
				OutputStream ops = client.getOutputStream();
				InputStream ips = client.getInputStream();
				String str = "welcome to server!\r\n";
				ops.write(str.getBytes());//将欢迎信息写到输出流中
				ops.flush();
				int input = 0;//一个字节一个字节的读取客户机的输入
				while(13!=input){
					input = ips.read();//读取客户端输入的信息
				}
				client.close();//关闭与客户端的连接对象
			}
		}catch(Exception ep){
			System.out.println("错误的端口号:"+port);
		}
	}

           //读取字符串的方法(读取小数据)
	          byte[] b = new byte[1024];
	          int length = ips.read(b);
	          String msg = new String(b,0,length);

        当清楚了这一点:数据的发送是以一个一个的bit在网络中传输的(通常理解为是一个一个字节在网络中传输的),之后我们对输入输出流的操作以及对整个通信流程的处理就显得清晰了。
二、多线程服务器
        第一个简单服务器有很大的缺陷,只能同时在线一个用户,这种情况是不允许的,我们必须用线程去控制客户端与服务器的连接,这样才能同时有多个用户共用一个服务器。实现很简单,只要把所有与服务器的连接对象并读取信息的方法放到一个线程中就可以了。
//在指定的端口上启动服务器
	public void setupServer(int port){
		try {
			//在指定端口上创建服务器
			ServerSocket sc = new ServerSocket(port);
			System.out.println("服务器创建成功:"+port);
			while(true){
				Socket client = sc.accept();//阻塞等待中
				System.out.println("进入一个客户机连接:"+client.getRemoteSocketAddress().toString());
				//启动一个处理线程,去处理这个连接对象
				ServerThread ct = new ServerThread(client);
				ct.start();
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

三、群聊服务器的实现
        当一个用户上线时,服务器会验证它的个人信息是否符合登录条件,如果登录成功,服务器会发送当前在线人数,发送给所有用户当前用户的上线信息,当有人下线时,服务器会发送当前用户的下线信息给其他所有在线用户,并且程序能实现群聊的功能。
        程序看起来有些复杂,如果我们把所有的发送与接收消息的方法都定义在同一线程类中,那样会更麻烦,这时就开始考虑设计上的一些东西了。一个类只干一件相同的事,将用户信息封装(以便以后的扩展),创建辅助类——提供静态的方法,用来从队列中添加、删除好友,通知好友上线或下线的信息,只管其他类进行调用。这样一来解结构就清晰化了!
public class ChatTools {
	//存储线程的队列
	private static List<ServerThread> stList = new ArrayList();
	private ChatTools(){}
	//将消息发送给每个在线用户
	public static void castMsg(UserInfo sender,String msg){
		msg = sender.getName()+"说:"+msg;
		for(int i=0;i<stList.size();i++){
			ServerThread st =stList.get(i);
			st.sendMsg2Me(msg);
		}
	}
	//将上线用户添加到队列中,发送在线人数
	public static void addClient(ServerThread ct){
		stList.add(ct);
		castMsg(ct.getOwerUser(),"我上线啦!目前人数"+stList.size());
		
	}
	//将下线客户从队列中删除
	public static void removeClient(ServerThread ct){
		stList.remove(ct);
	}
}

/** 
 * 用户数据封装类,方便以后扩展
 * @author Administrator
 *
 */
public class UserInfo {
	private String name;
	private String pwd;
	private String loginTime;
	private String address;
	//以下是get()set()方法
}

四、简单客户端
        当理解了服务器的创建方法之后,学习客户端就变的很简单了,只要知道服务器的IP地址与创建在哪个端口号上就可以搞定了,基本的通信流程和服务器别无二致。
五、XMPP风格协议应用
        只做服务器很简单,只做客户端也不难。但是要把两者一起做,再加上界面与自定义协议之后,就显得不是那么轻松了(例如:<msg><name>用户名</name></msg>,当然这条协议我们要解析的是用户名,可以用String类中的方法,也可以用dom4j创建并解析(个人喜欢比较优雅的 )同时,协议的制定也会影响整个程序的设计)。流程还是那个流程,但是要同时处理客户端与服务器,复杂程度就会大很多,而且还有很多不确定因素。
        首先是理清通信流程的问题,制定通信流程图,分清什么是同步消息什么是异步消息——同步消息:在我们注册或登录服务器时,首先客户端要发送请求信息,服务器收到请求信息解析并进行信息验证,然后返回注册或登录状态信息,客户端收到信息解析并判断成功或失败,然后进行相应的操作。异步消息:当客户端发送聊天信息时,不考虑服务器是不是收到,或者发送到其他客户端,我只管发我的,与你无关。这一类的消息必然要在run()方法中处理了!
        然后是关于类与方法的制定,客户机与服务器都要解析XML协议,所以我们将解析的方法放到一个util包下,服务器和客户端共用包里的方法,大大的减少了代码量。还有一些用户数据的封装类,存储用户线程发送上线下线的相关类的处理。(凭我现有的编程能力,只能尽量的让每个类处理同一件事,让每个方法只处理一件事!)
        最后就是关于验证,每写一步验证一步,把每个小问题解决了,大的问题也就迎刃而解了。(说的容易,做起来真的很难!)
        当然,不管在简单的服务器还是在XMPP中都遇到了这样或那样的问题,有些问题很幼稚,有些问题还没解决,这里只要谈到了自己的一些思路。关于字节流协议(文件头+文件体,相对与XMPP更严谨一些),对程序的设计上会有更多的问题,对于方法的设计会面临更多问题,这些都留待以后解决。

你可能感兴趣的:(多线程,数据结构,编程,socket,网络协议)