服务器端:
新建一个空的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链接库。