孙鑫视频笔记——网络编程2(dos界面的UDP聊天程序)

服务器端:

新建一个空的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链接库。

你可能感兴趣的:(孙鑫视频笔记——网络编程2(dos界面的UDP聊天程序))