客户-服务器网络编程实验报告
方健 20141203007
一、实验内容
设计一个简单的服务端的程序,监听10000端口,同时再设计一个客户端,向服务端发送数据,实现客户端与服务器的简单通信。
二、实现原理
建立Java的Socket连接,先明确服务器端和客户端,服务器端使用ServerSocket监听指定的端口,使用accept等待客户端请求,链接链接,开始会话、完成会话后,关闭链接。客户端使用Socket对服务器的端口发出链接请求,连接成功,开始会话,会话完成,Socket关闭。
本设计是基于Socket进行开发的,使用Java实现编程。
三、程序代码
服务器端:
package service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/*** 服务器 ***/
public class Services
{
private ServerSocket ss;
private Socket s;
private BufferedReader br;
private PrintWriter pw;
public Services()
{
try
{
ss = new ServerSocket(10000);
//服务端在10000监听
System.out.println("Server is starting...Server port " + 10000);//打印日志到控制台
while (true)
{
s = ss.accept();
//监听一直阻塞,相当于一直监听客户端的连接
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
//通过s对象就可以获取客户端的信息已经发来的数据
pw = new PrintWriter(s.getOutputStream(),true);
//通过s对象向客户端发送数据
String line = br.readLine();
//取出客户端发来的数据
System.out.println("客户端发来的数据:" + line);//打印日志到控制台
pw.println("向客户端回复数据" + line);
//向客户端回复数据
br.close();//关闭输入流
pw.close();//关闭输出流
}
}
catch (IOException ie)
{
ie.printStackTrace(); //打印出异常
}
}
/*** java程序入口函数:main***/
public static void main(String[] args) throws Exception
{
new Services();
}
}
客户端:
package client;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/** * 客户端***/
public class Client
{
private Socket s;
private BufferedReader br;
private PrintWriter pw;
private String line = "";
public Client()
{
try
{
s = new Socket("127.0.0.1",10000);
//客户端连接服务端,需要服务器的ip与端口
pw = new PrintWriter(s.getOutputStream(),true);
//创建输出流
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
//服务端返回数据流对象
pw.println("hello");//向服务端发送数据
line = br.readLine();//取出服务端返回来的数据
System.out.println("服务端返回的数据:" + line);
//打印日志到控制台
br.close();//关闭输入流
pw.close();//关闭输出流
}
catch(IOException ie)
{
ie.printStackTrace();//打印出异常
}
}
/***java程序入口函数:main***/
public static void main(String[] args) throws Exception
{
new Client();
}
}
四、仿真结果
服务器端
客户端
第二篇:东南大学Windows Socket编程实验报告
Windows Socket编程实验报告
一、实验目的
1. 加深对流量控制、差错处理方法的理解;
2. 熟悉TCP/IP编程, 将书本知识运用到实验中;
3. 开拓学生的创新意识,培养学生的独立动手操作的能力。
二、 实验内容
利用已有的模拟信道程序,编制发送、接收程序的部分模块,使系统具有可靠的收发功能。
三、 设计原理
1. 整体思路
实验中,我们采用了无连接的Socket编程模式,相对于面向连接的Socket编程模式较为简单。无连接的Socket实现收发功能的过程如下:
本实验中,我们采用了停等协议以及单工方式。同时为了保证系统收发功能的稳定进行,我们需要考虑各种故障因素。这里,我们考虑了数据丢失、数据出错以及数据发送延时等故障因素,通过奇偶校验方法以及应答帧的正确与否来判断是否出现以上故障,接着重新发送当前数据,很好地解决以上问题,保证了收发功能的稳定性。
1 / 8
2. 各模块设计
⑴ 发送端(Sender)
①发送与接收
int SendMsg(char *msg, int len)//由于是无连接的Socket,因此这里调用函数sendto发送数据
{
int n;
n=sendto(sender_sockfd, msg, len, 0, (struct sockaddr *) &channelsender_addr, sizeof(sockaddr));
return n;
}
int ReceiverMsg(char *msg)//调用函数recvfrom接收数据
{
int n;
int len=sizeof(sockaddr);
n=recvfrom(sender_sockfd,msg,MAX_FRAME_LEN, 0, (struct sockaddr *) &channelsender_addr,&len);
return n;
}
②编码
//产生发送的信息:头、序号、数据、校验位、长度、尾
void Encode(char msg, int no, int len, char *newmsg)
{
newmsg[0]=HEADER;//已在stdAfx.h中定义为0x3c
newmsg[1]=no;//序号
newmsg[2]=msg;//数据
newmsg[4]=len;//长度
newmsg[5]=HEADER;//尾
//计算msg‘1’的个数,累加到count中
int a,b,c;
int count=0;
c=msg;
while(c!=0){//相当于十进制数化为二进制数的过程
a=c%2;
b=c/2;
if(a==1) count++;
c=b;
}
//产生偶校验码
if(count%2==1) newmsg[3]=0x01;//‘1’的个数为奇数,偶校验码为1 else newmsg[3]=0x00; //‘1’的个数为偶数,偶校验码为0
}
2 / 8
③检查应答帧
BOOL check_data(char *msg)
{
if(msg[0]==HEADER&&msg[1]==ACK&&msg[2]==HEADER) return true;//ACK已定义为0x06
else return false;
}
④接收信道信息以及差错处理
LONG CMainFrame::MyChannelSend(UINT wParam,UINT lParam)
{
char msg[MAX_FRAME_LEN];/* 缓冲区*/
char str_show[100];
//接收信息,写入msg
ReceiverMsg(msg);
//在屏幕上打印出来
wsprintf(str_show, "%02XH--%02XH--%02XH ",msg[0], msg[1], msg[2]); Show_msg_ListBox(str_show);
//差错处理
if(check_data(msg)){//应答帧正确,继续发送剩余帧
iSendno++;//已正确发送的帧数加1,iSendno在前面已经初始化为0 if(iSendno==msglen) bSending=0;//发送完毕
else{//发送下一帧
bSending=1;
Encode(str_send[iSendno],iSendno,msglen,now_send);
SendMsg(now_send,DATA_FRAME_LEN);
}
}
else{//应答帧错误,重新发送当前帧
Encode(str_send[iSendno], iSendno, msglen, now_send);
SendMsg(now_send, DATA_FRAME_LEN);
}
return 1;
}
⑵ 接收端(Receiver)
①发送与接收
int ReceiverMsg(char *msg) //调用函数recvfrom接收数据
{
int n;
int len=sizeof(sockaddr);
n=recvfrom(receiver_sockfd,msg,MAX_FRAME_LEN, 0, (struct sockaddr *) &channelsender_addr,&len);
3 / 8
return n;
}
int SendMsg(char *msg,int len)// 调用函数sendto发送数据
{
int n;
n=sendto(receiver_sockfd,msg,len,0,(struct sockaddr *) &channelsender_addr,sizeof(sockaddr));
return n;
}
②检查校验位
BOOL check_data(char *msg)
{
int a,b,c,d;
int count=0;
c=msg[2];//将信息帧中的数据提取出
while(c!=0){
a=c%2;
b=c/2;
if(a==1) count++;
c=b;
}
d=count%2;
//检查msg中数据的‘1’的个数是奇数还是偶数,并与校验位比较,若数据发送正确,则count对2取余数的结果应该与校验码相同
if(d==msg[3]) return true;
else return false;
}
③接收信道信息以及构造应答帧
LONG CMainFrame::MyChannelSend(UINT wParam,UINT lParam)
{
char str_send[MAX_FRAME_LEN];
char check_ans[ANS_FRAME_LEN];
char show_str[100];
//接收信息,写入str_send
ReceiverMsg(str_send);
//在屏幕上输出接收的信息
wsprintf(show_str,"Sender:%02XH--%02XH--%02XH--%02XH--%02XH--%02XH %c ",str_send[0],str_send[1],str_send[2],str_send[3],str_send[4],str_send[5],str_send[2]);
4 / 8
Show_msg_ListBox(show_str);
//应答处理
if(check_data(str_send)){//接收正常
iRevno++;//已接收的正确帧数加1,iRevno在前面已经初始化为0 if(iRevno==str_send[4]){//接收所有的数据帧,显示提示信息 wsprintf(show_str,"Receiver已经正确接收所有数据!");
Show_msg_ListBox(show_str);
}
if(iRevno<=str_send[4])//没有接收完全,发送应答帧ACK,继续接收 {
check_ans[0]=HEADER;
check_ans[1]=ACK;//ACK定义为0x06
check_ans[2]=HEADER;
SendMsg(check_ans,ANS_FRAME_LEN);
}
}
else{//接收出错,发送NAK应答帧,提示Sender重新发送
wsprintf(show_str,"接收到错误数据!");//提示接收到错误数据 Show_msg_ListBox(show_str);
check_ans[0]=HEADER;
check_ans[1]=NAK;//NAK定义为0x15
check_ans[2]=HEADER;
SendMsg(check_ans,ANS_FRAME_LEN);
}
return 1;
}
四、 实验结果极其分析
假定要发送的数据为”abcde”,各实验结果截图如下:
1.正常发送
5 / 8
这时,当在Sender中输入abcde,并点击发送,接收端能够正常接收到所发信息abcde。
2.数据丢失
这种情况下,当发送信息时,由于数据c的丢失,接收端起初只收到a跟b,经过一段时间后接收到之后的信息。
6 / 8
3.数据延时
这种情况下,当发送数据时,channel中发送数据c时提示发送超时,并且立即重发,所以接收端能够接收到完整的信息,最后接收到延时发送到的c。
4.数据出错
这种情况下,当发送数据时,发送到c时,错发成了b,接收端提示接收到错误数据,并且将错误的应答帧3CH—15H—3CH发送到发送端,发送端重新发送数据,最后接收端接收到正确完整的信息。
5.随机情况
点击channel中的随机后,所发生的情况是前面其中情况的综合,这里不再重复。
通过以上5种不同情况的测试,
该系统已经基本能够完成稳定收发数据的功
7 / 8
能,很好地达到了实验之前所期望的效果。可能中间还有些许bug没有被发现,但是总体需要实现的功能已经达到,还是可以接受的。
通过此次实验,我加深了对差错处理方法的理解,并且大致熟悉了基于UDP的Windows Socket设计方法,虽然只是补充一个大系统之中某些具体的函数。这次实验,我终于能够将书本知识运用到了实际情况之中,这种能够将理论运用到实际中的感觉很好。另外,在这次实验中,我为了了解Windows Socket的部分函数,查阅了很多资料,包括直接通过MSDN对相关函数进行了解,这让我感受到我们在课堂上学到的知识还是远远不够的,更多的还是要靠自己平时的多多积累与资料文献的大量查阅。
8 / 8