UDP聊天程序学习笔记
2013-10-23 21:44
369 查看
近日看了孙鑫VC++视频,14及15课,了解了网络编程相关内容,并从网上找了相关聊天程序实现的代码进行学习。
UDP通信实例源文件下载地址:http://download.csdn.net/detail/fengdongjingquan/6460787
MFC相关背景知识:MFC的运行机制 以及 MFC中的DC、CDC、HDC、句柄、设备上下文
关于项目转换:VC 6.0工程向VS
2010转换的问题
网络编程基础:WS网络开发——第1篇
udp 服务端如何知道客户端socket?
如果是tcp,通过accept得到一个socket,然后向客户端发信息是就用这个socket,但是udp没有accept,当我要分发消息到多个客户端时要如何做?
你说的 udp服务端如何知道客户端socket,应该就是怎么知道客户端的IP地址和UDP端口。一般来说有两种方式:
一种是客户端发消息显示显式地告诉服务器IP地址和端口,消息内容就包括IP地址和UDP端口。
另外一种就是隐式的,服务器从收到的包的头部中得到包的源IP地址和端口。
通常udp服务端根本不需要知道客户端的socket,它直接建立一个socket用于发送即可,udp通信的关键只在于IP和端口。多个客户端如果需要点到点分发,必须给服务端socket循环设置每个客户端的IP并发出,但更常用的是广播分发,服务端socket设定一个224.X.X.X的广播地址并始终向它发送,每个客户端建立的socket只需要绑定这个广播地址便可以收到。
孙鑫程序的源码:此部分由于编译环境的改变(VC 6.0--->VS 2010)对代码进行了一些改动。从网上也看到了对于孙鑫编程序习惯的一些批评,由于初学,只是以能运行出结果为主。
关于代码转换:http://bbs.csdn.net/topics/380020531
由于未加载相应的库可能会出现类似于“error LNK2019: 无法解析的外部符号_WinMain@16,该符号在函数 ___tmainCRTStartup 中被引用”这样的错误。需要加载相应的库,在VS 2010中加载方式为:
同时,在调试过程中也可能出现10054错误:
解决办法:http://blog.csdn.net/ccnucjp8136/article/details/4515002
http://bbs.csdn.net/topics/90240718 --->#14
UdpSrv:
运行的时候不知道为什么不能同时对两个项目进行编译执行(不能像视频中的那样分别选择两个项目,先启动服务器端再启动客户端)。可以通过编译生成的Debug文件下的.exe文件,先执行UdpSrv.exe,再找到UdpClient项目下Debug中的UdpClient.exe,这样分别执行即可看到视频中的结果。即,执行完客户端程序后,服务器显示“hello”。
另外,在CSDN上面找到一个类似的程序,而且有详细的注释说明,一并贴上来。
资源地址:http://download.csdn.net/detail/sh167779/4245364
UdpClient:
UdpSrv:
执行结果
UDP通信实例源文件下载地址:http://download.csdn.net/detail/fengdongjingquan/6460787
背景知识
Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)MFC相关背景知识:MFC的运行机制 以及 MFC中的DC、CDC、HDC、句柄、设备上下文
关于项目转换:VC 6.0工程向VS
2010转换的问题
网络编程基础:WS网络开发——第1篇
相关问题及解答
疑问及解答:http://bbs.csdn.net/topics/330194207如果是tcp,通过accept得到一个socket,然后向客户端发信息是就用这个socket,但是udp没有accept,当我要分发消息到多个客户端时要如何做?
你说的 udp服务端如何知道客户端socket,应该就是怎么知道客户端的IP地址和UDP端口。一般来说有两种方式:
一种是客户端发消息显示显式地告诉服务器IP地址和端口,消息内容就包括IP地址和UDP端口。
另外一种就是隐式的,服务器从收到的包的头部中得到包的源IP地址和端口。
通常udp服务端根本不需要知道客户端的socket,它直接建立一个socket用于发送即可,udp通信的关键只在于IP和端口。多个客户端如果需要点到点分发,必须给服务端socket循环设置每个客户端的IP并发出,但更常用的是广播分发,服务端socket设定一个224.X.X.X的广播地址并始终向它发送,每个客户端建立的socket只需要绑定这个广播地址便可以收到。
孙鑫程序的源码:此部分由于编译环境的改变(VC 6.0--->VS 2010)对代码进行了一些改动。从网上也看到了对于孙鑫编程序习惯的一些批评,由于初学,只是以能运行出结果为主。
关于代码转换:http://bbs.csdn.net/topics/380020531
由于未加载相应的库可能会出现类似于“error LNK2019: 无法解析的外部符号_WinMain@16,该符号在函数 ___tmainCRTStartup 中被引用”这样的错误。需要加载相应的库,在VS 2010中加载方式为:
#pragma comment(lib,"ws2_32.lib")
同时,在调试过程中也可能出现10054错误:
解决办法:http://blog.csdn.net/ccnucjp8136/article/details/4515002
http://bbs.csdn.net/topics/90240718 --->#14
试试 BOOL bNewBehavior = FALSE; DWORD dwBytesReturned; WSAIoctl(m_Sckt, SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL); 其中 m_Sckt 是UDP SOCKET |
程序代码
UdpClient:#include <Winsock2.h> #include <stdio.h> #pragma comment(lib,"ws2_32.lib") void main() { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 1 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { return; } if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) { WSACleanup( ); return; } SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0); 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(6666); sendto(sockClient,"Hello everybody!",strlen("Hello everybody!")+1,0, (SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); closesocket(sockClient); WSACleanup(); }
UdpSrv:
#include <Winsock2.h> #include <stdio.h> #pragma comment(lib,"ws2_32.lib") void main() { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 1 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { return; } if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) { WSACleanup( ); return; } SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0); SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY); addrSrv.sin_family=AF_INET; addrSrv.sin_port=htons(6666); bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); SOCKADDR_IN addrClient; int len=sizeof(SOCKADDR); char recvBuf[100]={0}; recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len); printf_s("%s\n",recvBuf); closesocket(sockSrv); WSACleanup(); }
运行的时候不知道为什么不能同时对两个项目进行编译执行(不能像视频中的那样分别选择两个项目,先启动服务器端再启动客户端)。可以通过编译生成的Debug文件下的.exe文件,先执行UdpSrv.exe,再找到UdpClient项目下Debug中的UdpClient.exe,这样分别执行即可看到视频中的结果。即,执行完客户端程序后,服务器显示“hello”。
另外,在CSDN上面找到一个类似的程序,而且有详细的注释说明,一并贴上来。
资源地址:http://download.csdn.net/detail/sh167779/4245364
UdpClient:
#include <Winsock2.h> #include <stdio.h> #include <time.h>//声明应用的头文件 #pragma comment(lib,"ws2_32.lib")//引用ws2_32.lib库。VC6.0不用此命令!!! void time()//时间子函数,用于获取当前时间并显示出来 { time_t t;//定义日历时间结构体t struct tm * tm;//定义时间结构体变量tm t= time(NULL);//获取1900年至现在的秒数 tm=localtime(&t);//将秒数转换为当前时间,默认为ANSI C标准时间格式 printf("%04d年%02d月%02d日 %02d:%02d:%02d ",tm->tm_year+1900, tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec); } //将ANSI C标准时间格式转换为中国常用的表示方式并显示出来 void main()//主函数,实现通信功能 {//加载套接字库 WORD wVersionRequested;//wVersionRequested参数用于指定准备加载的Winsock库的版本。 //高位字节指定所需要的Winsock库的副版本,而低位字节则是主版本。 WSADATA wsaData;//参数是指向WSADATA结构的指针,WSAStartup用其加载的库版本有关的信息填在这个结构中。 int err; wVersionRequested = MAKEWORD( 1, 1 ); // Winsock版本(1.1) err = WSAStartup( wVersionRequested, &wsaData ); //加载套接字库;进行套接字库版本的协商 if ( err != 0 ) { return; }//如果不能找到合适的Winsock,程序退出 if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {//判断版本是否是1.1.若不是则WSACleanup()终止调用 WSACleanup();//WSAData的wVersion成员中将包含你的应用程序应该使用的版本, //它是DLL所支持的最高版本与请求版本中较小的那个。 //对于每一个WSAStartup的成功调用(成功加载WinSock DLL后), //在最后都对应一个WSACleanUp调用,以便释放为该应用程序分配的资源。 return; //未找到合适版本的Winsock就返回 } printf("欢迎使用本软件! \n"); SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);//该函数接收三个参数。 //第一个参数af指定地址族,对于TCP/IP协议的套接字,它只能是AF_INET(也可写成PF_INET)。 //第二个参数指定Socket类型,对于1.1版本的Socket,它只支持两种类型的套接字,SOCK_STREAM指定产生流式套接字,SOCK_DGRAM产生数据报套接字。 //第三个参数是与特定的地址家族相关的协议,如果指定为0,那么它就会根据地址格式和套接字类别,自动为你选择一个合适的协议。 SOCKADDR_IN addrSrv;//定义地址结构体变量addrSrv addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//设置客户端地址为127.0.0.1 addrSrv.sin_family=AF_INET;//指定该地址家族,在这里必须设为AF_INET。 addrSrv.sin_port=htons(5500); //(u_short)从主机字节序转换为网络字节序 ,注意要用1024以上的端口号。 char sayBuf[]={"说:\n "}; char ncBuf[20];//定义昵称数组 char dataBuf[80];//定义说话内容数组 char sendBuf[100];//定义发送数组 char recvBuf[100];//定义接收数组 char tempBuf[200];//定义临时数组 printf("请设置你的昵称:"); gets(ncBuf); strcat(ncBuf,sayBuf);//StrCat function //Appends one string to another. //Note Do not use. See Remarks for alternative functions printf("设置成功!\n"); int i; i=strlen(ncBuf)+1;//获取ncBuf的长度 int len=sizeof(SOCKADDR);//定义整形变量len,存放SOCKADDR的长度 while(1)//死循环,使程序一直处于监听状态 { char sendBuf[100]={" "}; strcat(sendBuf,ncBuf); printf("请输入聊天内容或按z键终止聊天: "); gets(dataBuf);//将从键盘输入的数据存入sendBuf time(); printf("%s",ncBuf); printf("%s \n",dataBuf); strcat(sendBuf,dataBuf); sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrSrv,len);//将从键盘输入的数据存入sendBuf printf("请等待对方回话... \n"); recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrSrv,&len);//接受数据 if('z'==recvBuf[0]) { sendto(sockClient,"通话已终止,请退出!",strlen("通话已终止,请退出!")+1,0,(SOCKADDR*)&addrSrv,len); printf("通话已终止,请退出... \n"); break; }//判断收到的数据是否是z,若是则发送回一个z,并终止程序。 sprintf(tempBuf,"(%s) %s ",inet_ntoa(addrSrv.sin_addr),recvBuf);//inrt_ntoa将in_addr结构体类型的参数化成点分十进制的IP地址字符串 //将recvBuf、IP地址字符串和"%s 说: \n %s"格式化到tempBuf中。 time();//调用时间子函数,显示当前时间 printf("主机"); printf(" %s\n",tempBuf);//显示tempBuf中的内容 } closesocket(sockClient);//关闭套接字,释放为程序调用的资源 WSACleanup();//WSACleanup()终止调用 system("pause");//等待用户关闭界面 }
UdpSrv:
#include <Winsock2.h> #include <stdio.h> #include <time.h>//声明应用的头文件 #pragma comment(lib,"ws2_32.lib")//引用ws2_32.lib库。VC6.0不用此命令!!! void time()//时间子函数,用于获取当前时间并显示出来 { time_t t;//定义日历时间结构体t struct tm * tm;//定义时间结构体变量tm t= time(NULL);//获取1900年至现在的秒数 tm=localtime(&t);//将秒数转换为当前时间,默认为ANSI C标准时间格式 printf("%04d年%02d月%02d日 %02d:%02d:%02d ",tm->tm_year+1900, tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec); } //将ANSI C标准时间格式转换为中国常用的表示方式并显示出来 void main()//主函数,实现通信功能 {//加载套接字库 WORD wVersionRequested;//wVersionRequested参数用于指定准备加载的Winsock库的版本。 //高位字节指定所需要的Winsock库的副版本,而低位字节则是主版本。 WSADATA wsaData;//参数是指向WSADATA结构的指针,WSAStartup用其加载的库版本有关的信息填在这个结构中。 int err; wVersionRequested = MAKEWORD( 1, 1 ); // Winsock版本(1.1) err = WSAStartup( wVersionRequested, &wsaData ); //加载套接字库;进行套接字库版本的协商 if ( err != 0 ) { return; }//如果不能找到合适的Winsock,程序退出 if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {//判断版本是否是1.1.若不是则WSACleanup()终止调用 WSACleanup();//WSAData的wVersion成员中将包含你的应用程序应该使用的版本, //它是DLL所支持的最高版本与请求版本中较小的那个。 //对于每一个WSAStartup的成功调用(成功加载WinSock DLL后), //在最后都对应一个WSACleanUp调用,以便释放为该应用程序分配的资源。 return; //未找到合适版本的Winsock就返回 } printf("欢迎使用本软件! \n"); SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0);//该函数接收三个参数。 //第一个参数af指定地址族,对于TCP/IP协议的套接字,它只能是AF_INET(也可写成PF_INET)。 //第二个参数指定Socket类型,对于1.1版本的Socket,它只支持两种类型的套接字,SOCK_STREAM指定产生流式套接字,SOCK_DGRAM产生数据报套接字。 //第三个参数是与特定的地址家族相关的协议,如果指定为0,那么它就会根据地址格式和套接字类别,自动为你选择一个合适的协议。 SOCKADDR_IN addrSrv;//定义地址结构体变量addrSrv addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//(u_long)从主机字节序转换为网络字节序(INADDR_ANY为0) addrSrv.sin_family=AF_INET;//指定该地址家族,在这里必须设为AF_INET。 addrSrv.sin_port=htons(5500); //(u_short)从主机字节序转换为网络字节序 ,注意要用1024以上的端口号。 bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//绑定到本地地址和端口上。 SOCKADDR_IN addrClient;//定义地址结构体变量addrClient int len=sizeof(SOCKADDR);//定义整形变量len,存放SOCKADDR的长度 char sayBuf[]={"说:\n "}; char ncBuf[20];//定义昵称数组 char dataBuf[80];//定义说话内容数组 char sendBuf[100];//定义发送收数组 char recvBuf[100];//定义接收数组 char tempBuf[200];//定义临时数组 printf("请设置你的昵称:"); gets(ncBuf); strcat(ncBuf,sayBuf); printf("设置成功!\n"); int i; i=strlen(ncBuf)+1;//获取ncBuf的长度 while(1)//死循环,使程序一直处于监听状态 { char sendBuf[100]={" "};//初始化sendBuf strcat(sendBuf,ncBuf);//将ncBuf中的值放入sendBuf recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);//接受数据 if('z'==recvBuf[0]) { sendto(sockSrv,"通话已终止,请退出!",strlen("通话已终止,请退出!")+1,0,(SOCKADDR*)&addrClient,len); printf("通话已终止,请退出... \n"); break; }//判断收到的数据是否是q,若是则发送回一个q,并终止程序。 sprintf(tempBuf,"(%s) %s",inet_ntoa(addrClient.sin_addr),recvBuf);//inrt_ntoa将in_addr结构体类型的参数化成点分十进制的IP地址字符串 //将recvBuf、IP地址字符串和"%s 说: \n %s"格式化到tempBuf中。 time();//调用时间子函数,显示当前时间 printf(" %s\n",tempBuf);//显示tempBuf中的内容 printf("请输入聊天内容或按z键终止聊天: "); gets(dataBuf);//将从键盘输入的数据存入dataBuf time(); printf("%s",ncBuf); printf("%s \n",dataBuf); strcat(sendBuf,dataBuf);//将dataBuf中的值放入sendBuf sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClient,len);//将从键盘输入的数据存入sendBuf printf("请等待对方回话... \n"); } closesocket(sockSrv);//关闭套接字,释放为程序调用的资源 WSACleanup();//WSACleanup()终止调用 system("pause");//等待用户关闭界面 }
执行结果
相关文章推荐
- 网络编程学习笔记(二)UDP协议及聊天小程序的实现
- unix学习笔记<2> 多线程 udp聊天程序 简单实例
- 孙鑫VC学习笔记:第十六讲 用异步套接字编写聊天程序
- 【Java学习笔记】43:UDP一对一聊天
- 基于node的websocket学习笔记三:scoket.io基础与利用scoket.io构建聊天程序示例
- java基础学习笔记——网络UDP/聊天
- ACE学习笔记二(开发聊天程序)
- java学习笔记: 实现简易局域网聊天程序
- .Net学习笔记----2015-07-06(简易聊天程序)
- TCP聊天程序学习笔记
- winform简易聊天程序。。。。。学习笔记。。转载的
- 笔记:先前的UDP聊天程序改良版
- 孙鑫VC学习笔记:第十五讲 编写一个基于MFC对话框的聊天程序
- c#网络编程学习笔记02_Tcp编程(中)_简单的同步tcp聊天程序
- 孙鑫VC学习笔记:第十六讲 (三) 用异步套接字编写聊天程序
- [Nodejs]初探nodejs学习笔记- 如何使用nodejs搭建简单的UDP聊天功能
- 孙鑫VC学习笔记:第十五讲 编写一个基于MFC对话框的聊天程序
- java学习之利用UDP编写网络聊天程序
- Linux网络编程学习笔记-简单点对点聊天程序--6
- 孙鑫视频笔记——网络编程2(dos界面的UDP聊天程序)