实验四:网络与分布式系统
4.1
1.题目要求
第一点:题目要求我们实现两个网络系统调用(connect()和accept(),记录在syscall.h中),它们在连接端点之间提供可靠的、面向连接的字节流通信。连接端点是一个链接地址和端口号的组合。
第二点:题目还要求我们将网络接口与文件接口相互匹配,并且重写read()和wirte()两个读写系统调用。建立连接后,每个系统调用都会返回一个表示连接的新文件描述符(在UNIX术语中,此文件描述符称为“socket”)。然后,应用程序可以通过调用套接字上的write()向网络发送消息,并可以通过调用套接字上的read()从网络读取消息。连接的一端在套接字文件描述符上执行close()操作时关闭。连接关闭后,套接字上的任何read()或write()调用(在连接的任一端!)应返回-1,表示出现错误。
第三点:
2.问题解决思路:
实现收发机制:首先我们研究nachos中基本的网络运行机理,发现在networklink类中,会开启一个线程不断接受到来的数据包,每当一个数据包到来,都会接受并调用一个接受的触发函数,这个接受的触发函数并没有实现。我们首先定义一个类PostOffice,在这个类中定义收发机制,与网卡的模拟类networklink打交道。我们令所有的信息接收之后都存到PostOffice内的一个与对应的端口号相匹配的列表中以供获取数据时使用。
实现系统调用:我们在NetProcess中实现相应的系统调用,我们令网络连接Socket继承自文件OpenFile类,以实现网络接口与文件接口相互匹配。Read(()和write()系统调用则重新改写,如果是网络连接则用PostOffice来收发,如果不是网络连接则用以前的文件系统的那一套方法来处理。
3.变量说明:
Socket类:表示一个连接的结构体,继承自OpenFile类
UdpPacket类:表示一个数据包的结构体,内部定义的各种特殊符号SYN,FIN等于TCP协议中定义的含义一致
PostOffice类:一个收发的管理类,与网卡类networklink打交道。内部有一个容器waitingDataMessages,里面存有很多个队列,每个端口号都对应着一个队列。该类内部的send()方法用于发送数据,receive()方法用于接收数据(ps:当waitingDataMessages内对应的队列为空时,receive()方法将返回一个null)
4.源代码:
Socket类代码:
package nachos.network; ///NEW/
import nachos.machine.Lib;
import nachos.machine.MalformedPacketException;
import nachos.machine.OpenFile;
import nachos.userprog.UserProcess;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
//为网络连接新建的 文件类 每一个文件都会和 接收方和发送方的地址相关联
public class Socket extends OpenFile {
public int sourceLink;
public int destinationLink;
public int sourcePort;
public int destinationPort;
public int currentSeqNum;
public int SeqNum;
public int bytesSent;
public int bytesRead;
public Deque<byte[]> readBuffer;
public int getSourcePort()
{
return sourcePort;
}
public Socket(int destinationLink, int destinationPort, int sourceLink, int sourcePort) {
super(null, "Socket");
this.sourceLink = sourceLink;
this.sourcePort = sourcePort;
this.destinationLink = destinationLink;
this.destinationPort = destinationPort;
this.currentSeqNum = 0;
this.SeqNum = 0;
readBuffer = new LinkedList<byte[]>();
bytesSent = 0;
bytesRead = 0;
}
public int read(byte[] buffer, int offset, int size)
{
//获取到指定端口上的数据 在这里阻塞
UdpPacket packet = NetKernel.postOffice.receive(sourcePort);
//如果包为空 则返回-1
if(packet == null)
{
return -1;
}
//如果不为空 则增加当前接受到的消息的序列号
currentSeqNum++;
//防止读取到的内容不越界
int bytesRead = Math.min(size, packet.payload.length);
//将内容复制到目标数组
System.arraycopy(packet.payload, 0, buffer, offset, bytesRead);
String r=new String(packet.payload);
System.out.print(new String(destinationLink+"说了"+r));
return bytesRead;
}
public int write(byte[] buffer, int offset, int size)
{
int amt = Math.min(offset+size, buffer.length);
byte[] elements = Arrays.copyOfRange(buffer, offset, amt);
// System.out.println("element内容为"+ Lib.bytesToString(elements,0,elements.length));
try {
//写入新的包
UdpPacket packet = new UdpPacket(destinationLink, destinationPort, sourceLink, sourcePort, UdpPacket.DATA ,SeqNum+1, elements);
//然后发送
NetKernel.postOffice.send(packet);
System.out.println("写东西给"+ destinationLink);
//当前发送的序列号+1
SeqNum++;
return amt;
}
catch(MalformedPacketException e)
{
return -1;
}
}
}
UdpPacket类代码:
package nachos.network; ///NEW/
import nachos.machine.MalformedPacketException;
import nachos.machine.Packet;
public class UdpPacket {
public Packet packet;
public int destPort;//目标端口号 附加头的第一位
public int srcPort;//源端口号 附加头的第二位
public int status;//状态位
public int seqNum;//序列号 后四位
public byte[] payload;//此包的所有内容
public int headerLength = 4;
public int maxContentsLength = Packet.maxContentsLength - headerLength;//最大内容长度
public static int DATA = (0);
public static int SYN = (1);
public static int ACK = (2);
public static int STP = (4);
public static int FIN = (8);
public static int FINACK = (5);
public static int SYNACK = (3);
public UdpPacket(){}
public UdpPacket(int dstLink, int destPort, int srcLink, int srcPort, int status, int seqNum, byte[] payload)throws MalformedPacketException {
if (destPort < 0 || destPort >= maxPortLimit ||
srcPort < 0 || srcPort >= maxPortLimit ||
payload.length > maxContentsLength) {
throw new MalformedPacketException();
}
this.destPort = (byte)destPort;
this.srcPort = (byte)srcPort;
this.status = status;
this.seqNum = seqNum;
this.payload = payload;
byte[] contents = new byte[headerLength + payload.length];
contents[0] = (byte)destPort;
contents[1] = (byte)srcPort;
contents[2] = (byte)status;
contents[3] = (byte)seqNum;
//其他八位存放内容
System.arraycopy(payload, 0, contents, headerLength, payload.length);
//然后构造成32位的包
packet = new Packet(dstLink, srcLink, contents);
}
public UdpPacket(Packet packet) throws MalformedPacketException {
this.packet = packet;
if(packet.contents.length < headerLength ||
packet.contents[0] < 0 || packet.contents[0] >= maxPortLimit ||
packet.contents[1] < 0 || packet.contents[1] >= maxPortLimit)
throw new MalformedPacketException();
destPort = packet.contents[0];
srcPort = packet.contents[1];
status = packet.contents[2];
seqNum = packet.contents[3];
payload = new byte[packet.contents.length - headerLength];
System.arraycopy(packet.contents, headerLength, payload, 0, payload.length);
}
@Override
public String toString(){
return "UdpPack || from: "+ packet.srcLink +":"+srcPort+" to: "+ packet.dstLink+":"+destPort+" "+payload.length+" bytes";
}
public static final int maxPortLimit = 128;
}
PostOffice类代码:
package nachos.network;
import nachos.machine.Lib;
import nachos.machine.Machine;
import nachos.machine.MalformedPacketException;
import nachos.machine.Packet;
import nachos.threads.KThread;
import nachos.threads.Lock;
import nachos.threads.Semaphore;
import nachos.threads.SynchList;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.TreeSet;
/**
* A collection of message queues, one for each local port. A
* <tt>PostOffice</tt> interacts directly with the network hardware. Because
* of the network hardware, we are guaranteed that messages will never be
* corrupted, but they might get lost.
* * 消息队列的集合,每个本地端口一个。
* * <tt>PostOffice</tt> 与网络硬件直接交互。由于网络硬件的原因,我们保证消息不会被破坏,但它们可能会丢失。
* <p>
* The post office uses a "postal worker" thread to wait for messages to arrive
* from the network and to place them in the appropriate queues. This cannot
* be done in the receive interrupt handler because each queue (implemented
* with a <tt>SynchList</tt>) is protected by a lock.
* <p>
* * post office使用“postal worker”线程等待来自网络的messages到达,并将它们放置在适当的队列中。
* * 无法在接收中断处理程序中执行此操作,
* * 因为每个队列(使用<tt>synchlist</tt>实现)都受锁保护。
*/
public class PostOffice {
/**
* Allocate a new post office, using an array of <tt>SynchList</tt>s.
* Register the interrupt handlers with the network hardware and start the
* "postal worker" thread.
* <p>
* * 使用<tt>synchlist</tt>s数组分配新 post office,。
* * <p>
* * 向网络硬件注册中断处理程序并启动“postal worker”线程。
*/
public PostOffice() {
messageReceived = new Semaphore(0);
messageSent = new Semaphore(0);
sendLock = new Lock();
portLock = new Lock();
queues = new SynchList[MailMessage.portLimit];
unackMessages = new HashSet<UdpPacket>();//未确认消息
for (int i = 0; i < queues.length; i++)
queues[i] = new SynchList();
waitingDataMessages = new Deque[MailMessage.portLimit];
availPorts = new TreeSet<Integer>();
for (int i = 0; i < MailMessage.portLimit; i++) {
availPorts.add(i);
waitingDataMessages[i] = new LinkedList<UdpPacket>();
}
Runnable receiveHandler = new Runnable() {
public void run() {
receiveInterrupt();
}
};
Runnable sendHandler = new Runnable() {
public void run() {
sendInterrupt();
}
};
Machine.networkLink().setInterruptHandlers(receiveHandler,
sendHandler);
KThread t = new KThread(new Runnable() {
public void run() {
try {
postalDelivery();
} catch (MalformedPacketException e) {
e.printStackTrace();
}
}
});
// KThread resend = new KThread(new Runnable() {
// public void run() { resendAll(); }
// });
// resend.fork();
t.fork();
}
/**
* Retrieve a message on the specified port, waiting if necessary.
* 不断的接收信息,无条件的接受
*/
//public MailMessage receive(int port) {
public UdpPacket receive(int port) {
// Lib.assertTrue(port >= 0 && port < queues.length);
Lib.debug(dbgNet, "waiting for mail on port " + port);
//MailMessage mail = (MailMessage) queues[port].removeFirst();//Dont want to return mail.Return our thing
// UdpPacket mail = (UdpPacket) queues[port].removeFirst();
// if (Lib.test(dbgNet))
// System.out.println("got mail on port " + port + ": " + mail);
return (waitingDataMessages[port].isEmpty()) ? null : waitingDataMessages[port].removeFirst();
// return mail;
}
public UdpPacket getFirst(int port) {
// Lib.assertTrue(port >= 0 && port < queues.length);
Lib.debug(dbgNet, "waiting for mail on port " + port);
//MailMessage mail = (MailMessage) queues[port].removeFirst();//Dont want to return mail.Return our thing
// UdpPacket mail = (UdpPacket) queues[port].removeFirst();
// if (Lib.test(dbgNet))
// System.out.println("got mail on port " + port + ": " + mail);
return (waitingDataMessages[port].isEmpty()) ? null : waitingDataMessages[port].element();
// return mail;
}
/**
* Wait for incoming messages, and then put them in the correct mailbox.
* 一旦有信息到来,就会调用当前函数,然后将它们放入正确的邮箱。
*/
private void postalDelivery() throws MalformedPacketException {
while (true) {
messageReceived.P();
Packet p = Machine.networkLink().receive();
UdpPacket mail;
try {
mail = new UdpPacket(p);
} catch (MalformedPacketException e) {
continue;
}
// //如果是回复包
// if(mail.status == UdpPacket.ACK )
// {
// for(UdpPacket m : unackMessages)
// {
// if(m.destPort == mail.srcPort && m.packet.dstLink == mail.packet.srcLink && m.seqNum == mail.seqNum)
// {
// unackMessages.remove(m);
// break;
// }
// }
// }
如果是数据包 则添加到对应端口的队列
// if(mail.status == UdpPacket.DATA )
// {
// waitingDataMessages[mail.destPort].add(mail);
// //然后构造返回包
// UdpPacket ackmail = new UdpPacket(mail.packet.srcLink, mail.destPort,mail.packet.dstLink, mail.srcPort, mail.status,mail.seqNum,mail.payload);
// send(ackmail);
// }
//
if (Lib.test(dbgNet))
System.out.println("delivering mail..."
+ ": " + mail);
// System.out.println("收到数据:" + Lib.bytesToString(mail.payload, 0, mail.payload.length) + "从" + mail.packet.srcLink+" 的 " +mail.srcPort);
waitingDataMessages[mail.destPort].add(mail);
// 自动将邮件添加到邮箱并唤醒等待线程
// queues[mail.destPort].add(mail);
//queues[mail.destPort].free = false;
setPortUsed(mail.destPort);
}
}
/**
* Called when a packet has arrived and can be dequeued from the network
* link.
* 当数据包到达并可以从网络链路中退出队列时调用,该方法会设置到machine的network中
*/
private void receiveInterrupt() {
messageReceived.V();
}
/**
* Send a message to a mailbox on a remote machine.
*/
public void send(UdpPacket mail) {
if (Lib.test(dbgNet))
System.out.println("sending mail: " + mail);
sendLock.acquire();
// System.out.println("send发出数据:" + Lib.bytesToString(mail.payload,0,mail.payload.length) + "到" + mail.packet.dstLink+" 的 " +mail.destPort);
Machine.networkLink().send(mail.packet);
unackMessages.add(mail);
messageSent.P();
sendLock.release();
}
/**
* Called when a packet has been sent and another can be queued to the
* network link. Note that this is called even if the previous packet was
* dropped.
*/
private void sendInterrupt() {
messageSent.V();
}
//标记已经使用的端口
public int getUnusedPort() {
portLock.acquire();
int i = 0;
for (SynchList obj : queues) {
if (obj.free) {
obj.free = false;
portLock.release();
return i;
}
i++;
}
portLock.release();
return -1;
}
public void setPortUsed(int i) {
portLock.acquire();
if (queues[i].free)
queues[i].free = false;
portLock.release();
}
private void resendAll() {
while(true) {
Lock lock = new Lock();
lock.acquire();
for(UdpPacket m : unackMessages)
send(m);
lock.release();
NetKernel.alarm.waitUntil(30000);
}
}
private Lock portLock;
private SynchList[] queues;
private Semaphore messageReceived; // 当消息出队列时唤醒
private Semaphore messageSent; // 到消息入队列时唤醒
private Lock sendLock;
private static final char dbgNet = 'n';
public HashSet<UdpPacket> unackMessages;//未确认消息
public TreeSet<Integer> availPorts;//可分配端口
public Deque<UdpPacket>[] waitingDataMessages;
}
系统调用的实现代码:
private int handleConnect(int host, int port) {
int srcLink = Machine.networkLink().getLinkAddress();
// if(NetKernel.postOffice.availPorts.isEmpty())
// {
// return -1;
// }
// int srcPort = NetKernel.postOffice.availPorts.first();
int srcPort = NetKernel.postOffice.getUnusedPort();
// NetKernel.postOffice.availPorts.remove(NetKernel.postOffice.availPorts.first());
int res;
Socket socket = null;
//检查是否存在相同的 文件描述符
if ((res = checkExistingConnection(host, srcLink, port, port)) == -1) {
//如果不存在则新建
socket = new Socket(host, port, srcLink, srcPort);
int i = findEmpty();
openfile[i] = new OpenFile(null,"connect");
socketList[i] = socket;
//openfile[i].setFileName("connect");
res = i;
}
//如果存在寻找之前旧的文件描述符
if (socket == null) socket = (Socket) socketList[res];
srcPort = socket.sourcePort;
try {
//SYN表示此数据包是启动后的第一个数据包
UdpPacket packet = new UdpPacket(host, port, srcLink, srcPort, UdpPacket.SYN, 0, new byte[0]);
//发送packet
NetKernel.postOffice.send(packet);
System.out.println("SYN包已发送,挂起等待回复");
/**
* 当用户进程调用connect()系统调用时,活动端点发送同步数据包(syn)。
* 这将导致创建挂起的连接。在被动端点的用户进程调用accept()系统调用之前,
* 此挂起连接的状态必须存储在接收器上。调用accept()时,
* 被动端向主动端发送一个syn确认数据包(syn/ack),并建立连接。
*/
UdpPacket SynAckPack = NetKernel.postOffice.receive(srcPort);
while (SynAckPack == null)
{
NetKernel.alarm.waitUntil(RETRANSMIT_INTERVAL);
SynAckPack = NetKernel.postOffice.receive(srcPort);
if(SynAckPack != null)
{
break;
}
}
System.out.println("SYN包以收到回复");
//收到确认的数据包
if (SynAckPack.status == UdpPacket.SYNACK && Machine.networkLink().getLinkAddress() == SynAckPack.packet.dstLink) {
System.out.print("SYNACK已经收到: ");
System.out.println(SynAckPack);
//发回ack。
UdpPacket ackPack = new UdpPacket(host, port, srcLink, srcPort, UdpPacket.ACK, SynAckPack.seqNum + 1, new byte[0]);
NetKernel.postOffice.send(ackPack);
//确认发送此时可以发送数据。 连接已经建立
}
} catch (MalformedPacketException e) {
return -1;
}
return res;
}
/**
*
* @param port
* @return
*/
private int handleAccept(int port) {
Lib.assertTrue(port >= 0 && port < Packet.linkAddressLimit);
UdpPacket mail = NetKernel.postOffice.receive(port);
while (mail == null)
{
NetKernel.alarm.waitUntil(RETRANSMIT_INTERVAL);
mail = NetKernel.postOffice.receive(port);
if(mail != null)
{
break;
}
}
if (mail == null) {
return -1;
}
//添加端口信息 已经包的序列号
int srcPort = mail.destPort;
int sourceLink = mail.packet.dstLink;
int destinationLink = mail.packet.srcLink;
int destinationPort = mail.srcPort;
int seq = mail.seqNum + 1;
int res;
//确认文件描述符还不存在
if ((res = checkExistingConnection(destinationLink, sourceLink, srcPort, destinationPort)) == -1) {
Socket conn = new Socket(destinationLink, destinationPort, sourceLink, srcPort);
int i = -1;
i = findEmpty();
openfile[i] = new OpenFile(null,"handleAccept");
socketList[i] = conn;
//FileDescriptors[i].setFileName("handleAccept");
res = i;
}
try {
//确定他是请求连接的数据包 同时确保它被发送给正确的人
UdpPacket ackPacket = null;
// System.out.println(mail.toString());
if (mail.status == UdpPacket.SYN && Machine.networkLink().getLinkAddress() == mail.packet.dstLink) {
ackPacket = new UdpPacket(destinationLink, destinationPort, sourceLink, srcPort, UdpPacket.SYNACK, seq, new byte[0]);
}
//表示收到的不是连接信息
if (ackPacket == null) {
NetKernel.postOffice.waitingDataMessages[port].add(mail);
return -1;
}
//回复确认收到
NetKernel.postOffice.send(ackPacket);
UdpPacket ackPack = NetKernel.postOffice.receive(port);
while (ackPack == null)
{
NetKernel.alarm.waitUntil(RETRANSMIT_INTERVAL);
ackPack = NetKernel.postOffice.receive(port);
if(ackPack != null)
{
break;
}
}
//当收到回复是 表示确认连接
if (ackPack.status == UdpPacket.ACK && Machine.networkLink().getLinkAddress() == mail.packet.dstLink) {
System.out.print("连接建立:");
}
} catch (MalformedPacketException e) {
return -1;
}
return res;
}
public int checkExistingConnection(int dest, int src, int srcport, int desport) {
//前两个是输入输出
for (int i = 2; i < openfile.length; i++) {
//看是否有相同的文件描述符
if (socketList[i] != null && (socketList[i] instanceof Socket)) {
Socket con = (Socket) socketList[i];
//如意已经存在 则返回索引号
if (con.destinationLink == dest &&
con.sourceLink == src &&
con.sourcePort == srcport &&
con.destinationPort == desport
) {
return i;
}
}
}
//如果不存在返回-1
return -1;
}
private int handleRead(int fileDescriptor, int vaddr, int size) throws MalformedPacketException {
// Check if the read wants UserProcess's handleRead instead (used for C code
// statements like printf)
if (fileDescriptor == 0 || fileDescriptor == 1) {
return super.handleSyscall(syscallRead, fileDescriptor, vaddr, size, 0);
}
// Return -1 if the input is invalid
if (size < 0 || (fileDescriptor >= MAX_SOCKETS || fileDescriptor < 0)
|| socketList[fileDescriptor] == null) {
return -1;
}
Socket fd = socketList[fileDescriptor];
OpenFile fd2 = openfile[fileDescriptor];
//读文件而不是读网络
if (fd == null&&fd2 != null) {
return super.handleSyscall(syscallRead, fileDescriptor, vaddr, size, 0);
}
else if(fd == null) {
return -1;
}
//判断一下收到的文件是不是要求关闭,要求关闭则返回确认
UdpPacket ask_close = NetKernel.postOffice.getFirst(fd.sourcePort);
if(ask_close!=null&&ask_close.status == 4) {
System.out.println("与客户端"+ask_close.packet.srcLink+"断开连接");
NetKernel.postOffice.receive(fd.sourcePort);
openfile[fileDescriptor]=null;
socketList[fileDescriptor].close();
socketList[fileDescriptor]=null;
return -1;
}
// UdpPacket ackPacket=null;
// if(ask_close!=null&&ask_close.status == UdpPacket.STP && Machine.networkLink().getLinkAddress() == ask_close.packet.dstLink) {
// ackPacket = new UdpPacket(socketList[fileDescriptor].destinationLink, socketList[fileDescriptor].destinationPort, socketList[fileDescriptor].sourceLink, socketList[fileDescriptor].sourcePort, UdpPacket.FIN, 0, new byte[0]);
// NetKernel.postOffice.send(ackPacket);
// //收到最后的确认,则可以关闭
// UdpPacket finAck_close = NetKernel.postOffice.receive(socketList[fileDescriptor].destinationPort);
// while (finAck_close == null)
// {
// NetKernel.alarm.waitUntil(RETRANSMIT_INTERVAL);
// finAck_close = NetKernel.postOffice.receive(socketList[fileDescriptor].destinationPort);
// if(finAck_close != null)
// {
// break;
// }
// }
// if(ackPacket.status == UdpPacket.FINACK && Machine.networkLink().getLinkAddress() == ackPacket.packet.dstLink) {
// //关闭
// openfile[fileDescriptor]=null;
// socketList[fileDescriptor].close();
// socketList[fileDescriptor]=null;
// }
// }
// //如果不要求关闭要加回去
// if(ackPacket==null) {
// NetKernel.postOffice.waitingDataMessages[socketList[fileDescriptor].destinationPort].add(ask_close);
// }
//需要写入主存的内容
byte[] buffer = new byte[size];
int readSize = fd.read(buffer, 0, size);
if (readSize <= 0)
return 0;
Machine.stats().numPacketsReceived++;
//发送ACK
UdpPacket packet = new UdpPacket(fd.destinationLink, fd.destinationPort, fd.sourceLink, fd.sourcePort, UdpPacket.ACK, fd.currentSeqNum+1, new byte[0]);
//发送packet
NetKernel.postOffice.send(packet);
System.out.println("已发送确认信息");
//从内存中读出数据 写入 虚拟内存(主存) 然后返回字节数
int writeSize = writeVirtualMemory(vaddr, buffer, 0, readSize);
System.out.println("read ok");
return writeSize;
// // Receive buffers from Socket
// int bytesRead = 0;
// UdpPacket udpPacket = NetKernel.postOffice.receive(socketList[fileDescriptor].sourcePort);
//
// while (udpPacket == null)
// {
// NetKernel.alarm.waitUntil(RETRANSMIT_INTERVAL);
// udpPacket = NetKernel.postOffice.receive(socketList[fileDescriptor].sourcePort);
// if(udpPacket != null)
// {
// break;
// }
// }
// byte[] receivedata = udpPacket.payload;
// byte[] readBuffer;
//
// if (receivedata == null) {
// readBuffer = null;
// } else {
//
// readBuffer = new byte[receivedata.length];
// System.arraycopy(receivedata, 0, readBuffer, 0, readBuffer.length);
// if (readBuffer != null) {
// // Print the read buffer and update number of bytes read
// bytesRead += FileDescriptors[1].getFile().write(readBuffer, 0, readBuffer.length);
// }
// // Return error if nothing was written to the memory
// if (bytesRead == 0)
// return -1;
//
// socketList[fileDescriptor].bytesRead += bytesRead;
// return bytesRead;
// }
// return 0;
}
private int handleWrite(int fileDescriptor, int vaddr, int size) {
// 写控制台或者是写文件
if(fileDescriptor == 0 || fileDescriptor == 1||(socketList[fileDescriptor] == null&&openfile[fileDescriptor]!=null)) {
return super.handleSyscall(syscallWrite, fileDescriptor, vaddr, size, 0);
}
// Return -1 if the input is invalid
if(size < 0 || (fileDescriptor >= MAX_SOCKETS || fileDescriptor < 0)
|| socketList[fileDescriptor] == null) {
return -1;
}
// // Count number of buffers to write and get the write buffer
// byte[] writeBuffer = new byte[size];
// int bytesToSend = readVirtualMemory(vaddr, writeBuffer, 0, size);
//
// // Send the buffer and update the number of bytes sent
// UdpPacket packet = null;
// try {
// packet = new UdpPacket(socketList[fileDescriptor].destinationLink, socketList[fileDescriptor].destinationPort, socketList[fileDescriptor].sourceLink, socketList[fileDescriptor].sourcePort, UdpPacket.DATA ,1, writeBuffer);
// NetKernel.postOffice.send(packet);
// socketList[fileDescriptor].bytesSent = bytesToSend;
// } catch (MalformedPacketException e) {
// e.printStackTrace();
// }
// return bytesToSend;
Socket fd = socketList[fileDescriptor];
if (fd == null)
return -1;
byte[] buffer = new byte[size];
//读取主存中的信息 虚拟内存
int readSize = readVirtualMemory(vaddr, buffer);
if (readSize == -1)
return -1;
//写入文件
int returnValue = fd.write(buffer, 0, readSize);
if (returnValue == -1)
return -1;
UdpPacket ackPack = NetKernel.postOffice.receive(fd.sourcePort);
int count = 0;
while (ackPack == null)
{
NetKernel.alarm.waitUntil(RETRANSMIT_INTERVAL);
ackPack = NetKernel.postOffice.receive(fd.sourcePort);
if(ackPack != null)
{
System.out.println("收到确认信息");
break;
}
count++;
if(count>10) {
fd.SeqNum--;
returnValue = fd.write(buffer, 0, readSize);
if (returnValue == -1)
return -1;
}
}
Machine.stats().numPacketsSent++;
System.out.println("write ok");
return returnValue;
}
private int handleClose(int fileDescriptor) throws MalformedPacketException {
Socket socket=null;
//删除socket和openfile
if(socketList[fileDescriptor]!=null) {
socket = socketList[fileDescriptor];
UdpPacket packet = new UdpPacket(socket.destinationLink, socket.destinationPort,socket. sourceLink, socket.sourcePort,UdpPacket.STP, socket.currentSeqNum + 1, new byte[0]);
//发送packet
NetKernel.postOffice.send(packet);
// UdpPacket fin_close = NetKernel.postOffice.receive(socketList[fileDescriptor].destinationPort);
// while (fin_close == null)
// {
// NetKernel.alarm.waitUntil(RETRANSMIT_INTERVAL);
// fin_close = NetKernel.postOffice.receive(socketList[fileDescriptor].destinationPort);
// if(fin_close != null)
// {
// break;
// }
// }
// if(fin_close.status == UdpPacket.FIN && Machine.networkLink().getLinkAddress() == fin_close.packet.dstLink) {
// UdpPacket FinAckPacket = new UdpPacket(socketList[fileDescriptor].destinationLink, socketList[fileDescriptor].destinationPort, socketList[fileDescriptor].sourceLink, socketList[fileDescriptor].sourcePort, UdpPacket.FINACK, 0, new byte[0]);
// NetKernel.postOffice.send(FinAckPacket);
// }
openfile[fileDescriptor]=null;
socketList[fileDescriptor].close();
socketList[fileDescriptor]=null;
}
return 1;
}
4.2
1.题目要求:
通过使用消息传递原语在多个(至少3个)用户之间实现一个类似IRC的“网络聊天”应用程序,演示您的消息传递原语的工作原理。网络聊天类似于UNIX talk实用程序,但它可以用于N向对话(而不仅仅是双向对话);每个用户坐在不同的机器上,任何人键入的任何内容都会广播给与聊天室相连的其他所有人。
2.问题解决思路:
服务器:服务器一致处于一个接受信息的无限循环,如果接收到的信息是一个连接请求则建立连接,如果是正常信息,则令这个信息广播到其他和服务器连接的客户端。
客户端:客户端启动后便于服务器连接,然后处于一个无限循环,不断的写数据给服务器,在每个循环中,还会检查是否收到有数据,收到的话就print打印。如果写的是“.”,则断开连接。
3.变量说明:
无
4.源代码:
服务器:
#include "syscall.h"
#include "stdio.h"
#define MAX_TEXT_SIZE 1000
#define MAX_CLIENT_SOCKETS 16
int clientSockets[MAX_CLIENT_SOCKETS], receivedEnd;
char receivedText[MAX_TEXT_SIZE];
void broadcastFromClient(int clientNum);
int main(int argc, char* argv[]) {
int newSocket = 0, i;
char result[1];
for (i = 0; i < MAX_CLIENT_SOCKETS; i++) {
clientSockets[i] = -1;
}
while (1) {
if (read(stdin, result, 1) != 0) {
break;
}
newSocket = accept(15);
if (newSocket != -1) {
clientSockets[newSocket] = newSocket;
printf("client %d connected\n", newSocket);
}
for (i = 0; i < MAX_CLIENT_SOCKETS; i++) {
if (clientSockets[i] != -1) {
broadcastFromClient(i);
}
}
}
}
void broadcastFromClient(int clientNum) {
int i, bytesWrit, bytesRead;
char result[5];
bytesRead = read(clientSockets[clientNum], result, 5);
if (bytesRead == -1) {
return;
}
if (bytesRead == 0) return;
for (i = 0; i < MAX_CLIENT_SOCKETS; ++i)
if (clientSockets[i] != -1 ) {
bytesWrit = write(clientSockets[i], result, 5);
if(bytesWrit<0)
{
printf("发送失败\n");}
}
}
客户端:
#include "syscall.h"
#include "stdio.h"
#include "stdlib.h"
#define MAX_TEXT_SIZE 1000
#define false 0
#define true 1
char receivedText[MAX_TEXT_SIZE], sendText[MAX_TEXT_SIZE];
int receivedEnd, sendEnd, host, socket, bytesRead, bytesWrit;
int main(int argc, char* argv[]) {
int host, socket, bytesRead, bytesWrit, done = false;
char lastByte;
receivedEnd = 0;
if (argc != 2) {
printf("error: please supply host address\n");
return 1;
}
host = atoi(argv[1]);
socket = connect(host, 15);
printf("Successfully connected to host %d\n", host);
while(!done) {
sendEnd = 0;
if ((bytesRead = read(stdin, sendText, 1)) == 1) {
lastByte = sendText[0];
sendEnd++;
while (lastByte != '\n') {
if ((bytesRead = read(stdin, sendText + sendEnd, 1)) == -1) {
printf("Error : Can't read from stdin. Bye!\n");
done = true;
break;
} else {
sendEnd += bytesRead;
lastByte = sendText[sendEnd - 1];
if (sendEnd == MAX_TEXT_SIZE - 1) {
sendText[MAX_TEXT_SIZE - 1] = '\n';
break;
}
}
}
if (sendText[0] == '.' && sendText[1] == '\n') {
printf("Received exit command. Bye!\n");
break;
} else if(sendText[0] != '\n') {
bytesWrit = write(socket, sendText, sendEnd);
}
}
bytesRead = -1;
strcpy(receivedText,"");
bytesRead = read(socket, receivedText, 5);
if (bytesRead > 0) {
printf("客户端说:%s\n",receivedText);
}
}
close(socket);
return 0;//Success
}
总结起来可以分为下面的几步:
1.首先令网络连接套接字与文件类继承自同一个接口以致使他们可以同样的用文件的读写来进行使用。
2.然后实现网络的系统调用,收发信息均包装成一个网络层数据包,最后调用网卡的链路层程序来发送接收。(在nachos中就是networklink类的方法来模拟)
另外,这个实验还让我体验到了计算机网络的TCP协议的一些内容,包括三次握手等,也让我体会到了网络协议设计的复杂与巧妙。