读书人

通讯基础2服务器的创建

发布时间: 2012-12-20 09:53:21 作者: rapoo

通信基础2—服务器的创建
学习通信基础快一周了。到了服务器、客户端的实现,进度很自然地变慢了。

上课中的两点,觉得有些体会:
1.减少线程之间依赖关系,
最基础、底层的类一改动,那么以它为基础的类都要受影响,所以,减少依赖关系。
2.区分整体框架设计和细节完善问题
明白当前是完成搭建框架,还是完善具体细节。
自顶向下设计,先完成类的设计,再完善具体方法。

先写服务器,客户端暂时由telnet命令产生。

其中,有几个地方花了较多的时间:
1.服务器接收客户端的消息,readString方法
(1)服务器读入正确的消息。
自己定义:按回车后,服务器接受一条消息。而按回车键,先后读入字节13(回车"\r")和字节10(换行"\n")两个字节。每次,读输入流中一句话的结束标志是读到字节13,所以,读的每句话都要对上一次按回车后,遗留在在输入流中未读入的的换行字节10作处理。
(2)点击客户端右上角关闭时,读取字节-1,若此时输入未结束,则可能进入死循环。完善服务器读取字节的条件,跳出读取一句话的循环。

//服务器读客户端的一句话,public String readString(java.io.InputStream ins){         //创建StringBufferStringBuffer strb=new StringBuffer();try{int t=ins.read();//按回车,字节为13、10//当关闭右上角时,t=-1while(t!=13 && t!=-1){//按回车,或关闭右上角时,读客户端的输入流结束strb.append((char)t);//按右边数字键盘的Enter就不对了,就10一个字节t=ins.read();}String str=strb.toString();if(t!=-1){//正常输入if(str.charAt(0)=='\n'){return str.substring(1, str.length());}else{//第一次回车之后return str;}}}catch(Exception ef){ef.printStackTrace();}//输入一半时,点击右上角关闭按钮return null;}

客户端结束对话的while循环,输入“end”结束对话:
while(!"end".equals(str) && str!=null ){System.out.println("进入while(end)中");//二......转发给其他客户端ChatTools.sendMsg2Others(this, str);str=readString(ins);}

2.加了服务器界面后,需要正常的启停控制。
设计启停功能:
(1)未按启动之前,线程休眠等待,
(2)按启动之后,服务器创建成功
(3)按停止之后,服务器关闭
循环执行(1)(2)(3)。

3.当在accept的时候,关闭ServerSocket对象,或者在read的时候,关闭Socket对象,则会捕获SocketException,而这些在阻塞状态下关闭,产生的异常是正常的,不可能避免。只要捕获异常后,适当处理,就可以了。

4. 当关闭服务器后,服务器仍可以向客户端发送发消息。
这个解决得比较快,将服务器接收客户端消息的线程对象ChatThread从队列中移除就可以了。当服务器没有发送对象时,消息就发不出了。

5. 当关闭服务器后,客户端仍然可以向服务器发送消息。
问题的本质是服务器接收客户端消息的线程没有执行结束,即run方法没有执行结束。
(1)原本以为服务器的线程会影响服务器接收客户端消息的线程ChatThread。因为,线程ChatThread是从服务器的线程中产生的。测试之后,两个线程是独立的,两者之间的结束,互不影响。
(2)强行关闭ChatThread
由于,线程数量未知,不便控制,所以将他们放入队列处理。除了队列,暂且想不出其他方法来解决。当服务器关闭时,将所有的Socket对象强制关闭,流也就没有了。那么run方法就执行完了,服务器就无法接收客户端消息了。
这里,要强调一点,要清楚地知道每个类的属性和方法,最好画一幅类之间的关系图。避免重复处理。比如,Socket是ChatThread类的属性,创建队列ArrayList<ChatThread>之后,那么就不必要再创建ArrayList<Socket>了。


进入到有界面的服务器之后,进度就特别慢。有时候,就要时不时地理清思路,考虑方法要实现什么功能。回顾以前的代码,也有助于更好地理解概念。
如由于代码较多,理了一下服务器的思路:
1.创建服务器,
2.服务器处于循环等待客户端连接的阻塞状态
3.客户端连接成功后,得到连接对象Socket (连接时,验证用户名、密码)
4.从Socket对象得到输入输出流
5.处理服务器与客户端ct的对话
5.1 服务器向客户端发送消息
5.2 服务器接收客户端发送的消息,线程
5.2.1将新的客户端,加入线程队列
5.2.2将一个客户端发送的消息,通过服务器发送给其他客户端
5.2.3将下线的客户端,从线程队列中删除
6.关闭服务器,关闭所有Socket对象,清空队列。


其中,也用到了较多的队列操作,一般有如下操作,若忘了其中一个步骤,就要找错误了:
1.加入队列
2.从队列中移除某个对象
3.程序结束,清空队列

这个还是要多练习,在练习的过程中理解,多学习。

读书人网 >编程

热点推荐