目录
前言
本实验为计算机网络课程设计内容,基本上所有代码都是根据指导书给的附录写出来的。有些实验需要实现图形界面,但是出于期末考试压力,我所有实验均是在控制台输入输出的,没有花额外时间去学习qt了,有精力的同学可以自学一下qt实现简单的图形界面。同时,该博客内容为部分报告内容,仅为大家提供参考,请勿直接抄袭。另外,本次实验所用平台是dev c++5.11
该实验需要配置一下防护墙和网络适配器,如果在操作系统课程设计时安装了vm虚拟机的,最好把两台电脑的vm网络适配器同时开启或者同时关闭。这个实验实际上我只是书上的代码跑了一下,但是因为我当时想去评一下优,而别人想评优的基本上都有界面,我没有,所以我就把这个实验也算上完成的了,拿数量来凑数了,有能力的同学尽力按要求完成做个图形界面
1 实验题目
实验五 基于 IP 多播的网络会议程序
2 实验目的
参照附录 5 的局域网 IP 多播程序,设计一个图形界面的网络会议程序(实现文本多播方式即可)。
3 实验内容
3.1 步骤
(1)初始化 WinSock2.2;
(2)创建套接字并加入多播组;
(3)发送者发送多播数据,并监听控制台输入,直到接收到 "QUIT" 命令;
(4)接收者加入多播组,接收并打印多播数据,直到接收到 "QUIT" 为止。
3.2 关键代码
3.2.1 发送者
(1)调用WSAJoinLeaf函数加入多播组
if(( sockM = WSAJoinLeaf(sock,(SOCKADDR*)&remote,
sizeof(remote),NULL,NULL,NULL,NULL,JL_BOTH)) == INVALID_SOCKET) {
printf("WSAJoinLeaf() failed:%d\n",WSAGetLastError());
closesocket(sock);
WSACleanup();
return -1;
}
(2)在发送端主要利用sendto(sockM,(char*)sendbuf,strlen(sendbuf),0,(struct sockaddr*)&remote,sizeof(remote))将数据发送到多播组中
//发送多播数据,当用户在控制台输入"QUIT"时退出。
while(1) {
printf("SEND : ");
scanf("%s",sendbuf);
if( sendto(sockM,(char*)sendbuf,strlen(sendbuf),0,(struct sockaddr*)
&remote,sizeof(remote))==SOCKET_ERROR) {
printf("sendto failed with: %d\n",WSAGetLastError());
closesocket(sockM);
closesocket(sock);
WSACleanup();
return -1;
}
memset(sendBuf,'\0',128);
cout<<sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(remote.sin_addr));
cout<<"remote:"<<sendBuf<<"\n";
if(strcmp(sendbuf,"QUIT")==0) break;
Sleep(500);
}
3.2.2 接收者
(1)调用WSAJoinLeaf函数加入多播组
if(( sockM = WSAJoinLeaf(sock,(SOCKADDR*)&remote,sizeof(remote),
NULL,NULL,NULL,NULL,
JL_BOTH)) == INVALID_SOCKET) {
printf("WSAJoinLeaf() failed:%d\n",WSAGetLastError());
closesocket(sock);
WSACleanup();
return -1;
}
(2)在接收端主要利用recvfrom(sock,recvbuf,BUFSIZE,0,(struct sockaddr*)&from,&len)函数从多播组中接收数据。
//接收多播数据,当接收到的数据为"QUIT"时退出。
while(1) {
if(( ret = recvfrom(sock,recvbuf,BUFSIZE,0,
(struct sockaddr*)&from,&len)) == SOCKET_ERROR) {
printf("recvfrom failed with:%d\n",WSAGetLastError());
closesocket(sockM);
closesocket(sock);
WSACleanup();
return -1;
}
if( strcmp(recvbuf,"QUIT") == 0 ) break;
else {
recvbuf[ret] = '\0';
printf("RECV:' %s ' FROM <%s> \n",recvbuf,inet_ntoa(from.sin_addr));
}
}
4 实验结果与分析
(1)在本机启动发送端加入多播组。
图1.1 发送端加入多播组
(2)在另一台主机上启动接收端加入多播组。
图1.2 接收端加入多播组
(3)在本机的发送端发送“你好,欢迎加入多播组!”,可以在另一台主机的接收端上收到相应的消息。
图1.3 发送端发送信息
图1.4 接收端成功接收到信息
5 代码
5.1 发送者
//sender.cpp
#include<bits/stdc++.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#define MCASTADDR "233.0.0.1" //本例使用的多播组地址。
#define MCASTPORT 5150 //本地端口号。
#define BUFSIZE 1024 //发送数据缓冲大小。
#pragma comment(lib,"ws2_32")
using namespace std;
int main( int argc,char ** argv) {
WSADATA wsd;
struct sockaddr_in remote,local,from;
SOCKET sock,sockM;
TCHAR sendbuf[BUFSIZE];
int len = sizeof( struct sockaddr_in);
//初始化 WinSock2.2
if( WSAStartup( MAKEWORD(2,2),&wsd) != 0 ) {
printf("WSAStartup() failed\n");
return -1;
}
if((sock=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,
WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF|
WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) {
printf("socket failed with:%d\n",WSAGetLastError());
WSACleanup();
return -1;
}
//加入多播组
remote.sin_family = AF_INET;
remote.sin_port = htons(MCASTPORT);
remote.sin_addr.s_addr = inet_addr( MCASTADDR );
if(( sockM = WSAJoinLeaf(sock,(SOCKADDR*)&remote,
sizeof(remote),NULL,NULL,NULL,NULL,
JL_BOTH)) == INVALID_SOCKET) {
printf("WSAJoinLeaf() failed:%d\n",WSAGetLastError());
closesocket(sock);
WSACleanup();
return -1;
}
char sendBuf[128];
cout<<sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(local.sin_addr));
cout<<"local:"<<sendBuf<<"\n";
memset(sendBuf,'\0',128);
cout<<sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(remote.sin_addr));
cout<<"remote:"<<sendBuf<<"\n";
//发送多播数据,当用户在控制台输入"QUIT"时退出。
while(1) {
printf("SEND : ");
scanf("%s",sendbuf);
if( sendto(sockM,(char*)sendbuf,strlen(sendbuf),0,(struct sockaddr*)
&remote,sizeof(remote))==SOCKET_ERROR) {
printf("sendto failed with: %d\n",WSAGetLastError());
closesocket(sockM);
closesocket(sock);
WSACleanup();
return -1;
}
memset(sendBuf,'\0',128);
cout<<sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(remote.sin_addr));
cout<<"remote:"<<sendBuf<<"\n";
if(strcmp(sendbuf,"QUIT")==0) break;
Sleep(500);
}
closesocket(sockM);
closesocket(sock);
WSACleanup();
return 0;
}
5.2 接收者
//receiver.cpp
#include<bits/stdc++.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#define MCASTADDR "233.0.0.1" //本例使用的多播组地址。
#define MCASTPORT 5150 //绑定的本地端口号。
#define BUFSIZE 1024 //接收数据缓冲大小。
#pragma comment(lib,"ws2_32")
using namespace std;
int main( int argc,char ** argv) {
WSADATA wsd;
struct sockaddr_in local,remote,from;
SOCKET sock,sockM;
TCHAR recvbuf[BUFSIZE];
/*struct ip_mreq mcast; // Winsock1.0 */
int len = sizeof( struct sockaddr_in);
int ret;
//初始化 WinSock2.2
if( WSAStartup( MAKEWORD(2,2),&wsd) != 0 ) {
printf("WSAStartup() failed\n");
return -1;
}
/*
创建一个 SOCK_DGRAM 类型的 SOCKET
其中,WSA_FLAG_MULTIPOINT_C_LEAF 表示 IP 多播在控制面层上属于
"无根"类型;
WSA_FLAG_MULTIPOINT_D_LEAF 表示 IP 多播在数据面层上属于"无根",
有关控制面层和
数据面层有关概念请参阅 MSDN 说明。
*/
if((sock=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,
WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF|
WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) {
printf("socket failed with:%d\n",WSAGetLastError());
WSACleanup();
return -1;
}
//将 sock 绑定到本机某端口上。
local.sin_family = AF_INET;
local.sin_port = htons(MCASTPORT);
local.sin_addr.s_addr = INADDR_ANY;
if( bind(sock,(struct sockaddr*)&local,sizeof(local)) == SOCKET_ERROR ) {
printf( "bind failed with:%d \n",WSAGetLastError());
closesocket(sock);
WSACleanup();
return -1;
}
//加入多播组
remote.sin_family = AF_INET;
remote.sin_port = htons(MCASTPORT);
remote.sin_addr.s_addr = inet_addr( MCASTADDR );
/* Winsock1.0 */
/*
mcast.imr_multiaddr.s_addr = inet_addr(MCASTADDR);
mcast.imr_interface.s_addr = INADDR_ANY;
if( setsockopt(sockM,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char*)&mcast,
sizeof(mcast)) == SOCKET_ERROR)
{
printf("setsockopt(IP_ADD_MEMBERSHIP) failed:%d\n",WSAGetLastError());
closesocket(sockM);
WSACleanup();
return -1;
}
*/
/* Winsock2.0*/
if(( sockM = WSAJoinLeaf(sock,(SOCKADDR*)&remote,sizeof(remote),
NULL,NULL,NULL,NULL,
JL_BOTH)) == INVALID_SOCKET) {
printf("WSAJoinLeaf() failed:%d\n",WSAGetLastError());
closesocket(sock);
WSACleanup();
return -1;
}
char sendBuf[128];
cout<<sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(local.sin_addr));
cout<<"local:"<<sendBuf<<"\n";
memset(sendBuf,'\0',128);
cout<<sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(remote.sin_addr));
cout<<"remote:"<<sendBuf<<"\n";
//接收多播数据,当接收到的数据为"QUIT"时退出。
while(1) {
if(( ret = recvfrom(sock,recvbuf,BUFSIZE,0,
(struct sockaddr*)&from,&len)) == SOCKET_ERROR) {
printf("recvfrom failed with:%d\n",WSAGetLastError());
closesocket(sockM);
closesocket(sock);
WSACleanup();
return -1;
}
if( strcmp(recvbuf,"QUIT") == 0 ) break;
else {
recvbuf[ret] = '\0';
printf("RECV:' %s ' FROM <%s> \n",recvbuf,inet_ntoa(from.sin_addr));
}
}
closesocket(sockM);
closesocket(sock);
WSACleanup();
return 0;
}