UDP 多客户端聊天+多客户端文件上传下载(C++/MFC)

来源:互联网 发布:国内主机推荐 知乎 编辑:程序博客网 时间:2024/05/21 09:04

工具:

C++/MFC

 

原理:

1.      服务器与客户端之间的通讯,通过socket套接字

2.      一个电脑有一个ip地址,和6万多的端口号

3.      当一个socket被创建,就会有一个ip和端口号联系到socket上

4.      一个应用程序,可以设置多个端口,即:客户端可以设置多个socket

5.      一个电脑相当于ip,电脑上的应用程序的一个功能相当于一个端口(4,5点的理解很重要)

6.      需要使用线程,因为sendto和recvfrom函数是阻塞的

7.      C(客户端)与S(服务器)之间的传递时通过数据包(char*)

8.      需要发送的信息,可以通过设计char*格式或者结构体来发送过去,即,C整合数据,S解析数据,然后在两端分别处理

 

思想:

1.      UDP多人聊天:

<1>多人聊天意思不是多人在一个地方群聊(当然这个实现很简单),这里我要说的是,客户端之间可以相互聊天,point2point,别人不知道内容。那么,服务器这时候扮演的是一个邮局的身份,而两个客户端就是发信人和收信人,发信人,把需要发送的信放到邮箱里面,然后邮局会过来取,那么邮局就会通过收信人的地址,把信投递到当地的邮箱中,然后收信人过来取,那么服务器就相当于一个转接员。在程序中的体现是服务器端保存所有登陆的用户名和他们对应的IP地址和端口号,当收到一个用户的请求的时候,就开始搜索,指定用户的IP和端口号,然后把相应的信息,发送到目标IP和端口号上,这就完成了C/S之间的通讯。

<2>这里需要掌握的知识是,套接字库的初始化,创建套接字,学会使用sendto和recvfrom,

这里要注意的是sendto和recvfrom是阻塞函数,意思就是,如果recvfrom这个函数没有收到数据,他就会卡在那里,直到接收到数据,sendto也是阻塞的具体百度。

服务器的处理方式就很简单了,设置结构体USERINFO,内有用户名(这里的用户名,一定要是唯一标识,类似于QQ号码),然后还有一个sockaddr_in结构体变量,此结构体用来保存一个IP和一个端口号,定义一个vector的容器,其他容器同样可以,当一个客户端发送连接请求的时候,把USERINFO赋值,然后推送到vector容器内,保存此用户的信息。这就是我的服务器一端的大体思路。

<3>需要学会线程的使用,原因很简单,因为sendto和recvfrom函数的阻塞的,不可以在主线程里面运行,不然主界面会卡死。(具体内容可以百度Afxbeginthread)

<4>接下来,说一下客户端的思路,客户端首先要发送连接请求,然后开线程进行发消息工作,这一部分最主要的就是消息的整合,我的消息整合是MSG_TYPE-FROMNAME-TONAME-TEXT,然后将这个消息发送过去,即可,当用户推出时发送下线消息。

 

2.      UDP文件传输:

<1>大体就是通过二进制方式读取文件,然后通过sendto和recvfrom进行传输,然后再以二进制方式写入文件。

<2>首先发送文件名和文件大小,目的是为了让接收端(这里为什么说叫接收端,因为,服务器和客户端都可以作为接收端,可以有文件的上传和下载得知)能够创建文件,并且开辟足够大的内存空间去准备接收发来的数据,因为,一个UDP包的大小最大为64k,那么显然当文件过大的时候64k是远远不够的,那么怎么办呢?我们可以把文件拆成很多个包,然后发送过去,然后再在接收端进行整合,这里需要知道一个知识点当发送端把很多的数据包发送给接收端的时候,数据包并不是按照发送的次序到达,也就是说,我发送的第五十个数据包,可能比发送的第十个数据包更早的到达接收端,那么这样一来,如果按照数据的接收顺序写入文件的话,就会出错,例如,发送了五个单词,h,e,l,l,o,那么如果按照接收顺序写入的话,他会可能会写入e,l,h,l,o。所以需要给每一个数据包编号,然后再在接收端整合。

 

3.      遇到的问题和解决方法:

<1>结构体是否可以作为数据包进行传输?

           可以,结构体可以强行转化为char*,但是值得注意的是结构体的大小问题,不是sizeof(结构体变量),具体百度。

 

<2>为什么会丢包?

        接收端接到数据后需要分析数据包,那么必定要花费一定的时间,如果发送端发的数据包的速度过快比如不去sleep,或者sleep时间过短,那么,发送端发送的包,可能在接收端处理上一个包的时候就丢掉了,其实数据包被传到了端口处,如果没有东西接收他的话,那么他就会被丢掉,所以发送端的文件发送,时间间隔要把握一下。

 

<3>一个端口可以向两个,多个,不同的目的端口发送数据包,但是一个端口不能同时接收两个端口发来的信息,为什么会这样?

           因为之前也提到过了,recvfrom函数是阻塞的,当这个sock在接收消息时候,如果没有接收到,那么他就会一直停滞在那里,那么又有人会问了,sendto函数不也是阻塞的吗?为啥它可以呢?这里不是说他不会那样,只是因为我们无需开一个线程去不停的给对方发送消息,也就是说,我啥时候给别人发送,我就啥时候sendto一下就好了,但是recvfrom需要不停地接收,在一个线程里面,那么这个全局变量sock就会一直被这个线程使用,所以如果再让他来接收消息,那么就是无法接收的。

 

<4>如何实现单个客户端同时上传下载文件?

           如果只是一个客户端的话,只需要将服务器,发送文件的代码放到线程里就好了,但是如果服务器还要处理其他其他用户的请求的话,那就可以上传一个端口,下载一个端口。

 

<5>多个客户端上传下载

         我想的方法很笨,因为,不想在一个端口接收所有人的数据包,那样会很慢,而且很容易丢包,然后我就想这在服务器建立多个端口,然后每一个端口用一个线程去维护,然后当一个客户端不需要文件传输服务的时候,就可以把这个端口让出来,让其他人使用,当需要的时候可以向服务器申请,然后服务器发一个没有任务的端口,然后客户端建立连接,进行通讯。

<6>推出进程,需要发送,功能结束消息之后break

 

没有学习很深,只是在做课设过程中遇到的一些问题,和自己的解决方法,我处理的方法很菜也很水,望指教。

 

0 0
原创粉丝点击