资源描述
实验三Socket通信实验报告
(1)实验目的和要求
1. 掌握VB、VC++、VS或JAVA等集成开发环境编写网络程序的方法;
2. 掌握客户/服务器(C/S)应用的工作方式;
3. 学习网络中进程之间通信的原理和实现方法;
4. 理解单播、组播和广播的原理并比较其不同之处;
5. 要求本机既是客户端又是服务器端;
(2)实验内容
所编写的程序应具有如下功能:
1. 具有点对点通信功能,任意客户端之间能够发送消息;
2. 具有群组通信功能,客户端能够向组内成员同时发送消息,其他组成员不能收到;
3. 具有广播功能,客户端能够向所有其他成员广播消息;
(3)编程语言和环境
1. 编程语言C/C++/C#/Java等均可;
2. 编程环境Windows(MS Visual系列,VC/VB/VS.Net;)和Linux(编辑器vi+编译器GCC)均可;
(4)实验主要功能实现说明
以下为针对三个实验内容实现方法的简要说明,示例所用语言为C。
服务器端
客户端
socket
bind
listen
socket
connect
accept
recv
send
recv
close
send
close
阻塞自己等待客户连接
建立连接
请求数据
应答数据
基于C的面向连接的socket编程模型
1. 点对点通信功能
实现网络点对点通讯程序的关键步骤就是实现信息在网络中的发送和接收。数据接收使用的是Socket,数据发送使用的是NetworkStream。
1.1利用Socket来接收信息
TcpListener tlListen1 = new TcpListener ( 8889 ) ;
//侦听端口号
tlListen1.Start ( ) ;
Socket skSocket = tlListen1.AcceptSocket ( ) ;
//接受远程计算机的连接请求,并获得用以接收数据的Socket实例
EndPoint tempRemoteEP = skSocket.RemoteEndPoint ;
//获得远程计算机对应的网络远程终结点
while ( true )
{
Byte [] byStream = new Byte[80] ;
//定义从远程计算机接收到数据存放的数据缓冲区
int i = skSocket.ReceiveFrom ( byStream , ref tempRemoteEP ) ;
//接收数据,并存放到定义的缓冲区中
string sMessage = System.Text.Encoding.UTF8.GetString ( byStream ) ;
//以指定的编码,从缓冲区中解析出内容
MessageBox.Show ( sMessage ) ;
//显示传送来的数据
}
1.2利用NetworkStream来传送信息
TcpClient tcpc = new TcpClient ( "10.138.198.213" , 8888 ) ;
//对IP地址为“10.138.198.213”的计算机的8888端口提出连接申请
NetworkStream tcpStream = tcpc.GetStream ( ) ;
//如果连接申请建立,则获得用以传送数据的数据流
string sMsg = "您好,见到您很高兴" ;
StreamWriter reqStreamW = new StreamWriter ( tcpStream ) ;
//以特定的编码往向数据流中写入数据 ,默认为UTF8编码
reqStreamW.Write ( sMsg ) ;
//将字符串写入数据流中
reqStreamW.Flush ( ) ;
//清理当前编写器的所有缓冲区,并使所有缓冲数据写入基础流
2. 群组通信功能
组播编程需要UDP,有两个类支持组播网络编程Socket和UdpClient.一台计算机要加入某一个组,然后接收发往这个组的信息。Socket类要调用SetSocketOption函数加入和离开某一个组。UdpClient类有直接的加入和离开某个组的成员函数可以调用。而向某个组发信息,则没有什么特殊的,只需把发送数据的目的地址设为组播地址就可以了。
发送端:
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("224.0.0.1"), 3000);
EndPoint ep = (EndPoint)iep;
byte[] b = Encoding.ASCII.GetBytes("just a test!");
s.SendTo(b, ep);
s.Close();
接收端:
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 3000);
EndPoint ep=(EndPoint)iep;
s.Bind(iep);
s.SetSocketOption(SocketOptionLevel.IP,SocketOptionName.AddMembership,new MulticastOption(IPAddress.Parse("224.0.0.1")));
byte[]b=new byte[1024];
s.ReceiveFrom(b,ref ep);
string test;
test = System.Text.Encoding.ASCII.GetString(b);
Console.WriteLine(test);
s.Close();
Console.ReadKey();
3. 广播功能
此功能和组播功能实现类似,只要在发送端获得子网中IP广播地址发送休息即可。
// 广播模式(自动获得子网中的IP广播地址)
broadcastIpEndPoint = new IPEndPoint(IPAddress.Broadcast, 3000);
服务器程序代码
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include<conio.h>
#include<windows.h>
#include<winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#define MYPORT 3490 /*定义用户连接端口*/
#define BACKLOG 10 /*多少等待连接控制*/
#define SERVER_IP_ADDR "192.168.1.102" /*服务器的IP地址*/
int _tmain(int argc, _TCHAR* argv[])
{
SOCKET sock, msgsock;
int length = 0;
struct sockaddr_in server;
struct sockaddr tcpaddr;
char buf[1024] = "";
int rval= 0, len= 0, err = 0;
WORD wVersionRequested;
WSADATA wsaData;
/*指定socket版本,否则创建socket失败,即使创建socket返回值不为-1,但是bind时会失败*/
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
return -1;
/* 建立套接字*/
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror("opening stream socket");
exit(1);
}
/* 使用任意端口命名套接字*/
server.sin_family = AF_INET;
server.sin_port = htons(MYPORT);
server.sin_addr.s_addr = inet_addr(SERVER_IP_ADDR);
memset(server.sin_zero, 0, sizeof(server.sin_zero));
//将服务器地址与socket绑定在一起
rval = bind(sock, (struct sockaddr *)&server, sizeof(server));
if (rval < 0)
{
perror("binding stream socket");
exit(1);
}
// 找出指定的端口号并打印出来
length = sizeof(server);
if (getsockname(sock, (struct sockaddr *)&server, &length) < 0)
{
perror("getting socket name");
exit(1);
}
printf("socket port #%d\n", ntohs(server.sin_port));
// 开始接收连接,最大请求数为
listen(sock, 5);
len = sizeof(struct sockaddr);
do
{ msgsock = accept(sock, (struct sockaddr *)&tcpaddr, (int *)&len);
if (msgsock == -1)
perror("accept");
else
{ memset(buf, 0, sizeof(buf));
if ( (rval = recv(msgsock, buf, sizeof(buf),0) < 0))
perror("reading stream message");
if (rval == 0)
printf("-->%s\n", buf);
}
closesocket(msgsock);
} while (TRUE);
/* 因为这个程序已经有了一个无限循环,所以套接字"sock"从来不显式关闭。然而,当进程被杀死或正常终止时,所有套接字都将自动地被关闭。*/
closesocket(msgsock);
return 0;
}
客户端程序代码
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <conio.h>
#include <windows.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#define PORT 3490 /* 客户机连接远程主机的端口*/
#define MAXDATASIZE 100 /* 每次可以接收的最大字节*/
int _tmain(int argc, _TCHAR* argv[])
{
WORD wVersionRequested;
WSADATA wsaData;
int err = 0 ,rval = 0 ;
SOCKET fd;
struct sockaddr_in servaddr;
struct hostent* hp;
char buf[1024] = "";
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
return -1;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Can not create socket!");
exit(2);
}
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
hp = gethostbyname("YUQIAN-SOFT"); //根据服务器的网络计算//机名称得到其IP地址等信息
memcpy((char*)&servaddr.sin_addr, (char*)hp->h_addr,hp->h_length);
memset(servaddr.sin_zero, 0, sizeof(servaddr.sin_zero));
//和服务器创建连接
rval = connect(fd, (sockaddr*)&servaddr, sizeof(servaddr));
if (rval < 0)
{//创建连接失败
printf("Can not create connect!");
exit(3);
}
else
{ memset(buf, 0, 1024);
printf("Please input a line to server:");
scanf("%s",&buf);
//向服务器发送信息
rval = send(fd, buf, strlen(buf) + 1,0);
if(rval < 0)
printf("Write error!");
}
closesocket(fd);
exit(5);
}
展开阅读全文