收藏 分享(赏)

C语言构建网络聊天室.doc

上传人:精品资料 文档编号:8706873 上传时间:2019-07-08 格式:DOC 页数:13 大小:177.50KB
下载 相关 举报
C语言构建网络聊天室.doc_第1页
第1页 / 共13页
C语言构建网络聊天室.doc_第2页
第2页 / 共13页
C语言构建网络聊天室.doc_第3页
第3页 / 共13页
C语言构建网络聊天室.doc_第4页
第4页 / 共13页
C语言构建网络聊天室.doc_第5页
第5页 / 共13页
点击查看更多>>
资源描述

1、C 语言课程设计第 1 页 共 13 页Windows C 语言构建网络聊天室利用 C 语言编写 Windows 应用程序有两种方式:一种是 Windows C 编程方式,另一种是 Visual C+编程方式。在一般情况下,Visual C+编程方式编写的程序源代码量小、开发时的工作量小、工作难度也较小,但编译后的代码量较大,运行速度略低;而 Windows C 编程方式编写的程序源代码量虽然较大,但可执行代码效率高。随着技术的进步,Visual C+编程方式已被广泛采用,但象网络编程等一些对速度要求高、对硬件操作较多的程序,大多数还是用 Windows C 编程方式开发的。另外,学习Wind

2、ows C 程序设计,还有助于更深入地了解 Windows 的内幕和 Windows API。基本的网络编程都是建立在 Winsock 基础上的。Winsock 是 90 年代初,为了方便网络编程,由Microsoft 联合了其他几家公司共同制定的一套 WINDOWS 下的网络编程接口,它是通过 C 语言的动态链接库方式提供给用户及软件开发者的,主要由 winsock.h 头文件和动态链接库 winsock.dll 组成,目前有两个版本:Winsock1.1 和 Winsock2.0。作为网络编程接口,Winsock 屏蔽了网络底层的复杂的协议和数据结构,使得编程人员对网络的操作变得非常简单,

3、因此,在 Win32 平台上,访问众多的基层网络协议,Winsock 是首选接口。用 Winsock 构建一个网络聊天室,有两种基本的方式:数据报方式和流方式。一、 面向无连接的数据报方式数据报方式又称无连接方式,对应的是 UDP(User Datagram Protocol)协议。这种方式不提供数据无错保证,数据可能丢失或重复并且接收顺序混乱,后发出的报文可能会先收到,并且报文的长度是有限制的;不过,由于取消了重发校验机制,能够达到较高的通信速率,可以用于对数据可靠性要求不高的通信,如实时的语音、图像传送和广播消息等。和 C 语言一样,函数是 Windows C 编程的最基本的单位。不过,W

4、indows C 主要使用 API 函数,而网络编程则主要使用 Winsock 提供的 API 函数。数据方式构建网络聊天室主要使用了以下几个函数:1WSAStartup():初始化。【函数原型】int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);【使用说明】每一个使用 winsock 的应用程序,都必须进行 WSAStart 函数调用,并且只有在调用成功之后才能使用其它的 winsock 网络操作函数。返回值:调用成功返回 0;否则,返回出错信息。WversionRequired:表示欲使用的 insoc

5、k 版本,这是一个 WORD 类型的整数,它的高位字节定义的是次版本号,低位字节定义的是主版本号。LpWSAData :是一个指向 WSADATA 资料的指针。这个资料我们一般不使用。2Socket():创建一个 Socket。【函数原型】SOCKET socket(int af,int type,int proctocol);【使用说明】Winsock 网络通信的第一步通常就是调用这个函数。所有的通信在建立之前都有要创建一个Socket。该函数的功能与文件操作中的 fopen()类似,返回值是由 Winsock 定义的一种数据类型SOCKET,它实际是一个整型数据,是 Socket 创建成功

6、时,Windows 分配给程序的 Socket 编号,后面调用传输函数时,可以把它像文件指针样引用。如果 Socket 建立失败,返回值WIVALID_SOCKET。Af:指 address family(地址族) ,一般都填 AF_INET,表示是在 Internet 上的 Socket;Type::是 Socket 的类型,当采用流连接方式时,用 SOCK_STREAM;采用数据报文C 语言课程设计第 2 页 共 13 页方式时,用 SOCK_DGRAM。Proctocol:一般都有为 0,表示对两种类型的 Socket 分别采用缺省的 TCP 和 UDP 传输协议。3Bind():为创建

