通信基础2—服务器的创建

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

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

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

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

//服务器读客户端的一句话,
public String readString(java.io.InputStream ins){
//创建StringBuffer
StringBuffer strb=new StringBuffer();

try{
int t=ins.read();
//按回车,字节为13、10
//当关闭右上角时,t=-1
while(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.程序结束,清空队列

这个还是要多练习,在练习的过程中理解,多学习。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值