这几天写链表版本的聊天室的感悟

  这几天一直在做链表版本的聊天室,本来前天这个聊天室就应该完成的,但因为一个小错误,找了两天时间,想尽各种办法都没有搞定,知道刚才我才找到,当我知道真相的那一刻,真的有种想狠狠抽自己一巴掌的感觉。

  错误:在服务器端的处理注册会员和更改个性签名的时候,总是出现段错误,然后我也用gdb调试了,也找到了错误的位置,gdb给我的结果是:read(cfd, &userInfo, sizeof(userInfo));这个read访问了非法地址,我实在是想不通,以为是哪边少了取地址符还是啥的,然后到处找,最后,我想到一个办法,就是到处打印cfd的值,我要看看到底是哪边的cfd被改变了,于是,我几乎是每隔一行就打印一个cfd的值,最后,在sprintf(xcf, "update save_user set moto = '%s' where name = '%s'",userInfo.moto,userInfo.name);这句话的上下侧发现cfd打印的值变了,于是我又仔细看了一下关于数据库操作的教程,仔细的对比了这句话,发现还是没啥问题,然后我又打印了一下userInfo.moto和userInfo.name的值,当我看到打印出来的值的时候,我突然想到,会不会sprintf存的那个数组越界了啊,于是我加大了那个数组的长度,啊,老天,我想抽我自己,真的是这个问题,我以为xcf【80】已经够用了,原来我还是太天真了,没有考虑到个性签名存放的字节,数组越界了,难怪出现段错误。

  从这次的教训中,我告诉我自己,每次定义数组的时候,一定要想好数组里面存放的是什么,会不会出现越界的情况,还有就是,如果出现段错误,一定不要怕,段错误出现的场合无非就那几种:数组越界,指针访问非法地址,内存泄漏啥的。对付段错误最好的方法就是到处打印,这样就能找到具体出错地点,然后有针对性的修改。


  还有,与之前写完的数组管理的那个版本的最大的区别,除了在线用户管理上的不同外,最大的区别就是,我在这个版本中,着重考虑了一下客户端的界面切换的问题,以及客户端线程开辟的时间点。我之前想过在最开始的时候,创建两个线程,一个读一个写,这样写固然思路上面很清晰,但是会伴随着各种各样的问题出现,那就是读线程和写线程的控制。首先,最关键的一点就是,读写一定要分离,千万不能在读线程里面写(scanf),那样的话,会与写线程里面的scanf打架。

  于是我没有选择一开始就分离线程,我是这样想的:分离线程,为什么要分离?分离线程有什么作用?分离线程无非就是把读的任务一直放在一个线程,在你登录成功后,确实有两个情况:1、自己发信息 2、对方给你发信息了。但是,在登录之前,我为什么要分离线程?我注册、登录、获取密码的时候又没有人给我发信息,我要读线程干嘛?所以,我把注册、登录、忘记密码和退出放在了主线程中。在一个人登录成功的时候,我才开辟一个线程用来读信息。

  在做到登录成功后,开始对各种需求进行处理的时候,我开辟了读线程,但是我也出现了一个难题,就是:作为接收信息的一方,在我接收到信息的时候,我的读线程工作了,也打印了消息的具体内容,但是,看完了这个内容,我怎么回到主界面?我怎样才能回复消息或者进行别的操作呢?

  我尝试了很多次,包括之前做数组版本的时候,都试了很多次,但是都没有解决。在经过很多次调试后,我突然发现,我读线程读到,然后打印完毕后,我的光标一直再闪,这个时候如果按下设定的选项,是可以进行操作的,于是我想到了,在读线程里面我再打印一句话:按a可以回复对方哦,这样不就解决了吗?可是,作为用户,我又想到:如果我收到一条信息,但我不想回他,我想进行其他操作的话怎么办呢?能不能有一个刷新的按键,按下这个按键就能回到主界面呢?这个时候,我突然想到,我这个时候虽然收到了信息,但是我还是阻塞在输入选项那边,那我能不能增加一个选项,比如说case 10:{}(10是回车的Ascil码值),然后里面就写一个break,这样我不就能刷新界面了吗?

  解决了接收方的界面转换后,我又考虑了一下,作为发送方,我发送信息完毕后,我不是应该收到一些提示吗?如果,我接收的那个人不在线,是不是应该发回去一个信号,对方暂时离线?发成功了也返回一个发送成功,再加上之前的刷新问题都已经解决了,返回主界面也是轻而易举的了。作为发送方和接收方,我两使用的是同一个客户端程序,那为了区分,也为了看起来方便些,我设置了一个全局变量,在我读线程读到的时候,我讲READEN置为1,将发送方接收成功失败的信息处理放到主线程(调用的函数)中。不过,这样又出现了一个问题,我是吧判断READEN值的那个步骤放到了界面显示的前面,有时候服务器处理的太快的话,直接被跳过了,于是,我每次选项处理完后,都sleep(2),这样的话就没有了这个问题。

  还有一个小问题也记录一下,就是在踢人时候,被踢的一方收到被踢的信号后,怎么样才能让他下线?在思考这个问题的时候,我钻了牛角尖,我想当然的认为,在接收到被踢信号后,就应该自动下线,但是遗憾的是,作为接收方,我接收到被踢信号的时候,我还是阻塞在主线程的scanf()那边,这个时候用pthread_exit函数根本不管用,这个问题我也是想了很久,数组做的时候也没做出来,最后,在别人的提示下,我想到了,既然不能自动下线,那为什么不能手动让你下线呢?我设一个全局的标志位(OUT),当收到被踢信号的时候,我就让OUT等于1,然后在主线程(调用函数)中,在scanf(我是fgets)的下方,加一个if判断,如果OUT等于1,那就返回退出信号,main函数接收到退出信号后,在进行退出聊天室,或者返回登录界面的操作。这样的话,被踢之后,无论你按下什么键,都会退出。

  做了两次聊天室后,感觉对线程还有服务器客户端通信方面多了一些理解,还有就是,说实在话,链表管理在线用户很方便,而且思路更加清晰,比数组做的要清晰很多。

  程序在后面两篇文章中会发出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值