1 Introduction
In this assignment, we are going to implement a chat program using UDP Layer 4 protocol talking
across an unreliable channel. The assignment will cover socket programming. You will use C or C++
to implement it although C is recommended.
Before starting the implementation, please read Section 3.4 – Principles of Reliable Data Transfer from Kurose & Ross to cover the theory. For the practice, you can read Beej’s Guide to Network
Programming (Using Internet Sockets). Finally, to understand the big picture regarding the reliable data transfer, you can check out the interactive material on the Kurose & Ross website and
this animation.
You can discuss the homework and ask your questions on the discussion forum thread on our
ODTUClass page. I am also available at yigit@ceng.metu.edu.tr.
2 Setup
Please follow the instructions given in this section thoroughly to set up your homework environment.
We will use an Ubuntu Vagrant box as the host machine and create two network containers in
it. By adjusting the parameters of the interfaces (at both ends) between the client and the server,
we will have a seamless and native unreliable channel without relying on any external programs.
Run the following commands on an empty folder. If you follow along, you can code on your
host machine which is synced with the Vagrant box, so you can compile and run your client and
server on there.
(host) vagrant init ubuntu/jammy64 # create a new ubuntu 22.04 box
# Places a Vagrantfile on the empty directory
# We will develop alongside it
(host) mkdir code
Now we have a directory with a Vagrantfile and the code directory inside. We can edit the
Vagrantfile to have the code directory synced inside the Vagrant box.
1
# -\*- mode: ruby -\*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don’t change it unless you know what
# you’re doing.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/jammy64"
# config.vm.synced\_folder line is commented out, you can edit it
config.vm.synced\_folder "./code", "/home/vagrant/code", type: "rsync"
# the rest of the file is commented out and is not important
end
You can use vagrant rsync to push your code inside the Vagrant box or vagrant rsync-auto
on another terminal (in the background) to automate the process.
The following sets up the containers.
(host) vagrant up
(host) vagrant ssh
# Set $TERM and useful aliases to enter containers quickly
(vagrant) echo -e "export TERM=xterm-256color" >> .bashrc
(vagrant) echo -e ’alias server="sudo nsenter --net=/var/run/netns/netns0"’ >> .bashrc
(vagrant) echo -e ’alias client="sudo nsenter --net=/var/run/netns/netns1"’ >> .bashrc
(vagrant) source ~/.bashrc # use the correct TERM and reload .bashrc
(vagrant) sudo apt update && sudo apt upgrade
(vagrant) sudo apt install gcc make
(vagrant) sudo ip netns add netns0
(vagrant) sudo ip link add veth0 type veth peer name ceth0
(vagrant) sudo ip link set veth0 up
(vagrant) sudo ip link set ceth0 netns netns0
(vagrant) sudo ip netns add netns1
(vagrant) sudo ip link add veth1 type veth peer name ceth1
(vagrant) sudo ip link set veth1 up
(vagrant) sudo ip link set ceth1 netns netns1
(vagrant) sudo nsenter --net=/var/run/netns/netns0
(server container) ip link set lo up
(server container) ip link set ceth0 up
(server container) ip addr add 172.24.0.10/16 dev ceth0 # from private IPv4 block
(server container) exit
(vagrant) sudo nsenter --net=/var/run/netns/netns1
(client container) ip link set lo up
(client container) ip link set ceth1 up
(client container) ip addr add 172.24.0.20/16 dev ceth1
(client container) exit
(vagrant) sudo ip link add br0 type bridge
(vagrant) sudo ip link set br0 up
2
(vagrant) sudo ip link set veth0 master br0
(vagrant) sudo ip link set veth1 master br0
The instructions were adapted from Container Networking is Simple!. If you are curious about
the process please refer to that tutorial, but understanding it is not necessary to complete this
homework.
You can also follow the steps on this screencast to see what should be happening at each step.
At the end of the screencast I demonstrate the nc binary as a makeshift server-client chat program.
Your binaries will have a similar behavior.
You should run your client binary at the client container and the server binary at the server
container.
2.1 Manipulating Traffic
All this setup for containers would be quite pointless sans this step. Here, we will write netem
(Network Emulator) rules using tc to make the connection between the server and the client a lot
worse. This section was prepared according to the lab manual from Jed Crandall’s course. You
can refer to that document for a complete picture.
The following tc/netem rules are the worst case you will have to deal with. You should run
the rules twice inside each container, one for each interface.
# on the server, for the server’s interface
tc qdisc add dev ceth0 root netem delay 100ms 50ms loss 25% 10% duplicate 25% reorder 25% 50%
# on the client, for the client’s interface
tc qdisc add dev ceth1 root netem delay 100ms 50ms loss 25% 10% duplicate 25% reorder 25% 50%
While developing, you might want to start with a good channel between the client and the
server, and degrade it as you complete the requirements. If you would like to follow that route,
you should first start with;
# on the server, for the server’s interface
tc qdisc add dev ceth0 root netem delay 100ms 10ms
# on the client, for the client’s interface
tc qdisc add dev ceth1 root netem delay 100ms 10ms
And then change the tc rules gradually.
# on the server, for the server’s interface, note the change rather than add
tc qdisc change dev ceth0 root netem delay 100ms 10ms loss 20%
# on the client, for the client’s interface
tc qdisc change dev ceth1 root netem delay 100ms 10ms loss 20%
The following are useful as well;
# see the current rules in effect
tc qdisc show
# deleting a rule using change is cumbersome
# delete alltogether and start from scratch
# mind the interface
tc qdisc del dev ceth0 root
소개이 과제에서는 신뢰할 수 없는 채널에서 UDP 계층 4 프로토콜 토킹을 사용하는 채팅 프로그램를 사용하여 채팅 프로그램을 구현해 보겠습니다. 이 과제에서는 소켓 프로그래밍을 다룰 것입니다. C 또는 C++를 사용하여 구현할 수 있지만 C를 권장합니다.구현을 시작하기 전에 Kurose & Ross의 섹션 3.4 - 신뢰할 수 있는 데이터 전송의 원칙을 읽고 이론을 다루시기 바랍니다. 실습은 Beej의 네트워크 가이드(인터넷 소켓 사용)를 참조하세요.프로그래밍(인터넷 소켓 사용)을 참고하세요. 마지막으로, 안정적인 데이터 전송에 대한 큰 그림을 이해하려면 Kurose & Ross 웹사이트의 대화형 자료 및이 애니메이션.숙제에 대해 토론하고 토론 포럼 스레드에서 질문을 할 수 있습니다.토론 포럼 스레드에서 토론하고 질문할 수 있습니다. yigit@ceng.metu.edu.tr 에서도 확인할 수 있습니다.2 설정이 섹션의 지침을 철저히 따라 숙제 환경을 설정하세요.우분투 배그런트 박스를 호스트 머신으로 사용하고 그 안에 두 개의 네트워크 컨테이너를 생성합니다.생성합니다. 클라이언트와 서버 사이의 인터페이스(양쪽 끝)의 매개변수를 조정하여,외부 프로그램에 의존하지 않고도 원활하고 신뢰할 수 있는 네이티브 채널을 확보할 수 있습니다.빈 폴더에서 다음 명령을 실행합니다. 따라하면 다음과 같이 코딩할 수 있습니다.방랑자 상자와 동기화되는 호스트 머신에서 코딩할 수 있으므로 클라이언트와서버를 컴파일하고 실행할 수 있습니다.(호스트) vagrant init ubuntu/jammy64 # 새 ubuntu 22.04 상자 만들기# 빈 디렉토리에 방랑자 파일을 배치합니다.# 함께 개발할 것입니다.(호스트) mkdir 코드이제 Vagrant파일과 코드 디렉터리가 있는 디렉터리가 생겼습니다. 이제Vagrant파일을 편집하여 코드 디렉터리가 Vagrant 상자 안에 동기화되도록 할 수 있습니다.1# -\*- 모드: ruby -\*-# vi: set ft=ruby :# 모든 방랑자 구성은 아래에서 이루어집니다. Vagrant.configure의 "2"# 의 "2"는 구성 버전을 구성합니다(이전 버전과의 호환성을 위해# 이전 버전과의 호환성을 지원합니다). 여러분이 무엇을 하고 있는지 모른다면 변경하지 마세요.# 무엇을 하는지 모른다면 변경하지 마세요.Vagrant.configure("2") do |config|config.vm.box = "ubuntu/jammy64"# config.vm.synced\_folder 줄이 주석 처리되어 있으므로 편집할 수 있습니다.config.vm.synced\_folder "./code", "/home/vagrant/code", 유형: "rsync"# 파일의 나머지 부분은 주석 처리되어 중요하지 않습니다.end다른 터미널에서(백그라운드에서) Vagrant 상자 안에 코드를 푸시하거나 vagrant rsync-auto를 사용하여 다른 터미널(백그라운드에서)에서 프로세스를 자동화할 수 있습니다.다음은 컨테이너를 설정하는 방법입니다.(호스트) vagrant up(호스트) vagrant ssh# 터미널과 유용한 별칭을 설정하여 컨테이너에 빠르게 진입합니다.(vagrant) echo -e "export TERM=xterm-256color" >> .bashrc(vagrant) echo -e 'alias server="sudo nsenter --net=/var/run/netns/netns0"' >> .bashrc(vagrant) echo -e 'alias client="sudo nsenter --net=/var/run/netns/netns1"' >> .bashrc(vagrant) source ~/.bashrc # 올바른 TERM을 사용하고 .bashrc를 다시 로드합니다.(방랑자) sudo apt 업데이트 && sudo apt 업그레이드(방랑자) sudo apt 설치 gcc make(방랑자) sudo ip netns add netns0(vagrant) sudo ip link add veth0 type veth 피어 이름 ceth0(vagrant) sudo ip link set veth0 up(VAGRANT) sudo ip link set ceth0 netns netns0(VAGRANT) sudo ip netns add netns1(vagrant) sudo ip link add veth1 type veth peer name ceth1(VAGRANT) sudo ip link set veth1 up(방랑자) sudo ip link set ceth1 netns netns1(vagrant) sudo nsenter --net=/var/run/netns/netns0(서버 컨테이너) IP LINK SET LO UP(서버 컨테이너) IP LINK SET CETH0 UP(서버 컨테이너) ip addr add 172.24.0.10/16 dev ceth0 # from private IPv4 block(서버 컨테이너) 종료(방랑자) sudo nsenter --net=/var/run/netns/netns1(클라이언트 컨테이너) IP 링크 설정 설정(클라이언트 컨테이너) IP LINK SET CETH1 UP(클라이언트 컨테이너) ip addr add 172.24.0.20/16 dev ceth1(클라이언트 컨테이너) 종료(vagrant) sudo ip link add br0 type bridge(방랑자) sudo ip link set br0 up2(vagrant) sudo ip link set veth0 master br0(방랑자) sudo IP 링크 설정 VETH1 마스터 BR0이 지침은 컨테이너 네트워킹은 간단하다! 에서 가져온 것입니다. 이 과정이 궁금하다면프로세스가 궁금하신 경우 해당 튜토리얼을 참조하시기 바라며, 이 숙제를 완료하기 위해 이 튜토리얼을 이해할 필요는 없습니다.숙제.이 스크린캐스트의 단계를 따라 각 단계에서 어떤 일이 일어나야 하는지 확인할 수도 있습니다.스크린캐스트 마지막에는 임시 서버-클라이언트 채팅 프로그램으로서 nc 바이너리를 시연합니다.여러분의 바이너리도 비슷한 동작을 할 것입니다.클라이언트 바이너리는 클라이언트 컨테이너에서 실행하고 서버 바이너리는 서버컨테이너에서 실행해야 합니다.2.1 트래픽 조작하기이 단계가 없다면 컨테이너에 대한 이 모든 설정은 무의미할 것입니다. 여기서는 네트워크 에뮬레이터(네트워크 에뮬레이터) 규칙을 사용하여 서버와 클라이언트 사이의 연결을 훨씬 더더 나쁘게 만들겠습니다. 이 섹션은 Jed Crandall의 강좌의 실습 매뉴얼에 따라 준비되었습니다. 여러분해당 문서를 참조하세요.다음 tc/netem 규칙은 여러분이 처리해야 할 최악의 경우입니다. 실행해야 합니다.규칙을 각 컨테이너 내부에서 각 인터페이스에 대해 하나씩 두 번 실행해야 합니다.서버의 인터페이스를 위해 서버에 #를 추가합니다.