7、 Socket 指定通信对象。【函数原型】int bind ( SOCKET s, const struct sockaddr FAR* name, int namelen );【使用说明】成功创建了 Socket 之后,就应该选定通信的对象。首先是自己的程序要与网上的哪台计算机通话;其次,在多任务的系统下,该台计算机上可能会有几个程序在工作,必须指出要与哪个程序通信。前者可以通过 IP 地址来确定,而后者则由端口号来确定的。一台计算机有65536 个端口,端口号范围为 065535,不同的通信程序使用不同的端口。不过,1024 以下的端口号一般都已被一些常用的网络服务程序所占用,因此,编制自

8、己的通信程序时,指定的端口号应大于 1024。s:上一步创建 Socket 时创建好的套接字。name:是指向描述通信对象地址信息的结构体 strict sockaddr_in 的指针。namelen:name 结构体的长度。Sockaddr_in 的定义如下:struct sockaddr_inshort sin_family;unsigned short sin_port;struct in_addr sin_addr;char sin_zero8;其中,sin_family 是指一套地址族,它指定所要使用的通信协议,通常设为AF_INET;sin_port 端口号;sin_addr 是

9、IP 地址;而 sin_zero8的作用,只是使该结构的大小和 SOCKADDR 结构大小相同。IP 地址 sin_addr 结构定义如下:struct in_addr union struct u_char s_b1,s_b2,s_b3,s_b4; S_un_b;struct u_short s_w1,s_w2; S_un_w;u_long S_addr; S_un;这样,对于一个 IP 地址,例如“192.168.0.1” ,你可以用以下三种方法赋给一个 sockaddr 结构体(例如 struct sockaddr_in m_addr;):方法 1:m_addr.sin_addr.S_u

