Windows C语言构建网络聊天室
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
作为网络编程接口,Winsock屏蔽了网络底层的复杂的协议和数据结构,使得编程人员对网络的操作变得非常简单,因此,在Win32平台上,访问众多的基层网络协议,Winsock是首选接口。
用Winsock构建一个网络聊天室,有两种基本的方式:数据报方式和流方式。
一、面向无连接的数据报方式
数据报方式又称无连接方式,对应的是UDP(User Datagram Protocol)协议。这种方式不提供数据无错保证,数据可能丢失或重复并且接收顺序混乱,后发出的报文可能会先收到,并且报文的长度是有限制的;不过,由于取消了重发校验机制,能够达到较高的通信速率,可以用于对数据可靠性要求不高的通信,如实时的语音、图像传送和广播消息等。
和C语言一样,函数是Windows C编程的最基本的单位。不过,Windows C主要使用API函数,而网络编程则主要使用Winsock提供的API函数。数据方式构建网络聊天室主要使用了以下几个函数:
1.WSAStartup():初始化。
【函数原型】
int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);
图1面向无连接的数据报方式流程图
可以为服务器端和客户端分别建立如图1、图2所示对话框:
图1服务器端图Байду номын сангаас客户端
相应的源程序见附件中的源程序Chat_Room1。
二、面向连接的流方式
流方式又称无连接方式,对应的是TCP(Transport Control Protocol))协议。在这种方式下,两个通信的应用程序之间先要建立一种连接链路,确定了这条链路之后,数据才能被正确接收和发送。流方式的特点是通信可靠,对数据有校验和重发的机制,通常用来做数据文件的传输如FTP、Telnet等。
flags<输入>:一般取0。
from、fromlen/to、tolen<输入>:含义和用法与bind()中的相同,分别表示接收和发送数据的对象。
5.Closesocket():
【函数原型】
int closesocket ( SOCKET s);
【使用说明】
和关闭文件操作一样,socketd在使用以后,也要关闭。
4.recv()/send():
【函数原型】
建立连接后,用来接收和发送数据。其中:
s<输入>:是连接用的socket。
buf、len和flags的含义与作用方法与recvfrom()/connect()中的相同,分别表示接收和发送的数据包字符串的地址、长度和标志。
面向无连接的数据报方式的程序流程图如图1所示:
【使用说明】
每一个使用winsock的应用程序,都必须进行WSAStart函数调用,并且只有在调用成功之后才能使用其它的winsock网络操作函数。
返回值:调用成功返回0;否则,返回出错信息。
WversionRequired<输入>:表示欲使用的Winsock版本,这是一个WORD类型的整数,它的高位字节定义的是次版本号,低位字节定义的是主版本号。
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;):
基本的网络编程都是建立在Winsock基础上的。Winsock是90年代初,为了方便网络编程,由Microsoft联合了其他几家公司共同制定的一套WINDOWS下的网络编程接口,它是通过C语言的动态链接库方式提供给用户及软件开发者的,主要由winsock.h头文件和动态链接库winsock.dll组成,目前有两个版本:Winsock1.1和Winsock2.0。
3.Bind():为创建Socket指定通信对象。
【函数原型】
int bind ( SOCKET s, const struct sockaddr FAR* name, int namelen );
【使用说明】
成功创建了Socket之后,就应该选定通信的对象。首先是自己的程序要与网上的哪台计算机通话;其次,在多任务的系统下,该台计算机上可能会有几个程序在工作,必须指出要与哪个程序通信。前者可以通过IP地址来确定,而后者则由端口号来确定的。一台计算机有65536个端
口,端口号范围为0~65535,不同的通信程序使用不同的端口。不过,1024以下的端口号一般都已被一些常用的网络服务程序所占用,因此,编制自己的通信程序时,指定的端口号应大于1024。
s<输入>:上一步创建Socket时创建好的套接字。
name<输入>:是指向描述通信对象地址信息的结构体strict sockaddr_in的指针。
Internet上的聊天室程序一般都是Client/Server结构的,由服务器提供服务端连接响应,使用者通过客户端程序登录到服务器(面向接连的流方式),或直接向服务端发送报文(面向无连接的数据报方式)。相应地,聊天室程序也就分为服务器端和客户端两部分。
面向无连接的数据报方式的程序流程图如图1所示:
m_addr.sin_addr.S_un.S_un_w.s_w1=(168<<8)|192;
m_addr.sin_addr.S_un.S_un_w.s_w2=(1<<8)|0;
方法3:
m_addr.sin_addr.S_un.S_addr=(1<<24)|(0<<16)|(168<<8)|192;
为了更方便地赋值,winsock还为我们提供了一个函数inet_addr(),可以把用字符串表示的IP地址“192.168.0.1”直接赋给结构体m_addr:
图2面向连接的流方式流程图
分别为服务器端和客户端建立如图3、图4所示对话框:
图3图4
这是一个单方向传送的面向连接的的流方式聊天程序,源程序附在附件源程序Chat_Room2中,源代码比较简单,就不在这里另作说明了。
namelen<输入>:name结构体的长度。
Sockaddr_in的定义如下:
struct sockaddr_in
{
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
其中,sin_family是指一套地址族,它指定所要使用的通信协议,通常设为AF_INET;sin_port端口号;sin_addr是IP地址;而sin_zero[8]的作用,只是使该结构的大小和SOCKADDR结构大小相同。
LpWSAData<输出>:是一个指向WSADATA资料的指针。这个资料我们一般不使用。
2.Socket():创建一个Socket。
【函数原型】
SOCKETsocket(int af,int type,int proctocol);
【使用说明】
Winsock网络通信的第一步通常就是调用这个函数。所有的通信在建立之前都有要创建一个Socket。该函数的功能与文件操作中的fopen()类似,返回值是由Winsock定义的一种数据类型SOCKET,它实际是一个整型数据,是Socket创建成功时,Windows分配给程序的Socket编号,后面调用传输函数时,可以把它像文件指针样引用。如果Socket建立失败,返回值WIVALID_SOCKET。
int FAR* fromlen );
int sendto (SOCKET s,const char FAR * buf, int len, int flags,const struct sockaddr FAR * to,
int tolen);
【使用说明】
s<输入>:是连接用的socket。
buf、len<输入>:发送或接收的数据包字符串的地址和长度。
2.listen():
【函数原型】
int listen (SOCKET s,int backlog);
【使用说明】
对于服务器端程序,当申请到Socket,并指定通信对象为INADDR_ANY之后,就应该等待一个客户端程序的连接。当没有连接请求时,就进入等待状态,直至有一个请求到达为止。其中:
s<输入>:是socket()创建的socket。
char * IP_String=”192.168.0.1”;
m_addr.sin_addr.S_un.S_addr=inet_addr(IP_String);
4.recvfrom()/sendto():
【函数原型】
int recvfrom ( SOCKET s,char FAR* buf, int len,int flags,struct sockaddr FAR* from,
这种方式主要使用了以下几个函数:
1.Connect():
【函数原型】
int connect ( SOCKET s, const struct sockaddr FAR* name, int namelen);
【使用说明】
与通信对象建立连接,主要用在客户端。其中s、name和namelen的含义与使用方法和bind()相同。如果连接失败,该函数会返回SOCKET_ERROR。
【使用说明】
对与服务器端程序,在接收到一个连接请求之后,要为这个连接建立一个新的socket,这个任务由accept()函数来完成,并把它作为返回值。新建的Socket与原来的Socket有相同的特性,包括端口号。原来的Socket用于继续等待其他的连接请求,而新生成的Socket才是与客户端进行通信的实际Socket。一般将参数中的SOCKET称做“监听”Socket,它只负责接受连接,不负责通话;而accept返回的SOCKET则称为“会话”Socket,它只负责与客户端通话。参数中的指针addr和addrlen用来返回客户机的sockaddr_in结构体,通过addr可得到客户机的IP地址和连接端口。使用方法则与bind()中的name和namelen相同。
方法1:
m_addr.sin_addr.S_un.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:
Windows C语言构建网络聊天室
利用C语言编写Windows应用程序有两种方式:一种是WindowsC编程方式,另一种是VisualC++编程方式。在一般情况下,VisualC++编程方式编写的程序源代码量小、开发时的工作量小、工作难度也较小,但编译后的代码量较大,运行速度略低;而WindowsC编程方式编写的程序源代码量虽然较大,但可执行代码效率高。随着技术的进步,VisualC++编程方式已被广泛采用,但象网络编程等一些对速度要求高、对硬件操作较多的程序,大多数还是用WindowsC编程方式开发的。另外,学习Windows C程序设计,还有助于更深入地了解Windows的内幕和Windows API。
Af<输入>:指address family(地址族),一般都填AF_INET,表示是在Internet上的Socket;
Type<输入>::是Socket的类型,当采用流连接方式时,用SOCK_STREAM;采用数据报文方式时,用SOCK_DGRAM。
Proctocol<输入>:一般都有为0,表示对两种类型的Socket分别采用缺省的TCP和UDP传输协议。
backlog<输入>:等待连接的队列长度,可取1~5。如果当某个客户程序要求连接之时,服务器已与其他客户程序连接,则后来的连接请求会被放在队列中,等待服务器空闲的时候再与之连接。当队列达到指定长度(backlog的值)时,再来的连接请求都将被拒绝。
3.accept():
【函数原型】
SOCKET accept (SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);
用Winsock构建一个网络聊天室,有两种基本的方式:数据报方式和流方式。
一、面向无连接的数据报方式
数据报方式又称无连接方式,对应的是UDP(User Datagram Protocol)协议。这种方式不提供数据无错保证,数据可能丢失或重复并且接收顺序混乱,后发出的报文可能会先收到,并且报文的长度是有限制的;不过,由于取消了重发校验机制,能够达到较高的通信速率,可以用于对数据可靠性要求不高的通信,如实时的语音、图像传送和广播消息等。
和C语言一样,函数是Windows C编程的最基本的单位。不过,Windows C主要使用API函数,而网络编程则主要使用Winsock提供的API函数。数据方式构建网络聊天室主要使用了以下几个函数:
1.WSAStartup():初始化。
【函数原型】
int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);
图1面向无连接的数据报方式流程图
可以为服务器端和客户端分别建立如图1、图2所示对话框:
图1服务器端图Байду номын сангаас客户端
相应的源程序见附件中的源程序Chat_Room1。
二、面向连接的流方式
流方式又称无连接方式,对应的是TCP(Transport Control Protocol))协议。在这种方式下,两个通信的应用程序之间先要建立一种连接链路,确定了这条链路之后,数据才能被正确接收和发送。流方式的特点是通信可靠,对数据有校验和重发的机制,通常用来做数据文件的传输如FTP、Telnet等。
flags<输入>:一般取0。
from、fromlen/to、tolen<输入>:含义和用法与bind()中的相同,分别表示接收和发送数据的对象。
5.Closesocket():
【函数原型】
int closesocket ( SOCKET s);
【使用说明】
和关闭文件操作一样,socketd在使用以后,也要关闭。
4.recv()/send():
【函数原型】
建立连接后,用来接收和发送数据。其中:
s<输入>:是连接用的socket。
buf、len和flags的含义与作用方法与recvfrom()/connect()中的相同,分别表示接收和发送的数据包字符串的地址、长度和标志。
面向无连接的数据报方式的程序流程图如图1所示:
【使用说明】
每一个使用winsock的应用程序,都必须进行WSAStart函数调用,并且只有在调用成功之后才能使用其它的winsock网络操作函数。
返回值:调用成功返回0;否则,返回出错信息。
WversionRequired<输入>:表示欲使用的Winsock版本,这是一个WORD类型的整数,它的高位字节定义的是次版本号,低位字节定义的是主版本号。
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;):
基本的网络编程都是建立在Winsock基础上的。Winsock是90年代初,为了方便网络编程,由Microsoft联合了其他几家公司共同制定的一套WINDOWS下的网络编程接口,它是通过C语言的动态链接库方式提供给用户及软件开发者的,主要由winsock.h头文件和动态链接库winsock.dll组成,目前有两个版本:Winsock1.1和Winsock2.0。
3.Bind():为创建Socket指定通信对象。
【函数原型】
int bind ( SOCKET s, const struct sockaddr FAR* name, int namelen );
【使用说明】
成功创建了Socket之后,就应该选定通信的对象。首先是自己的程序要与网上的哪台计算机通话;其次,在多任务的系统下,该台计算机上可能会有几个程序在工作,必须指出要与哪个程序通信。前者可以通过IP地址来确定,而后者则由端口号来确定的。一台计算机有65536个端
口,端口号范围为0~65535,不同的通信程序使用不同的端口。不过,1024以下的端口号一般都已被一些常用的网络服务程序所占用,因此,编制自己的通信程序时,指定的端口号应大于1024。
s<输入>:上一步创建Socket时创建好的套接字。
name<输入>:是指向描述通信对象地址信息的结构体strict sockaddr_in的指针。
Internet上的聊天室程序一般都是Client/Server结构的,由服务器提供服务端连接响应,使用者通过客户端程序登录到服务器(面向接连的流方式),或直接向服务端发送报文(面向无连接的数据报方式)。相应地,聊天室程序也就分为服务器端和客户端两部分。
面向无连接的数据报方式的程序流程图如图1所示:
m_addr.sin_addr.S_un.S_un_w.s_w1=(168<<8)|192;
m_addr.sin_addr.S_un.S_un_w.s_w2=(1<<8)|0;
方法3:
m_addr.sin_addr.S_un.S_addr=(1<<24)|(0<<16)|(168<<8)|192;
为了更方便地赋值,winsock还为我们提供了一个函数inet_addr(),可以把用字符串表示的IP地址“192.168.0.1”直接赋给结构体m_addr:
图2面向连接的流方式流程图
分别为服务器端和客户端建立如图3、图4所示对话框:
图3图4
这是一个单方向传送的面向连接的的流方式聊天程序,源程序附在附件源程序Chat_Room2中,源代码比较简单,就不在这里另作说明了。
namelen<输入>:name结构体的长度。
Sockaddr_in的定义如下:
struct sockaddr_in
{
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
其中,sin_family是指一套地址族,它指定所要使用的通信协议,通常设为AF_INET;sin_port端口号;sin_addr是IP地址;而sin_zero[8]的作用,只是使该结构的大小和SOCKADDR结构大小相同。
LpWSAData<输出>:是一个指向WSADATA资料的指针。这个资料我们一般不使用。
2.Socket():创建一个Socket。
【函数原型】
SOCKETsocket(int af,int type,int proctocol);
【使用说明】
Winsock网络通信的第一步通常就是调用这个函数。所有的通信在建立之前都有要创建一个Socket。该函数的功能与文件操作中的fopen()类似,返回值是由Winsock定义的一种数据类型SOCKET,它实际是一个整型数据,是Socket创建成功时,Windows分配给程序的Socket编号,后面调用传输函数时,可以把它像文件指针样引用。如果Socket建立失败,返回值WIVALID_SOCKET。
int FAR* fromlen );
int sendto (SOCKET s,const char FAR * buf, int len, int flags,const struct sockaddr FAR * to,
int tolen);
【使用说明】
s<输入>:是连接用的socket。
buf、len<输入>:发送或接收的数据包字符串的地址和长度。
2.listen():
【函数原型】
int listen (SOCKET s,int backlog);
【使用说明】
对于服务器端程序,当申请到Socket,并指定通信对象为INADDR_ANY之后,就应该等待一个客户端程序的连接。当没有连接请求时,就进入等待状态,直至有一个请求到达为止。其中:
s<输入>:是socket()创建的socket。
char * IP_String=”192.168.0.1”;
m_addr.sin_addr.S_un.S_addr=inet_addr(IP_String);
4.recvfrom()/sendto():
【函数原型】
int recvfrom ( SOCKET s,char FAR* buf, int len,int flags,struct sockaddr FAR* from,
这种方式主要使用了以下几个函数:
1.Connect():
【函数原型】
int connect ( SOCKET s, const struct sockaddr FAR* name, int namelen);
【使用说明】
与通信对象建立连接,主要用在客户端。其中s、name和namelen的含义与使用方法和bind()相同。如果连接失败,该函数会返回SOCKET_ERROR。
【使用说明】
对与服务器端程序,在接收到一个连接请求之后,要为这个连接建立一个新的socket,这个任务由accept()函数来完成,并把它作为返回值。新建的Socket与原来的Socket有相同的特性,包括端口号。原来的Socket用于继续等待其他的连接请求,而新生成的Socket才是与客户端进行通信的实际Socket。一般将参数中的SOCKET称做“监听”Socket,它只负责接受连接,不负责通话;而accept返回的SOCKET则称为“会话”Socket,它只负责与客户端通话。参数中的指针addr和addrlen用来返回客户机的sockaddr_in结构体,通过addr可得到客户机的IP地址和连接端口。使用方法则与bind()中的name和namelen相同。
方法1:
m_addr.sin_addr.S_un.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:
Windows C语言构建网络聊天室
利用C语言编写Windows应用程序有两种方式:一种是WindowsC编程方式,另一种是VisualC++编程方式。在一般情况下,VisualC++编程方式编写的程序源代码量小、开发时的工作量小、工作难度也较小,但编译后的代码量较大,运行速度略低;而WindowsC编程方式编写的程序源代码量虽然较大,但可执行代码效率高。随着技术的进步,VisualC++编程方式已被广泛采用,但象网络编程等一些对速度要求高、对硬件操作较多的程序,大多数还是用WindowsC编程方式开发的。另外,学习Windows C程序设计,还有助于更深入地了解Windows的内幕和Windows API。
Af<输入>:指address family(地址族),一般都填AF_INET,表示是在Internet上的Socket;
Type<输入>::是Socket的类型,当采用流连接方式时,用SOCK_STREAM;采用数据报文方式时,用SOCK_DGRAM。
Proctocol<输入>:一般都有为0,表示对两种类型的Socket分别采用缺省的TCP和UDP传输协议。
backlog<输入>:等待连接的队列长度,可取1~5。如果当某个客户程序要求连接之时,服务器已与其他客户程序连接,则后来的连接请求会被放在队列中,等待服务器空闲的时候再与之连接。当队列达到指定长度(backlog的值)时,再来的连接请求都将被拒绝。
3.accept():
【函数原型】
SOCKET accept (SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);