 All right, so we can get started. So this is a little loud. OK, so this is lecture 17 of computer science 162. So still kind of loud. Goals for today, that's a little better. So we're going to talk about the Socket API. So this is the interface between applications and the kernel for doing networking. And then we're going to talk about TCP. And TCP contains four important components that we're going to talk about today. The first being how you open a connection, the three-way handshake that occurs. The second being reliable transfer. The third being when you're done with a connection, how you actually tear that connection down. And there are some interesting challenges. Seems like it'd be really simple, but it's actually a very complex problem. And then flow control. What we're not going to talk about is congestion control. And this is a plug for going across the hall and listening in on EE-122 if you want to learn about how congestion control works in TCP. So let's start with the Socket API. So I have a bunch of applications. So I have Mozilla, I have Firefox rather, I have Thunderbird, and I have Skype. So a web browser email and a video conferencing application. They use the Socket API to talk to the transport layer, the transmission control protocol and the user datagram protocol. And then those two talk to the internet protocol layer, the network layer. Now, where do you think the Socket API was invented? Wrong answer. Sorry, not Stanford. Berkeley. Back in the 1980s, it was part of the Berkeley standard distribution. It is the most popular network API today in billions of devices, many, many billions of devices. Every smartphone supports the Socket API. That projector with its little ethernet cable supports the Socket API. Laptops support the Socket API. So it's in many different operating systems, including embedded operating systems. And then most languages support it. So obviously Java, Python, Perl, and so on all support the Socket API. It's very similar to the file API that we have in Unix. So you actually have a socket descriptor, which is very similar to a file descriptor. It's a handle that gives you access to a socket. And then you can use read, write, and close system calls also on a socket. All right? Now, the transmission control protocol is built on top of the Sockets layer. And it provides you with reliable, in order, at most once delivery. So lots of guarantees there. Yes. Ah, so good question. When they were building the socket API, why didn't they build it on top of the file descriptor API? It is. It shares a lot of commonalities in terms of the kinds of functions that you can do on a file handle. You can open and close and read. You can do on a socket also. So it shares a lot of similarities. The difference is that there are some things that you want to do on Sockets that are very different from what you would do on a file. You wouldn't wait for a file to appear. Whereas with a socket, you'll wait for an incoming connection to appear. Or you'll make a connection to another remote machine. So there's some differences, and that's why it's not a perfect match. But a lot of the functions are actually shared between the two. OK, so we get all these great things. Last week, we heard about IP was best effort delivery, which means maybe you get the packet. Maybe you don't. Maybe it arrives in order. Maybe it doesn't. Maybe it's folded, spindled, mutilated. Not with TCP. With TCP, you get the packet. It arrives in order. You only get it once. You don't get duplicates, and it will be delivered. It's also stream-oriented. So you can send arbitrary length messages across TCP. Whereas underlying that, IP is fixed-size packets. Or I shouldn't say not fixed-size packets. It's maximum-sized packets. It also provides multiplexing and demultiplexing. So IP remembers between two machines. Your machine has a unique IP address. My phone has another IP address. But there are many applications running on each one of those. TCP gives you the demultiplexing and the multiplexing to let those different applications, both on my laptop and my smartphone, talk to each other. I could have a web server on my laptop and a web browser on my phone. I could have a mail server on my laptop and a mail client on my phone. And those both can work because TCP takes care of multiplexing and demultiplexing into IP. And then you also get congestion and flow control. So congestion control is how we deal with the fact that, for example, if we're using a wireless network, it could be very crowded. And we want to fairly share that bandwidth across all of the different users of the network. So TCP tries to be fair when it's using the network. Then flow control is what we'll actually talk about today. And that's where I'm sending data at a high data rate from my laptop to my phone or from my server to my phone. And this phone has a little bitty, tiny ram in it. So I don't want to overrun that buffer. And flow control is a way of the phone saying, hey, don't send me anymore. We'll see how that works. And then there's lots and lots and lots of applications that are built on top of TCP, or applications that want reliable communication. You build things like chat. You don't want to lose messages. So we can wonder about certain operating systems, whether they, certain mobile phone operating systems, whether they use TCP, because my messages always seem to get lost. File transfer, web browsing, all takes place over TCP. Any questions? OK. So TCP as a service does the following things. First, we open the connection. There's a three-way handshake that occurs when we open the connection to set up that connection. Then we actually send our bytes. And so again, bytes go from one tuple to another tuple. The tuple is the local IP address and local port to the remote IP address and the remote port. Now I did say it provides this guarantee of reliable in-order at most one's delivery. But the real world is not perfect. And connections can fail. So if I'm talking to the access point in this room and the power goes out, what are the odds of that happening? Then there's no way my packets are going to get through. And so when that happens, TCP will reset the connection. So it's going to try and it'll try and it'll try. So even if I'm dealing with a noisy wireless network, the packets will eventually get through. But if it tries and tries and tries and can't get the data through, it resets the connection. So you'll see this with a web browser. You'll wait for a really slowly loading page and then it'll say the connection was reset. That means TCP tried really hard and then gave up. OK. And then when we're done, we tear down the connection. So let's go through each of these processes. So first, we want to open the connection. So our goal here is between, yes, question? Yes. So that's a very good question. These messages can be of arbitrary length. So how do we know when we can close the connection? That's why it becomes, that's why we're going to actually spend some time talking about how you close the connection. And it's not, you think it would be easy to just say, OK, close the connection. But it's actually a lot more complicated under the covers. OK. So first, we're going to open the connection. So we need to agree on some parameters. And in particular, the parameter we need to agree on is the starting sequence number that we're going to use. Now the starting sequence number is going to be the sequence number of the first byte in the stream that we're going to send. So this way, when we get bytes at the other end, we can tell whether they're arriving in order or not. If we didn't know what the starting sequence number was, we'd have no way of knowing. Now as a really simple security thing, and also to deal with repeated connections that I might make from one machine to another, we make the sequence numbers random. So hopefully that means we don't have collisions. I don't get a packet that was delayed in the network from a connection that I just had from a particular host. OK, so the way this works is the server, the application here calls listen. It says I want to listen on a port. So if it's a web server, it says I want to listen on port 80. If it's a server used to send mail, it says I want to listen on port 25. It's SSH service, as I want to listen on port 22, and so on. These are well-known numbers. If you go in your computer, if you have a Mac, and you look in slash, et cetera, slash services, you'll see a list of all these well-known port numbers. OK, at the client side, it says connect. So this is you typed in, www.cs.berkeley.edu. And it connects to that machine on port 80. So it'll issue a connect. And the operating system will now send a special packet from the client to the server called a SIN packet. This contains its proposal for the first sequence number. So we pick this random number, x, and we send it. So that message arrives here. And when it arrives, the server application gets notified that there's an incoming connection, and then it accepts that connection. And now once it accepts the connection, the operating system now will send back a message containing a SIN and ACK, and a sequence number Y and an ACK x plus 1. So why does it send x plus 1 back? Any ideas? Exactly. It means that we have received the connection. We received the x. So we're sending it back to say, yes, I got your x. And now I'm expecting x plus 1. It also sends a sequence number Y, because TCP is bidirectional. So we can send data from the server to the client or from the client to the server. So we need to tell the other machine, the client here, what packets to expect from us. So it will expect y plus 1 from us. So once we get that, we now have to send another message, an acknowledgment, to say we got the SIN ACK. So we send the SIN. We receive that, send back a SIN ACK, and then send back an ACK for the SIN ACK with y plus 1 to let it know we got it. Yes, in the back first? So the question is, what happens if you pick randomly your sequence number as the max, and then plus 1 causes it to overflow? So it wraps around. We'll see everything in networking is all about circular. So we assume it's modular arithmetic. Question, so why do we have to add 1 and not just respond with x or respond with y? Because we tell it we've received and we're expecting the next one. So when we tell it y plus 1, we're saying, okay, the next thing I'm expecting from you is y plus 1. And same thing with the x plus 1. We'll see when we go through, the various reliability protocols exactly how that gets worked. But it's basically, we're saying what we expect next, not what we've received in this case. Okay, so yes, question? Ah, why not start with zero? So originally they did, and the problem is that what happens if I open a connection to your machine and now I close the connection, and now I open another connection. And I do that really quickly. Then if packets get reordered in the network, you don't know whether the packets are coming in from the first connection or from the second connection. So by starting with a random sequence number, the idea is we start off set and that way we hopefully don't have overlap. Yes, question? That is absolutely correct. A packet of data has not been sent. Now as an optimization, we can actually send data, we can send our HTTP get requests, can go in this packet also. Because the ACK is really small, it's just a bit that we flip in the packet. And the sequence number's pretty small also. But we have added one round trip time. Because we have to wait for the message to go when the message to come back and then we could actually send our data. So we're adding an extra delay. And this, in many cases this won't be noticeable, but if you're operating over a satellite link, if you have like some satellite internet service at home in the woods or something like that, then this could be significant. Now the benefit however we do get from having this handshake is congestion control. That 40 byte packet that we send one way and we get back the response is a way of probing to see is the network congested. If that packet gets dropped we know that the network is congested. It also acts against packets that might have been delayed and stuck in the network and might arrive after a previous connection has closed. Make sure those packets have already gotten through the network before we now start sending data. Okay, so this kind of agreement, agreeing on sequence numbers, this was pretty easy. So let's try a little bit harder problem. This problem is called the general's paradox. Now here's the constraints of the problem. We have two generals. One's on one mountain, one's on the other mountain. There's an army in the middle. And they want to attack that army. Now each of the generals has a small army. So if they were to go and attack alone they'd get slaughtered. But together their army is larger than the army in the valley and they would be victorious. So they can communicate using messengers. So they can send a messenger through the valley to say something and the other general can send a messenger back. Now because there's an army in the middle these messengers could be caught, captured, spindled, folded, mutilated, whatever. So there's no guarantee that they'll get through. But we have a lot of messengers, infinite supply. They're really cheap, okay? So the question now, or I should say the problem now is we need to coordinate our attack. If one general attacks and then the other one we fail, because if the first general attacks he gets slaughtered and the other general comes down, gets slaughtered too. So they have to coordinate and attack at the same time and then they'll win. So this was named after Custer because he arrived at Little Big Corn a few days early and didn't survive. Now, infinite number of messengers, two generals have to pick a time. Can we agree? I'll give you a hint, it's a paradox, okay? So we wanna make sure that these two entities and there's obviously an analog analogy here for computing, these two entities agree, sending as many messages as they want back and forth on something. Those messages can be lost or corrupted. The answer is no. Even if all the messages get through. This is why it's a paradox. Because you'd think if all the messages can go through this should work, right? We should be able to agree on a time because we know the messages are going through, no messages are lost. These messengers are really fast and sneaky. All right, so let's look at an example. All right, so first general, general A says to general B, let's attack at 11 a.m., sends the messenger, okay? What do we know at this point? What does general A know? He knows he sent a message. What does general B know at this point? That he got a message from general A saying let's attack at 11. So he says okay, yeah, let's attack at 11. All right, now what do we know? What does general A know? General B received the message and is okay to attack at 11. What does general B know? That he received the message at 11 a.m. was okay and he sent the message saying 11 a.m. was okay. So can we attack? I hear no's, why not? Exactly, because B doesn't know that A actually got this message. So B doesn't know, A might not have gotten the message and so then B goes down and gets slaughtered. And similarly, A knows that it got the message from B but it also knows that B doesn't know that it got the message. So it doesn't know, is B really gonna go? So maybe it'll go down at 11 and get slaughtered or maybe they'll both show up at 11 and they'll win. We can fix this, right? We send a message from A to B saying okay, yeah, it's 11. Now we can attack, right? Why not? Exactly, right? A does not know if B actually got that message and if B didn't get the message then B still might not know that A got the message so you can see. So we can fix this. We send another message, right? We can't, right? No matter how many messages we send back and forth there's always this sort of trailing thing and besides it'll be after 11 anyway and then we'll have to pick a new time. Okay, so this is the frustration, right? We can't know that the last message got through. So why did I take this divergent path and start talking about the general's paradox because this is exactly the situation we run into we're trying to close the connection. We don't know if that message we sent through saying let's close the connection actually got through. Now because we don't want it to take an infinite amount of time to try and close our connections we take a shortcut and we simplify it. It'll work in almost all cases but not all cases. All right, so the goal here is we want to agree that we're gonna close the connection. And by the way I should also say that we're gonna look later on in the semester at a solution to the general's paradox that doesn't say when you're gonna do something but says whether you will do something or not. So you can't use it to agree to attack but you could use it to agree to do something. No guarantees on when everybody will have actually done that though. It's called two phase commit. Okay, so we're gonna do a four way connection tear down process. So the first thing is the application on host one says close the connection. So it calls the closed system call and that's gonna send a thin packet for finish to host two. So now when host two gets this packet it's gonna send back a fin act. Now are we done? Not quite because all this is done is said that host one would like to close the connection but remember this is a bi-directional connection. So host two if this is a web server might still be sending data. So there may be more data that comes down because host two is not done. But at some point host two will have finished sending all of its data and now the application on host two will close the connection. So now we send a fin packet in the other direction. Are we done now? Not quite because host two doesn't know that it's fin actually got through. So it's gonna send back a host one rather will send back a fin act. Now when host two gets the fin act it knows the connection is closed. But does host one know the connection is closed? No, it's the same as the general's paradox. What if this message got lost? So for simplicity we just wait a time out period. We could try retransmitting the fin act but instead we just simply close the connection. So in most cases this will work. In a few cases it won't work and you'll get a connection that kind of gets stuck in a weird state. So that's closing. All right so now let's switch gears and talk about yes, the question is is there any way for host one to know whether or not the fin act got through? What does the class say? No, there's no way for us to guarantee right cause we could send back a fin act act right and then we wouldn't know if that went through and so then we'd have to send a fin act act act. I mean we could just go back and forth we would never know whether the one side would always kind of be left hanging wondering if its message had gotten through. So this is the best we can do in the real world. And again in 99.99999 something percent of the time it works just fine. Occasionally it'll get a little confused and one side or the other might not have gotten it. So we could time out here also if we didn't send it. Now why do we have to wait a time out period? Because if it doesn't get back an act from the fin it'll send another fin. And we wanna make sure that that fin arrives here and the connection is still open. Cause if we've already closed the connection when we get the fin then we wouldn't be able to retransmit the fin act. Cause we would have gotten rid of all of the state. Once you close the connection all the state is lost in the kernel. Yes. Yeah so that's a very good question. When you send the fin why don't we just simply mark the connection as closed on host two and throw everything away. The problem is that the fin could get lost and then on the host one side it would still think the connection was open. So by retransmitting the fin we're able to try, excuse me, we're able to try and get it through and then eventually we'll time out and reset the connection which is effectively gonna close it. Yeah. So the question is can I elaborate on a case where this would screw up? So the thing is where you could have a problem is if the fin act just keeps getting lost. So if the network connection is really bad I'll send multiple fins and then eventually I'll time out on the host one side I'll time out on the host two side and just mark the connection as closed. No so the question is will we lose any data? No because the data's already been sent here and in just a few minutes we'll learn how we send the data reliably. And again everything will work as long as we don't get into a pathological situation like the access point dies or an intermediate link completely fails and there's no route between the two hosts in the back. No, so the question is what happens if a packet arrives and the connection is closed? That's why we have to wait a timeout period because if the fin was sent and then the fin act gets lost, right? So if we lose the fin act we're gonna send another fin. And if we've already marked the connection as closed on host one then host one will say I've never seen this before but I'll just drop the packet on the floor. I don't know what connection this belongs to. So that's the reason why we wait this timeout period is because that way we can retransmit the fin and still have the state in the kernel. But we wanna clean up that state eventually, right? If this is a web server and we're getting thousands of connections per minute we wanna make sure we're not gonna use up all our socket space. So we wanna close connections very timely. This is an attempt to try and do that. In the ideal case the fin act goes through very quickly and we can mark it closed. Yes. Yeah, so the question is could we have quality of service for these kinds of closed operations? The answer is unfortunately there isn't an IP. Everything is best effort. There's no prior authorization. There have been lots of standards for having quality of service but that's usually at the lower layers at the link layer. So you may run into this timeout problem. On the project. So when you're doing the last project, project four you're gonna start up one of your servers listening on a particular port and try to connect to it. If you shut down the server and try to listen on the same port again it may not let you because it's waiting for this timeout period in case there's any packets floating around. So you may have to change the number. Depends on the operating system how long the timeout is. Okay, yes. You mean for the data? Yeah, so we're gonna go through exactly how we do reliable transfer on my next slide. Okay, any other questions? So why not just reply to, if you've already closed the connection why not just reply with a FIN Act to any incoming FINs? That could be used for denial of service. It could cause you to basically have to send packets back so you could use it for an amplification attack. I could take some very powerful server. I could spoof your IP address of your smartphone and send a FIN packet to the server with your IP address and then it'll send the FIN Act to you and I could just send a few of these and it'll just send a flood to you. Redirection attacks and things like that are something you wanna avoid. I think later on in the security part we'll talk about some of the attacks that can happen on the handshake side that are also spoofing attacks. Okay, so any other questions? Okay, let's talk about how we actually get our data there. So we can do this, there's some simple parts to this. I mean, the basic concept here is if a packet doesn't make it through, we retransmit it. And we're gonna number packets, we're gonna number acknowledgment so we can see if something goes missing. Our goal is to do this very efficiently. So we wanna transmit whenever we can transmit so we can move data as fast as possible and we wanna detect when a packet goes missing and be able to retransmit as quickly as possible. So we're gonna look at two schemes. First scheme we'll look at is called stop and wait and the second scheme is called sliding window. And we'll look at two different variants of sliding window. Stop and wait is just a toy protocol so we can understand how this works. Sliding windows is actually what's implemented in TCP. Okay, how does this work? So we use timeouts. So if a sender will timeout, if it doesn't receive an acknowledgment. So when I send a packet, I set a timer. And there's a lot of work that goes into figuring out how long should that timer be. Again, take EE-122 if you wanna know all the different algorithms that you use for computing how long to wait. Now, if you have a, every packet that the receiver gets, it's gonna send an acknowledgment back. So the sender can figure out if a packet goes missing because it'll see a gap in the sequence of acknowledgments that come back. So I'm sending packets, the receiver's sending back acts. Now, we have to be careful because the network is allowed to reorder which means packet one could arrive before packet two. So just because I see an acknowledgement for packet two doesn't mean, and I haven't seen one for packet one that packet one got lost. It could just be about to arrive. And then I get an acknowledgement for packet one. This is one of the things that makes it a little complicated. The network is allowed to do anything to the packets. Now, another form of acknowledgement is called a negative acknowledgement. A negative acknowledgement is where the receiver says, I didn't receive a packet. So the receiver could either say, I did receive a particular packet or it could say, I didn't receive a packet. Right, see, they're symmetric. I can say the same thing, right? I can either acknowledge a later packet and that tells you you have a gap or I can explicitly say I didn't get a particular packet. So let's look at a very simple protocol. Let's just start. All right, so this is called stop and wait and we'll look at it without any errors occurring. Very, very simple. The sender sends a packet, waits for an acknowledgement. Repeat, okay? So sender sends a packet, packet one to the receiver. Receiver sends back an acknowledgement. That takes total time, round trip time, where the one-way time is D, that's the delay. So roughly, if assuming the network is symmetric, the round trip time is twice the one-way delay. All right, sort of you can think about it the other way around. If you measure the round trip time, the one-way delay is approximately half the round trip time. Okay, it's time to get the packet out and back. All right, now we can send our second packet. Once that's received at the receiver, send back an acknowledgement for two, okay? So that takes another round trip time. Now we can send packet three and so on. Now, how many packets can you send? You can send one packet every round trip time, right? Cause here's one round trip time and we sent one packet. Here's another round trip time and we sent one packet. You know, we'd have to wait for this to come back and that'd be another round trip time and we'd send our third packet. So we can now ask, what's the throughput? How many bytes are we getting from A to B? From our sender to receiver. That's the number of bits that get delivered. So we need to make this a little bit more concrete. So let's pick some numbers. So just to be convenient, we're gonna say the round trip time is 100 milliseconds. That makes the delay approximately 50 milliseconds. So one way delay is half of that. And we're gonna make our packets 1500 bytes in size. Okay, so what's our throughput? Well, it's simply gonna be, how many bits can we get from A to B per second? So it's gonna be 1,500 bytes times eight bits over 100 milliseconds. So we get 120 kilobits per second. That's like dial-up speed, which is like twice dial-up speed, but it's pretty slow. Now, how fast are the links here? I didn't say, because it doesn't matter. That's the problem. These links could be one gigabit links. They could be 40 gigabit links. And we're only going to get 120 kilobit per second out of those links. So that's not very good. We obviously wanna do better. But this is a simple protocol just to explain how this works. So now let's add errors. Make it a little bit more complicated. So we wait and don't get an acknowledgement back because our acknowledgement actually gets lost. So how do we pick this time-out? Well, we're gonna wanna pick a time-out that's larger at least than the round-trip time. In fact, you don't even wanna make it close to the round-trip time. Because remember, these networks can reorder and there's jitter, which means that your packet might arrive a little bit later. If there was congestion on the network or something. We're on a link. So we have to pick a larger time-out. It's called the retransmission time-out RTO in TCP. And again, take 122 if you wanna learn exactly all the algorithmic ideas behind picking that. So after a time-out occurs, we can retransmit. Now we'll get another acknowledgement and hopefully that acknowledgement will go through. If it doesn't, we'll wait another time-out interval and retransmit again. And we'll just keep doing that until we finally give up and decide to reset the connection. So that's the basics of what we're gonna do here. Yes, question? No, so the receiver, here the receiver, the question is, does the receiver drop the packet after they get the second one? No, here the receiver successfully received the packet and so here it's just gonna send another acknowledgement. It could already have taken and passed the packet up to the application, because it arrived in order. Well, this will become a lot clearer when we look at sliding windows. Hopefully, hopefully not too confusing, yes. So remember, the question is, how would we know the RTT if the first packet never made it? So remember we had that two-way handshake. So the SIN Act tells us roughly what the round-trip time is. And then we're gonna pick a time-out that might be a multiple of that, just in case our SIN Act went through really quickly, but the network's kinda congested and so packets are taking longer to get through. Yes, so the question is, wouldn't you have to receive the act first? And yes, remember with the three-way handshake, I send a SIN and then I get back a SIN Act. So that tells the sender now what the round-trip time is. Ah, if the SIN Act never made it, then we'll send another SIN. And we'll just keep trying until we get through. And then eventually we'll give up and declare we can't connect to the host. Okay, so that was simple. Now I'm gonna make it unfortunately really complicated. We're gonna do sliding windows. So a window is just a set of adjacent sequence numbers. It's like two, three, four, five, seven, eight, nine, 10, 11. That's a window. They have to be adjacent. The size of the set is the window size. That's pretty easy. We're gonna assume we start with a window size of N. So if A is the last acknowledged packet of the sender without gaps, so it's the last packet that we got in a continuous stream going back to the beginning of the connection, then the window of the sender is A plus one, A plus two all the way up to A plus N. The sender can send packets in its window. So at the sender we keep track of what was the last packet we got acknowledged from the receiver and what are the packets we haven't had acknowledged yet. At the receiver we maintain a window also. B is the last packet that the receiver has received without a gap. So B's window is then B plus one all the way up to B plus N. Now the receiver can accept a packet that comes out of sequence as long as it's within its window. So if it gets like B plus one and then it gets B plus three and the window size is seven, that's perfectly fine. It can accept B plus three. Yes. Ah, the question is how do we know there will be no gaps? There can be gaps. This window in fact is everything before this window had no gaps. So we successfully received all the packets in order up to this point. Within this window the packets could arrive out of order. We can't slide the window forward until we have B plus one. Then we can slide it forward by one. We get B plus two, we can slide it forward again. But if we get B plus four and we haven't gotten B plus one we can't move the window yet. You'll see this hopefully it'll be a little bit clearer in just a moment, yeah. Yes, the question is does this mean the packets are explicitly numbered? Yes, every packet has to have a number. We're gonna change that later on, but for now every packet has a number. Okay, so we'll keep it simple. Window size of three. These are the unacknowledged packets within the sender's window. We're gonna keep track of them over here. And over here we're gonna keep track of the out of sequence packets in the receiver's window. Now first let's look at what happens without errors. So we send a packet one from the sender to the receiver. Okay, so now our window contains one packet. Then we send a second packet. Because remember we can send any, as long as it's within our window, we can send a packet. So that means we can send packet two. And in fact our window is three so we can also send packet three. So now as these get received what happens? So the receiver receives packet one. What's the receiver's window now? Nothing, right? Because we received one in order so we can just pass it up to the application. We send back an acknowledgement. Now when we get the acknowledgement for one at the sender, what's our window now? Two, three, four. Our window slides forward because we receive the acknowledgement for one and this is only keeping track of unacknowledged packets. So now we receive packet two here. We send back the acknowledgement. Our window slides forward. One more becomes three, four, five. And again we receive packet three and our window slides forward to four, five, six. And we can send six. Yes, that's correct. So the window at the receiver is keeping track of packets that are arriving out of order. When you start the connection there's no packets that have arrived. So if packet one arrives that's the first packet you're expecting. If packet three arrived first you'd have to put it in the window and wait. But there's no errors here. Everything's arriving in order and so it goes nice and cleanly and smoothly. Yes, no. The question is are we enforcing the sender seven to everything in order? No, it just happens again. This is an example with no errors. Everything arrives in order. We'll see an example with errors in just a moment. But in this case everything arrives in order so it comes in, gets delivered to the application. The receiver doesn't have to maintain anything in terms of state. Yes. Ah, question is do the windows have to be the same size on both sides? No. And we'll actually see how through flow control the receiver actually controls the sender's the window that they can use to send. Okay, so this keeps going. Back and forth as the packets keep going we can just keep sending and we're never gonna grow our window at the receiver because in this case all the packets are arriving in order. And at the sender we're just gonna slide our window forward sending a packet every time we get an acknowledgement. So now we can ask what's our throughput? Well in a round trip time how many packets can we send? Gonna be W times the size of the packets over the round trip time. So we can have W packets in flight. We were able to send one, two, three before we got the acknowledgement for one. And then we sent four, five, six before we got the acknowledgement for four. So each round trip time we have three packets in flight now as opposed to stop and wait which would be equivalent to basically a window size of one. We'll have to have one unacknowledged packet. Yes. So the question is if W is really, really large could we get back acknowledgements before we had actually exhausted our window at the sender? Yes, yeah. So now there's kind of the question of how do we actually set W? And ideally if we have a high speed link we wanna match W to be appropriate for both that link and the round trip time. So let's assume we've got a link with the capacity of a gigabit. We've got a round trip time of 80 milliseconds and we've got a packet length of 1,000 bytes. So what do we wanna make our window size be such that our throughput matches the link's capacity? So it's actually pretty simple. Remember the throughput was the window size times the packet size over the round trip time. So that's simply gonna be, we can flip that around by dividing out and we get that W, the window size is going to be C times the round trip time so we just had multiplied to get that to the other side and we divided by the packet size. So this is gonna be a gigabit times 80 milliseconds over 8,000 bits equals 10,000 packets. So if we set our window size to 10,000 we will be perfectly aligned with the bandwidth of the link because every time we complete a round trip time we'll have sent a gigabit, we'll have sent 10,000 packets worth of data which will fill that gigabit link. Yes. Yeah, so the question is, is there a bookkeeping overhead? Absolutely, there's jitter in the network so packets can get delayed and so we have to deal with the fact that there might be bursts that could occur. We also have to deal with the fact that there may be other users of that one gigabit link and then we also have to deal with the fact that the recipient might be my phone and I could easily fill the entire memory of the operating system on this phone in fractions of a second at that rate. So we're going to want to balance that and that's where flow control comes into play and that's where we get a lot more bookkeeping. Okay, so but roughly, when you're trying to figure out what the window size is supposed to be, it's going to be proportional to the bandwidth of the link or the capacity and times the delay. This is called the bandwidth delay product because you multiply the bandwidth and the delay and that'll give you what you want to set your window size to. All right. So now let's introduce errors because that makes it more fun. So there are two ways we could deal with errors and errors in this case is a lost packet or a lost acknowledgment. The first approach is called go back N and it means just that we're going to go rewind back N and retransmit. The other selective repeat. Now if there are no losses, these two protocols behave identically just as I showed you is exactly what happens. With go back N, you're allowed to transmit your window so your window size here is N so you can transmit up to N unacknowledged packets and then if you get a timeout for a particular packet, you don't get the acknowledgement for K, then you're going to retransmit packet K, packet K plus one, all the way up to the end of your window. Now, typically this will use a NAC-based approach instead of an act so it'll say, the receiver will send back a NAC, I didn't receive packet K and then you just retransmit K, K plus one and so on. The NAC tells you the first packet that you didn't receive in sequence. Okay, so let's look at an example. So here, same example we just saw, but packet four gets lost. So what's going to happen? Well, packet five arrives. Now our out of sequence packet window at the receiver grows because we were expecting four and didn't get it. So we're going to have to hold on to five. We can't give it to the application. Then packet six arrives and we have to do the same thing, we have to hold on to it. Now a timeout will occur for packet four and then we have to assume it's lost and so now we may also as an optimization earlier on get a negative acknowledgement for four but even if we do get a negative acknowledgement we can't retransmit the packet. We wait until the timeout and then transmit it. Now why don't we retransmit it earlier? Any ideas? Exactly. Remember that the network could be doing reordering. So it might be that we received five, we received six and then oh, here comes four. It took the slow boat around the different route and eventually arrived. So we wait for the timeout before we retransmit. So we're being conservative about when to retransmit. If we were being very aggressive we would have retransmitted as soon as we got the negative acknowledgement but if the network's reordering that's bad because that means we'll retransmit too much and that will impact our throughput because we're sending the same data again and especially in this case where we retransmitted four, five and six. So we're gonna really hurt our throughput. So this is called a good put. How many bytes are you delivering that are good bytes at the end of the day? Here we might be filling the link but we're wasting it because we're retransmitting five and six. Yes, question. We got two. Ah, so the question is how would we know if it got reordered if it was just delayed and even though we got these negative acknowledgements because we could get a positive acknowledgement saying I got six. And that tells us we got all the ones before it. So if that arrived before the timeout then we wouldn't have to retransmit four, five and six. So the question is if we don't wanna overwhelm the network why are we transmitting these negative acknowledgements multiple of those. Those are small, they're like about 40 bytes whereas the packet we're sending might be say 1,500 bytes. There's definitely an overhead. Communication requires checksums and packet headers and things like that. So there's definitely an overhead associated with any packets that you send was there a correct question? So the question is how does the receiver know what order it's supposed to receive things in? Every packet has an ID number, a sequence number. So if I received four I expect to receive five next and then six and then seven and so on. Question in the back? Ah, so the question is why do we have to receive five and six as well? This is go back in. So we go back in. So when you get a negative acknowledgement and a timeout occurs you roll back and retransmit everything. The assumption here was this was one of the first implementations in TCP. The assumption was that when you have losses you tend to have bursts of losses and so you just retransmit everything to get through the burst of the losses. Yes, what happens if the NAC gets lost? Well, we're sending multiple NACs but in any case we're gonna have a timeout which is gonna cause us to retransmit from the sender in any case. The question is why do we keep five and six in the receiver's window if the sender's gonna resend them? Cause they might get lost. The sender resends them but if the network is noisy they could also get dropped and this way we already have it so we might as well not drop it. We've allocated the memory for it, yes. Depends on the, the question is are NACs or only NACs being sent or NACs and NACs depends on the particular variant of TCP. There's like I think about three or four different variants at least which tweak this setting various ways. You'll see them in a moment. The names are, might look a little familiar. Okay, any other questions? Yes, that's right. Not receiving an act can also cause you to roll back. Okay, so an alternative that's rather recent in the last, probably in the last decade roughly to go back in is selective repeat. And this was made under, it took a long time for this protocol to be accepted and widely deployed. I think it was one of the service packs for XP that brought it to Windows. So that tells you kind of how recent it is. And it was mainly because there was a recognition that networks were pretty good at most times not reordering packets. When a packet was lost it really was lost. It wasn't matter that it was just kind of delayed and sent by a different route and so it showed up much later. So here, again it's a window protocol. We allow the sender to send up to unacknowledged packets. And if we assume a particular packet gets lost like packet K, the receiver indicates I lost packet K. I didn't get it. And it uses acknowledgements to do this instead of negative acknowledgements. And the sender just retransmits packet K, nothing else. So, coming back to our picture here, I don't get packet four. So I send an acknowledgement for packet five when I receive packet five. I then send an acknowledgement for packet six when I receive packet six. When the sender receives the acknowledgement for five but not four, it knows four was lost. So it sends, or the acknowledgement for four was lost. So it sends just four again. That's here. And then I'll send back an acknowledgement for four. And once I do that, now my window can slide all the way forward because I know five has been received and six has been received so it can become just seven. All right? So this is much better at retransmitting less as long as the network isn't busy reordering things under the covers. And again, with most modern networks reordering is not an issue. Yes. Yeah, so the question is could we make this better than even go back in by waiting a timeout before we retransmit? The downside of that would be delay, right? So as soon as we receive this acknowledgement that tells us there's a gap, we retransmit as opposed to perhaps waiting until much later and then retransmitting. So it's much more aggressive on the retransmission with the assumption that there isn't reordering. So if a packet gets lost or an acknowledgement gets lost, it really was lost and not just delayed and reordered. So this is why it was somewhat controversial because you're making this assumption and if the networks don't meet it, then you could end up with lots and lots of retransmissions occurring that are spurious retransmissions, not valid retransmissions or necessary, I should say retransmissions. Yes. That's correct. That's right. So if we go back to go back in, we keep the packets that we've received out of order. We're allowed to receive anything within our window we can receive and our window size is three. So we're expecting four at this point, we get five. That's within our window of four, five, six. We get six, we can keep that also. If we got packet seven, we'd have to drop it. It's outside our window. Yes. The window has, yeah. Right, so the question is what is actually, this window has three slots. Slot one is empty, the first slot of the window is empty. The second slot has five, the third slot has six. So we've reserved the buffer space. We'll see this in a moment, I think after the break. We'll see exactly how we kind of keep track of all of this. Any other questions? Summary. So TCP gives us this reliable byte stream. We open a connection with this three-way handshake. When we wanna close the connection, there's not an ideal solution because the general's paradox makes that impossible. So we have an approximation that allows us to close the connection in all but sort of the worst Byzantine cases. We have reliable transmission, is one of the other things that TCP provides and a very simple version of reliable transmission is stop and wait. Not very good because it does not work well on links that have a high bandwidth delay product. So instead we use a sliding window protocol but as we've just seen in the last couple of minutes, it's a really complicated protocol to implement. So it works, it's much more efficient but it's very complex. Questions? Okay, so again I want to revisit this slide because Professor Canning put this up right before we had Project To Do. This is a class that is all about collaboration because as a computer scientist, that's what you're gonna do in your job. You're gonna have to work with other people. So we strongly wanna encourage people to collaborate where appropriate. Now, where is it appropriate? When you're talking about designs, when you're talking about algorithms, when you're trying to figure out strategies for doing white box, glass box, black box, gray box, testing, those are all things that we want you to collaborate on. Also, if you finished your code and you got it working a week early and somebody else is having trouble getting their code working, helping them get their code working is a very productive thing to do in this class and something we strongly want to encourage people to help with. What's not okay, use common sense. So if you find solutions from somebody who took this class a couple years ago online, that's not an acceptable solution. Even looking at those is not because that's gonna cloud the way and color the way that you actually do your implementation. It turns out these projects have enough flexibility in the way you can do things that it's like a fingerprint when someone writes their code. In fact, it's a good enough fingerprint that we can actually compare against the submissions that people have done in prior years. So we did that. We found over 25% of the groups in this class are copying code from online solutions. That's not acceptable. So we're offering you an option. You can notify us that you are doing that. The deadline for that is tomorrow before midnight and we'll go lenient. That means you'll lose credit for the code portion, at least lose credit for the code portion of that project. If you don't, you risk losing all the credit for the project and potentially failing this class if we have to go to you and ask you that question. We know who is being honest in this class and working very hard. We know who isn't. So we're giving you the option to come to us, email us and make an appointment to see us and we will be lenient. Now, I know this is a really hard class and so there are a lot of us. We have six staff in this class. That's 12 hours of office hours a week and there are some students who are having trouble and they come to our office hours every week religiously and they go to multiple TAs, office hours. There are other students, there are others of you who I know are having a very tough time in this class and are not taking advantage of that. There's no reason. We're all very approachable. We're all doing this because we want you to be successful in this class. So there's no reason that you should do poorly in this class and no reason that you should really need to resort to looking at prior year solutions. We will sit with you and get your projects working. Now you got a message earlier today from actually, I think just before class Suzanne sent out the message because as we've been talking to the groups that are involved in this unpleasant process, it's proven to be extremely stressful for the students. This is already a stressful time to be a student and we want you to be aware and Suzanne wanted you to be aware that there are lots of resources that are all confidential, all free, that you can take advantage on campus. So even if you're not one of the students involved in this, if life is difficult right now, there's no reason not to stop and see Christine. Okay, so hopefully this will not be a topic, collaboration should be very clear and hopefully it's not be a topic we have to revisit again. With that, we'll take our five minute break. Okay, I still have a lot of slides to go through, so we're gonna get started a little bit earlier. All right, so, flow control. The challenge here is we wanna make sure a fast sender does not overwhelm a slower receiver. All right, so we put a buffer effectively between our producer and our consumers. Fresh out of lecture five, you can remember that far back. You've got the producer putting things in the buffer, the consumer taking things out of the buffer. So with TCP, I didn't quite tell you the truth, all right? So the sliding windows, we're doing those at the byte level, not the packet level. So for simplicity, I used packets, but from now on everything is gonna be in bytes and that's what TCP really does. So there are many variants of TCP that implemented go back in. TCP Tahoe, Reno, and new Reno. You can kinda see a naming trend that the Berkeley folks had. And then the version that did selective acknowledgement was called TCP Sack for selective acknowledgement. Now, the receiver will tell the sender how many more bytes it can receive without overflowing its buffer. This is called our advertised window. So now when we send an acknowledgement, the acknowledgement tells us the number of the next byte that the receiver is expecting instead of the next packet. It's gonna be the next byte that we're expecting. And that means the receiver has received, so if we send ACK for N, that means the receiver has received in sequence all the bytes up to N including N minus one. So now, flow control. So it's the same kind of producer-consumer relationship the sending process through its local operating system is filling a buffer in the operating system of the receiving host. That buffer is being delayed by, being drained rather by the receiving process. Now, TCP is implemented in the kernel, which means we don't wanna be context switching every time we get in a packet because one gigabit per second, we're receiving packets very, very quickly. So we're gonna batch instead, right? We're gonna use buffers and then the receiver will get a bunch of bytes out of the buffer all at once. So we can amortize the cost of context switching from the kernel into the receiving process. So we're gonna use buffers to make all this work. Now, we need buffers between the sending process and the OS, the OS and the other OS and the OS and the receiving process. That's this picture. Lots of buffers. So there's a buffer here, okay? Because the sending process is producing bytes. The OS is draining bytes out of this buffer and sending them to the remote OS and filling this buffer. And then this buffer is being drained by the receiving process. So some assumptions. We'll assume our max packet size is 100 bytes. We'll assume the receiver can store 300 bytes in its receiving buffer, right? And remember, the acknowledgement we send back is the next byte that we're expecting to receive in sequence. Now, to make all of this work, we use circular buffers. So how many people have seen circular buffers before? So not a lot. Circular buffers appear everywhere in operating systems and especially within the network. We use circular buffers for everything. So we're gonna assume we have a buffer that has size n, okay? So here's our circular buffer size n. And we're gonna use modular arithmetic when storing things into the buffer. So a particular sequence number of our buffer data, so like 28, would be stored in 28 mod n plus one. So 28 plus mod 10 rather is eight, plus one is nine, okay? And so on. The key thing about circular buffers is they wrap around, right? So the ELLO space WOR wraps around and then L and D. So this allows us to have a fixed size buffer that we can just keep circulating data through. So we need to keep track of the start of the buffer and the end of the buffer. But everything else is just done through modular arithmetic to index into the buffer. So this is a much more efficient and simpler way of storing the data than say, just keep growing and allocating more and more space say in the kernel. We have a fixed size buffer and it just wraps around. Okay, now with TCP, lots of variables we gotta keep track of. The last byte that was written by the sending process. The last byte that was acknowledged by the receiver. The last byte that was sent by the sender, okay, to here. And then on the receiver side, we have three more variables we have to keep track of. The last byte we received from the sender, okay? The next byte that we're expecting, so the next in sequence byte that we're expecting. And then the last byte that was read into the receiving process, all right? So flow control, oh, that's cute. Flow control basically keeps track of all of this at the receiver, right? So we have the last byte that's been read by the receiver, the next byte, so it's behind what we've actually received. Here's the last byte that we've received and then we're expecting the next byte to come in here. And when we fill this, it'll wrap back around. Now, the receiver has extra space here, right? This is occupied by data that we're waiting for the receiving process to take, but the rest of this is available. And so our advertised window is the number of bytes that we can still receive. Again, it's gonna be this white space in both sides. So in this case, it's going to be the max received buffer size minus the last byte received minus the last byte read. Okay, we do this all with modular arithmetic, so it works. So now over at the sender side, we have the last byte that's been acknowledged by the receiver. We have the last byte that's been written by the sending process and then we have the last byte that's been sent. So here we've actually kept up and we've sent the last byte that was written by the sender. The sender's window, the number of bytes it is allowed to send is now going to be reduced. We're gonna take the advertised window, instead of how much space we may have, the max send buffer now doesn't really matter unless it's smaller than the advertised window. We decrement the advertised window by the last byte sent and the last byte acknowledged. So this is how we keep track of how much space in the receiver's buffer we can go and fill and make sure we don't overrun it. Okay, this is still true if the receiver has missed data because the last byte read and the last byte received are not gonna have changed. We'll have a gap, but that's perfectly fine. Okay, the right window is how we limit the sending process and it's now gonna be a function of the max send buffer size. So it'll be the max send buffer size minus the last byte that we've written and the last byte that's been acknowledged. So again, it'll be the white space on the sender side. Lots of variables are gonna keep track of on both sides. So let's say the sending app wants to send 350 bytes and we're gonna assume that we can only have packets that are 100 bytes in size and the max receive buffer here is 300 bytes so our initial advertised window is 300 bytes. So 350 bytes get written by the process. Now we actually need to send them in the kernel. So first, we're gonna send our first packet. So our first packet goes out and it's from one to 100. So now our last byte acknowledged is still zero and our last byte sent is updated to 100. Now it gets received here. Our last byte received becomes 100 because we got that in order. Our next byte expected is 101. So now we send back an acknowledgment. The acknowledgment is 101 because we expect to get byte 101 next and our advertised window is now decremented because we've got 100 bytes filling our 300 byte buffer. So it's now gonna be 200 bytes more that can be sent. Okay, sender sends the second packet and the receiver gets the next 100 bytes. We update our last byte sent. We update our last byte received because that was in order. Next byte expected goes to 201. So now we adjust our advertised window because the receiving process pulled out the first 100 bytes and so our advertised window is still 200. Even though we've received 200 bytes, we only need enough space to store 100, I'm sorry, 200. No, I'm sorry, we only need enough space to store this 100 which has not been received by the receiving process yet. Okay, so the other side we can send our third packet and it gets lost. So what happens now? Our last byte sent goes to 300 and the sender stops sending. We've sent 300 bytes and our advertised window minus last byte sent minus last byte acknowledged goes to zero. So now we get the acknowledgement for 101. So our average and the advertised window is 200. So this acknowledgement tells us the next byte that's expected, which is gonna be 101, and that the receiver no longer needs thus the first 100 bytes. So we can drop them out of our window. Forget about them. Okay, we still can't send because the advertised window is still full. We sent 300 and we haven't been told that we can open up our window. So now we get the acknowledgement for the second packet and advertised window is 200. Now we can send new data because we know that second packet was received and the advertised window is 200 and so that allows us to send another 100 bytes. So we can send the last 50 bytes and that goes in. Okay, now we get back an acknowledgement for 201. And an advertised window of 50. So since it still specifies 201, we know that it was out of sequence. We know that it missed an acknowledgement. We expect it to see 301. So can we resend that, even though the advertised window is 50, can we resend that packet? That's a 100 byte packet and the advertised window is only 50. What's that? Send only 50 of it. Send only 50 of it. No. So it turns out actually, we can send the whole packet because we already sent it. So it was already allocated space was allocated for that packet. So we can resend it again. It's not gonna make the window grow. So we just simply resend it again. And then it gets it. Now we'll get the acknowledgement back for 351 and we're done. No more sending. So I think at this point, I'm probably gonna stop. There's only a couple more slides and you can look at them. It's basically a discussion. But I strongly encourage you take a look at the slides and work through the messages that are going back and forth and see if you understand how all of six of those parameters are being updated. And we'll also post some of the material from EE-122 if you wanna look at it a little bit more in depth. With that, any questions? Okay, see you on Wednesday.