10、n.S_un_b.s_b1=192;m_addr.sin_addr.S_un.S_un_b.s_b2=168;m_addr.sin_addr.S_un.S_un_b.s_b3=0;m_addr.sin_addr.S_un.S_un_b.s_b4=1;方法 2:m_addr.sin_addr.S_un.S_un_w.s_w1=(168:是连接用的 socket。buf、len:发送或接收的数据包字符串的地址和长度。flags:一般取 0。from、fromlen/to、tolen :含义和用法与 bind()中的相同,分别表示接收和发送数据的对象。5Closesocket():【函数原型】int

11、 closesocket ( SOCKET s);【使用说明】和关闭文件操作一样,socketd 在使用以后,也要关闭。Internet 上的聊天室程序一般都是 Client/Server 结构的,由服务器提供服务端连接响应,使用者通过客户端程序登录到服务器(面向接连的流方式) ,或直接向服务端发送报文(面向无连接的数据报方式) 。相应地,聊天室程序也就分为服务器端和客户端两部分。面向无连接的数据报方式的程序流程图如图 1 所示:图 1 面向无连接的数据报方式流程图可以为服务器端和客户端分别建立如图 1、图 2 所示对话框:调用 socket()创建一个监听 Socket调用 bind()监听

12、 socket 指定通讯对象调用 recvfrom()和 sendto()进行通讯调用 closesocket()关闭 socket调用 WSAStartup()初化 Winsock 调用 WSAStartup()初化 Winsock调用 closesocket()关闭 socket调用 recvfrom()和 sendto()进行通讯调用 socket()创建一个会话 Socket服务器端 客户端C 语言课程设计第 4 页 共 13 页图 1 服务器端 图 2 客户端相应的源程序见附件中的源程序 Chat_Room1。二、面向连接的流方式流方式又称无连接方式,对应的是 TCP(Transpo

13、rt Control Protocol)协议。在这种方式下,两个通信的应用程序之间先要建立一种连接链路,确定了这条链路之后,数据才能被正确接收和发送。流方式的特点是通信可靠,对数据有校验和重发的机制,通常用来做数据文件的传输如 FTP、Telnet 等。这种方式主要使用了以下几个函数:1Connect():【函数原型】int connect ( SOCKET s, const struct sockaddr FAR* name, int namelen);【使用说明】与通信对象建立连接,主要用在客户端。其中 s、name 和 namelen 的含义与使用方法和 bind()相同。如果连接失败,

14、该函数会返回 SOCKET_ERROR。2listen() :【函数原型】int listen (SOCKET s,int backlog);【使用说明】对于服务器端程序,当申请到 Socket,并指定通信对象为 INADDR_ANY 之后,就应该等待一个客户端程序的连接。当没有连接请求时,就进入等待状态,直至有一个请求到达为止。其中:s:是 socket()创建的 socket。backlog:等待连接的队列长度,可取 15。如果当某个客户程序要求连接之时,服务器已与其他客户程序连接,则后来的连接请求会被放在队列中,等待服务器空闲的时候再与之连接。当队列达到指定长度(backlog 的值)时

15、,再来的连接请求都将被拒绝。3accept():【函数原型】SOCKET accept (SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);【使用说明】对与服务器端程序,在接收到一个连接请求之后,要为这个连接建立一个新的 socket,这个任务由 accept()函数来完成,并把它作为返回值。新建的 Socket 与原来的 Socket 有相同的特性,包括端口号。原来的 Socket 用于继续等待其他的连接请求,而新生成的 Socket 才是与客户端进行通信的实际 Socket。一般将参数中的 SOCKET 称做“监听”Socket,它只

16、负责接受连接,不负责通话;而 accept 返回的 SOCKET 则称为“会话” Socket,它只负责与客户端通话。参数中的指针 addr 和 addrlen 用来返回客户机的 sockaddr_in 结构体,通过 addr 可得到客户机的IP 地址和连接端口。使用方法则与 bind()中的 name 和 namelen 相同。4recv()/send():【函数原型】C 语言课程设计第 5 页 共 13 页建立连接后,用来接收和发送数据。其中:s:是连接用的 socket。buf、len 和 flags 的含义与作用方法与 recvfrom()/connect()中的相同,分别表示接收和发

17、送的数据包字符串的地址、长度和标志。面向无连接的数据报方式的程序流程图如图 1 所示:图 2 面向连接的流方式流程图分别为服务器端和客户端建立如图 3、图 4 所示对话框:调用 WSAStartup()初化 Winsock调用 socket()创建一个监听 Socket调用 bind()为监听 Socket 指定通讯对象调用 listen()设置等待连接状态调用 accept()接收连接并生成会话 socket调用 send()和 recv()进行对话调用 closesocket()关闭 socket调用 WSAStartup()初化 Winsock调用 socket()创建一个会话 Sock

18、et调用 connect()与服务器端连接调用 send()和 recv()进行对话调用 closesocket()关闭 socket阻塞,等待用户连接阻塞,等待用户发送数据服务器端 客户端C 语言课程设计第 6 页 共 13 页图 3 图 4这是一个单方向传送的面向连接的的流方式聊天程序,源程序附在附件源程序 Chat_Room2 中,源代码比较简单,就不在这里另作说明了。三、面向连接的异步模式在上面的流方式中,函数 listen()要等到有客房端的连接请求或是出错时才能返回;recv()函数也要等到有数据发送过来的时候或是出错的时候才能返回。这时,如果网络拥挤或一次发送的数据量过大,交换的

19、数据不能在短时间内传送完, 收发数据的函数就因此不能返回,我们把这种现象叫做阻塞。在阻塞期间,除了等待网络操作完成不能进行任何操作。为了解决这一问题,Winsock 为我们提供了一种异步模式,在这种模式中,函数在被调用后立即返回,Winsock 通过函数 WSAAsyncSelect()来实现非阻塞通信。方法是,由该函数指定某种网络事件(如有数据到达、可以发送数据、有程序请求连接等) ,当被子指定的网络事件发生时,由 Winsock 发送由程序事先约定的消息。程序中就可以根据这些消息做出相应的处理。WSAAsyncSelect(),它的原型是:int WSAAsyncSelect (SOCKE

20、T s,HWND hWnd,unsigned int wMsg,long lEvent);Socket 在这个函数调用中被自动设成非阻塞方式,hWnd 是接收 Winsock 消息的窗口句柄,wMsg是向窗口发出的消息名称,用户可以任意定义。LEvent 是被指定的网络事件,如下表所示:网络事件 lEvent值 说明FD_READ 希望 Socket 收到数据时发送读的消息FD_WRITE 希望 Socket 发送数据时发送写的消息FD_OOB 希望 OOB data 到达时发送到达的消息FD_ACCEPT 希望有连接到来时发送连接请求的消息FD_CONNECT 希望完成连接时发送连接完成的消

21、息FD_CLOSE 希望接收 Socket 关闭的消息FD_QOS 希望接收 Socket 服务质量(QoS)变化的消息FD_GROUP_QOS 希望接收 Socket 服务质量(QoS)不变的消息FD_ROUTING_INTERFACE_CHANGE 希望接收指定的地址路由接口变化的消息FD_ADDRESS_LIST_CHANGE 希望接收 Socket 协议族局部地址变化的消息需要注意的是,WSAAsyncSelect()的设定是针对某一个 Socket 的,也就是说,只有当被设定为异步模式的 Socket 事件发生时,才会发送这些信息。如果开启了很多 Socket,而要让每个 Socke

22、t 都变成异步模式,那么就必须对每一个 Socket 都呼叫 WSAAsyncSelect()来一一设定。有了异步模式之后,可建立如下的服务器端和客户端程序,对话框如图 5、图 6 所示:C 语言课程设计第 7 页 共 13 页服务器端和客户端程序分别为:Chat_RoomS.h#define SER_MESSAGE WM_USER+100#define MaxNumber 5int WSA_return;WSADATA WSAData;SOCKET server_hSocket;SOCKET Listen_hSocket;struct sockaddr_in server_addr;stru

23、ct sockaddr_in Listen_addr;int Listen_addrlen=sizeof(Listen_addr);int fromlen=sizeof(server_addr);BOOL server_bErr;UINT server_uPort;char ShowText1024;char InputText256;char Receive_Text256;SOCKET nSocketMaxNumber;char *ClientIPMaxNumber;int i;char ComeMsg30=“ 走进聊天室rn“;char LeaveMsg30=“ 离开聊天室rn“;cha

24、r LeaveMsg130=“ 异常离开rn“;BOOL APIENTRY Hostname_ipDlgPro(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam);Chat_RoomS.c#include#include“resource.h“#include“Chat_RoomS.h“int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)DialogBox(hInstance,(LPCTSTR)“DIALOG

25、1“,NULL,(DLGPROC)Hostname_ipDlgPro);C 语言课程设计第 8 页 共 13 页return(TRUE);BOOL APIENTRY Hostname_ipDlgPro(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam)switch(message)case WM_INITDIALOG:WSA_return=WSAStartup(0x0002,if(WSA_return!=0)MessageBox(NULL,“初始化失败!“,“警告!“,MB_OK);WSACleanup();return TRUE;case

26、SER_MESSAGE:switch(lParam)case FD_ACCEPT:Listen_hSocket=accept(server_hSocket,(LPSOCKADDR)for(i=0;i0)MessageBox(NULL,“异步模式创建失败! “,“警告!“,MB_OK);ClientIPi=inet_ntoa(Listen_addr.sin_addr);break;if(i0)MessageBox(NULL,“异步模式创建失败! “,“警告!“,MB_OK);break;server_addr.sin_family=AF_INET;server_addr.sin_addr.S_u

27、n.S_addr=INADDR_ANY;server_uPort=GetDlgItemInt(hDlg,IDC_PORTUINT,server_addr.sin_port=htons(u_short)server_uPort);if(bind(server_hSocket,(LPSOCKADDR)break;if(listen(server_hSocket,5)=SOCKET_ERROR)MessageBox(NULL,“侦听失败!“,“警告!“,MB_OK);break;strcpy(ShowText,“服务器建立成功!rn“);SetDlgItemText(hDlg,IDC_SHOWTEX

28、T,ShowText);return TRUE;case IDC_SHOWTEXT:if(HIWORD(wParam)=EN_CHANGE)if(strlen(ShowText)1024-256)strcpy(ShowText,“);case IDC_INPUTTEXT:if(HIWORD(wParam)=EN_CHANGE)GetDlgItemText(hDlg,IDC_INPUTTEXT,InputText,256);if(InputTextstrlen(InputText)-1=n)strcat(ShowText,“管理员:rn “);strcat(ShowText,InputText)

29、;SetDlgItemText(hDlg,IDC_SHOWTEXT,ShowText);for(i=0;i#include“resource.h“#include“Chat_RoomC.h“int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)DialogBox(hInstance,(LPCTSTR)“DIALOG1“,NULL,(DLGPROC)Hostname_ipDlgPro);return(TRUE);C 语言课程设计第 12 页 共 13 页BOOL

30、APIENTRY Hostname_ipDlgPro(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam)switch(message)case WM_INITDIALOG:WSA_return=WSAStartup(0x0002,if(WSA_return!=0)MessageBox(NULL,“初始化失败!“,“警告!“,MB_OK);WSACleanup();return TRUE;case CLI_MESSAGE:switch(lParam)case FD_READ:len=recv(client_hSocket,ReceiveText

31、,256,0);ReceiveTextlen=0;strcat(ShowText,ReceiveText);SetDlgItemText(hDlg,IDC_SHOWTEXT,ShowText);return TRUE;case FD_WRITE:return TRUE;case FD_CLOSE:return TRUE;case FD_CONNECT:return TRUE;default:strcat(ShowText,“网络错误,连接失败!“);SetDlgItemText(hDlg,IDC_SHOWTEXT,ShowText);closesocket(client_hSocket);re

32、turn TRUE;case WM_COMMAND:switch(LOWORD(wParam)case IDC_IPADDRESS:if(HIWORD(wParam)=EN_CHANGE)GetDlgItemText(hDlg,IDC_IPADDRESS,ClientIP,20);break;case IDC_INPUTTEXT:if(HIWORD(wParam)=EN_CHANGE)GetDlgItemText(hDlg,IDC_INPUTTEXT,InputText,256);if(InputTextstrlen(InputText)-1=n)send(client_hSocket,Inp

33、utText,256,0);strcpy(InputText,“);SetDlgItemText(hDlg,IDC_INPUTTEXT,InputText);C 语言课程设计第 13 页 共 13 页return TRUE;case IDC_CONNECT:if(client_hSocket!=0)closesocket(client_hSocket);client_hSocket=0;if(client_hSocket=0)if(client_hSocket=socket(AF_INET,SOCK_STREAM,0)=SOCKET_ERROR)MessageBox(NULL,“建立套接字失败

34、! “,“警告!“,MB_OK);if(WSAAsyncSelect(client_hSocket,hDlg,CLI_MESSAGE,FD_READ|FD_WRITE|FD_CLOSE|FD_CONNECT)0)MessageBox(NULL,“异步模式创建失败 !“,“警告!“,MB_OK);client_addr.sin_family=AF_INET;client_addr.sin_addr.S_un.S_addr=inet_addr(ClientIP);client_uPort=GetDlgItemInt(hDlg,IDC_PORTTEXT,client_addr.sin_port=ht

35、ons(u_short)client_uPort);connect(client_hSocket,(LPSOCKADDR)break;case IDCANCEL:closesocket(client_hSocket);WSACleanup();EndDialog(hDlg,TRUE);return(TRUE);return(FALSE);利用 C 语言编写 Windows 应用程序有两种方式:一种是 Windows C 编程方式,另一种是 Visual C+编程方式。在一般情况下,Visual C+编程方式编写的程序源代码量小、开发时的工作量小、工作难度也较小,但编译后的代码量较大,运行速度略

36、低;而 Windows C 编程方式编写的程序源代码量虽然较大,但可执行代码效率高。随着技术的进步,Visual C+编程方式已被广泛采用,但象网络编程等一些对速度要求高、对硬件操作较多的程序,大多数还是用 Windows C 编程方式开发的。在上面的程序中,大家注意到,客户端程序,我并没有把要发送的信息直接显示在IDC_SHOWTEXT 编辑框中,而是在发送后,由服务器端再发给各个进入聊天室的客户,客户端程序连接完成,就开始接收服务器发送的信息,这样的客户端程序,事实上就是一个简单的端口扫描程序,有兴趣的读者可以试试连接不同 IP 地址机器的不同端口,就可以根据返回的的信息,判断这台机器都开启了哪些网络服务程序了。通过聊天室程序的编写,可以基本了解 Windows Sockets API 编程的基本过程和精要之处。本程序在 VC+6.0 下编译通过,在使用 windows 98/2000/XP/NT 的局域网里运行良好。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报