新建一个空的win32控制台项目,然后新建一个ChatUdpSrv.cpp源文件,源文件内容如下:
#include <Winsock2.h> //包含socket的dll相关头文件 #include<stdio.h> //以下例子中用到C语言相关的库函数 void main() { //1 加载套接字库dll WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested=MAKEWORD(1,1); //1.1分别表示主副版本号,将1.1转为Word类型的一个值 err=WSAStartup(wVersionRequested, &wsaData);//初始化套接字dll,参数1指示版本,参数2输是输出参数,接收返回的描述信息 if( err!= 0){ return; } if(LOBYTE( wsaData.wVersion) != 1 || //分别取出低字节和高字节判断是不是需要的版本号 HIBYTE( wsaData.wVersion) != 1 ){ WSACleanup(); //清理套接字dll return; } //2 创建一个空的套接字,基于数据报的类型 SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0); //三个参数分别表示:地址族,流还是数据报类型,协议(0表示由系统选) //3 将空的套接字绑定到一个地址结构上(此结构包括3个信息:地址族,ip地址,端口号) //用到函数bind(空socket,一个地址结构SOCKADDR的指针,地址结构的大小),所以关键是建立一个地址结构,分以下3步 //3.1 建立一个地址结构SOCKADDR_IN类型(简化了的地址结构,类似SOCKADDR,以后可以强转过去),不用考虑网络字节序 SOCKADDR_IN addrSrv; //3.2 给该简化版地址结构的地址族成员赋值(所有成员前缀为sin_,由SOCKADDR_IN取两个单词首字母简化而来) addrSrv.sin_family= AF_INET; //3.3 给该简化版地址结构的ip地址成员赋值,但要注意,ip地址成员本身也是一个结构体类型, //该结构体里面包含一个联合类型成员,此成员名为S_un(由Struct的Union成员缩写而成,即父子组合)。 //此联合成员里面又包含一个成员叫S_addr(缩写道理同上),它才最终指代一个ip地址。 //另外,包中的数据如ip地址有一个网络字节序的问题,原因是有点电脑高位地址代表高位数据,其它的电脑可能相反 //所以此时需要用htonl或htons函数将当前电脑的数据转换成网络字节序的数据(但地址族成员不用转换), //其中htonl表示将host主机字节序的u_long类型转成网络net字节序,而htons将host的u_shot(无符号短整形)转换为网络字节序 //所以最终赋值写法如下: addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY); //INADDR_ANY代表多网卡中任选一个ip地址,相当于192.168.xx.xx对应的无符号整形形式 //3.4 给该简化版的地址结构(体)的端口号成员赋值,也要考虑网络字节序的问题 addrSrv.sin_port=htons(6000);//用户自定义端口号要大于1024 //3.5 将空套接字和上诉的地址结构(体)绑定(bind)起来 //此时使用bind函数,注意地址类型强制转化的写法,最后一个参数表示地址结构体大小 bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); char recvBuf[100];//接收缓存 char sendBuf[100];//发送缓存 char tempBuf[200];//用来存放中间数据 SOCKADDR_IN addrClient; int len=sizeof(SOCKADDR); //这两个东西用来存放客户端的地址结构体信息 while(1) { recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len); if('q'==recvBuf[0]) //如果客户端发送的是q,则退出 { //回复一个q,然后退出。目的是,服务器或客户端都可以主动发出q,以便结束对话 sendto(sockSrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClient,len); printf("Chat end!\n"); break;//聊天终止则退出循环 } sprintf(tempBuf,"%s say:%s",inet_ntoa(addrClient.sin_addr),recvBuf);//格式化收到的数据,准备打印输出 printf("%s\n",tempBuf); printf("Please input data to client:\n"); gets(sendBuf);//C语言标准函数,将数据的数据放入缓存中,以回车符为结束标记 sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClient,len);//将缓存数据发给客户端 } closesocket(sockSrv);//关闭套接字 WSACleanup();//清理套接字dll //getchar();//VC++用这个暂停住dos界面,或用system("pause");后者好在显示一行“按任意键继续” system("pause"); }
新建一个空的win32控制台项目,然后新建一个ChatUdpClient.cpp源文件,源文件内容如下:
#include <Winsock2.h> //包含socket的dll相关头文件 #include<stdio.h> //以下例子中用到C语言相关的库函数 void main() { //1 加载套接字库dll WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested=MAKEWORD(1,1); //1.1分别表示主副版本号,将1.1转为Word类型的一个值 err=WSAStartup(wVersionRequested, &wsaData);//初始化套接字dll,参数1指示版本,参数2输是输出参数,接收返回的描述信息 if( err!= 0){ return; } if(LOBYTE( wsaData.wVersion) != 1 || //分别取出低字节和高字节判断是不是需要的版本号 HIBYTE( wsaData.wVersion) != 1 ){ WSACleanup(); //清理套接字dll return; } //2 创建一个空的套接字,基于数据报的类型 SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0); //三个参数分别表示:地址族,流还是数据报类型,协议(0表示由系统选) //3 定义要连接的服务器地址结构体,然后直接用上面空的套接字向这个地址信息发送数据 SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); addrSrv.sin_family=AF_INET; addrSrv.sin_port=htons(6000); char recvBuf[100]; char sendBuf[100]; char tempBuf[200]; int len=sizeof(SOCKADDR);//用来保存服务器的地址结构体信息,至于SOCKADDR_IN变量就用上面3定义的addrSrv即可 while(1) { printf("Please input data to server:\n"); gets(sendBuf); sendto(sockClient,sendBuf,strlen(sendBuf)+1,0, //用上面定义的空套接字直接向服务器地址发信息 (SOCKADDR*)&addrSrv,len); recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrSrv,&len);//接收客户端发来的数据到recvBuf中 if('q'==recvBuf[0]) //如果是服务器发来主动要求退出聊天的要求,客户端也要响应并结束对话 { sendto(sockClient,"q",strlen("q")+1,0, (SOCKADDR*)&addrSrv,len); printf("Chat end!\n"); break; } sprintf(tempBuf,"%s say: %s",inet_ntoa(addrSrv.sin_addr),recvBuf);//格式化收到的数据并准备打印输出 printf("%s\n",tempBuf); } closesocket(sockClient);//关闭套接字 WSACleanup();//清理套接字dll //getchar();//VC++用这个暂停住dos界面,或用system("pause");后者好在显示一行“按任意键继续” system("pause"); }
初始:
服务器端:
什么内容都没有
客户端:
Please input data to server:
当客户端输入内容“I am clientA.”时,
客户端:
Please input data to server:
I am clientA.
服务器端:
127.0.0.1 say:I am clientA.
Please input data to client:
当服务器端输入内容“Hello,my name is server.”时,
服务器端:
127.0.0.1 say:I am clientA.
Please input data to client:
Hello,my name is server.
客户端:
Please input data to server:
I am clientA.
127.0.0.1 say:Hello,my name is server.
Please input data to server:
然后当,客户端发出“q”时,(注,此程序也可以由服务器发出退出请求)
客户端:
Please input data to server:
I am clientA.
127.0.0.1 say:Hello,my name is server.
Please input data to server:
q
Chat end!
请按任意键继续...
服务器端:
127.0.0.1 say:I am clientA.
Please input data to client:
Hello,my name is server.
Chat end!
请按任意键继续...
最后补充一点:所有项目都在vs2010中测试运行过,所有项目都需要在属性-连接器-附加依赖项,添加ws2_32.lib引入库即可运行。系统会自动在系统目录下寻找到相关的dll链接库。