局域网实时聊天系统

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

局域网实时聊天系统

1、图形界面的设置:

使用MFC应用程序框架设计局域网聊天系统的图形界面(对话框),使用的Windows标准控件包括:按钮、静态文本、IP地址控件、分组框、编辑框、列表框等控件。

本系统集服务器和客户端为一体,通过网络设置按键弹出具体设置页面,通过单选按钮进行单项设置:服务器端:本地监听端口,用于监听客户端的连接信息;

开启按键和停止按键,用于强制服务器的开启和停止。

客户端:服务器的IP设置和服务器的端口设置,用于连接服务器;

连接服务器按键和停止按键,用于主动加入和退出聊天系统。

聊天记录编辑框:默认只读,用于显示聊天系统中各客户端和服务器的聊天记录;

聊天记录输入框:输入信息之后,可点击Enter或者发送信息按键发送信息;

关闭窗口:点击按键,可关闭正在执行的对话框;

更多功能:可用于聊天系统的其他功能扩展。

2、初始化状态:

CheckRadioButton:选择单选按钮中的一个;

SetDlgItemText:设置编辑框中显示的字符串;

EnableWindow:重载函数,设置控件的启用与关闭;

ExtendDiaog:设置四个静态变量

m_DlgRectLarge、m_GroupRectLarge:用于保存主对话框和分组框的临时变量;

m_DlgRectSmall、m_GroupRectSmall:用于保存主对话框和分组框的改变变量。SetWindowPos:有ID获得主对话框和分组框的句柄,设置界面的伸缩。

3、开启服务器:

(1)创建监听线程:

m_hListenThread = CreateThread(NULL, 0, ListenThreadFunc, this, 0, NULL);

●NULL:返回的句柄不能被继承;

●0:新线程堆栈的大小与进程主线程堆栈相同;

●ListenThreadFunc:线程开始运行的地址,一般为线程入口函数名;

●This:传递给线程启动函数的32位参数;

●0:线程创建后立即执行;若为CREAT_SUSPEND,则挂起不执行;

●NULL:存放返回的线程ID。

(2)创建监听线程入口函数:

DWORD WINAPI ListenThreadFunc(LPVOID pParam)

{

CChatRoomDlg *pChatRoom = (CChatRoomDlg *)pParam;

//创建监听套接字(IPv4网络协议、流式套接字、TCP协议)

pChatRoom->m_ListenSock = socket(AF_INET , SOCK_STREAM , IPPROTO_TCP);

//创建sockaddr_in结构存储IP地址和端口

sockaddr_in service;

//绑定IP地址和端口到监听套接字

bind(pChatRoom->m_ListenSock, (sockaddr*)&service, sizeof(sockaddr_in));

//监听申请的连接,等待客户端连接其中两个参数为:

S:用于标识一个已捆绑未连接套接口的描述字。

backlog:等待连接队列的最大长度。

listen(pChatRoom->m_ListenSock, 5);

}

(3)使用异步I/O模型防止阻塞

connect、accept、recieve或recievefrom这些都是阻塞程序,所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回。

可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生,则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。

int select(nfds, readfds, writefds, exceptfds, timeout);

返回值:准备就绪的描述符数,若超时则返回0,若出错则返回-1。

nfds:select监视的文件句柄数,视进程中打开的文件数而定,一般设为你要监视各文件

中的最大文件号加一。(注:nfds并非一定表示监视的文件句柄数。)

readfds:select监视的可读文件句柄集合。

writefds: select监视的可写文件句柄集合。

exceptfds:select监视的异常文件句柄集合。

timeout:本次select()的超时结束时间。

当readfds或writefds中映象的文件可读或可写或超时,本次select()就结束返回。程序员利用一组系统提供的宏在select()结束时便可判断哪一文件可读或可写,对Socket编程特别有用的就是readfds。

几行相关的宏解释如下:

FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。

FD_SET(intfd, fd_set *fdset):建立文件句柄fd与fdset的联系。

FD_CLR(intfd, fd_set *fdset):清除文件句柄fd与fdset的联系。

FD_ISSET(intfd, fd_set *fdset):检查fdset联系的文件句柄fd是否可读写,当>0表示可读写。

(4)非阻塞情况下,在一个套接口接受一个连接

SOCKET accept(SOCKET s, structsockaddr FAR *addr, int FAR *addrlen);

accept函数主要用于服务器端,一般位于listen函数之后,默认会阻塞进程,直到有一个客户请求连接,建立好连接后,它返回的一个新的套接字socketfd_new,此后,服务器端即可使用这个新的套接字socketfd_new与该客户端进行通信,而sockfd则继续用于监听其他客户端的连接请求。

第一个参数:用来标识服务端套接字(也就是listen函数中设置为监听状态的套接字)

第二个参数:用来保存客户端套接字对应的“地方”(包括客户端IP和端口信息等),第三个参数是“地方”的占地大小。

返回值:对应客户端套接字标识。

实际上是这样的: accept函数指定服务端去接受客户端的连接,接收后,返回了客户端套接字的标识,且获得了客户端套接字的“地方”(包括客户端IP和端口信息等)。

这个新的套接字socketfd_new与监听套接字sockfd是什么关系?它所代表的socket对象包含了哪些信息?socketfd_new是否占用了新的端口与客户端通信?

先简单分析一番,由于网站的服务器也是一种TCP服务器,使用的是80端口,并不会因客户端的连接而产生新的端口给客户端服务,该客户端依然是向服务器端的80端口发送数据,其他客户端依然向80端口申请连接。因此,可以判断,socketfd_new并没有占用新的端口与客户端通信,依然使用的是与监听套接字socketfd_new一样的端口号。

那这么说,难道一个端口可以被两个socket对象绑定?当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢?

首先,一个端口肯定只能绑定一个socket。我认为,服务器端的端口在bind的时候已经绑定到了监听套接字socetfd所描述的对象上,accept函数新创建的socket对象其实并没有进行端口的占有,而是复制了socetfd的本地IP和端口号,并且记录了连接过来的客户端的IP和端口号。

那么,当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢?

客户端发送过来的数据可以分为2种,一种是连接请求,一种是已经建立好连接后的数据传输。

由于TCP/IP协议栈是维护着一个接收和发送缓冲区的。在接收到来自客户端的数据包后,服务器端的TCP/IP协议栈应该会做如下处理:如果收到的是请求连接的数据包,则传给监听着连接请求端口的socetfd套接字,进行accept处理;如果是已经建立过连接后的客户端数据包,则将数据放入接收缓冲

相关文档
最新文档