Topics to Cover

Netcat

Netcat is a great network utility for reading from and writing to network connections using the TCP and UDP protocol. The basic syntax is as follows:
nc [options] ip_address port
There are many options (see here for more detail), but in this course, we will mainly use the following options:
-l: listen mode, for inbound connects
-u: UDP mode
-n: numeric-only IP addresses, no DNS
-v: verbose (typically used along with -n options)

TCP connection

Suppose Alice on a host with IP address 111.111.111.111 wants to connect to Bob on another host with IP address 222.222.222.222. In this case:
Alice plays the role of the client, and Bob plays the role of the server.
  • Bob (the server) listens at a specific port.

    Let's say the port number is 9000. Therefore, Bob should run the following nc command:

    nc -lnv 9000
    
  • Alice must know that Bob is listening at this port 9000.

    Alice connects Bob using port 9000.

    nc 222.222.222.222 9000
    
The following diagram summarizes the above description:
    Alice (111.111.111.111)                         Bob (222.222.222.222)

                                                    (step 1) nc -lnv 9000

    (step 2) nc 222.222.222.222 9000

UDP connection

UDP connections can be established essentially in the same manner. But, we need to add "u" option to tell the nc program that we are using the UDP protocol.
    Alice (111.111.111.111)                         Bob (222.222.222.222)

                                                    (step 1) nc -ulnv 9000

    (step 2) nc -u 222.222.222.222 9000

Activity 1

Bring up two terminals. Have a netcat chat between the two terminals. Try a TCP connection, and then a UDP connection.

TCP server socket with Python: bind, listen, accept


#!/usr/bin/python3
# serv_simple.py
from socket import *

sock = socket()
sock.bind(("0.0.0.0",8000))
sock.listen()
(newsock, addr) = sock.accept()
print("connection from", addr)

data = newsock.recv(1024)
print(data)
s = input()
newsock.send(s.encode())
newsock.close()
For a server socket, you need to do the following.
  1. (socket) Create a socket by calling socket().
  2. (bind) You need to call bind() to bind the socket to a specific port so that the clients can connect the server by using that port.
  3. (listen) Call listen() to look for any connection attempt from a remote client.
  4. (accept) Now, you can call accept() to accept any connection request.
After the connection has been established, one can use send() and recv() to exchange the data. You need to use the data of bytes type.

One can close the socket by calling close().

Type conversion between string objects and bytes objects

Use encode() and decode() to perform to conversion.

>>> s = "hello world"
>>> type(s)
<class 'str'>
>>> b = s.encode()
>>> b
b'hello world'
>>> type(b)
<class 'bytes'>
>>> t = b.decode()
>>> t
'hello world'
>>> type(t)
<class 'str'>
Note: 0.0.0.0 vs 127.0.0.1

A sample run

~$ ./serv_simple.py
connection from ('127.0.0.1', 54214)
b'Hello!\n'
hello back
~$ nc 127.0.0.1 8000
Hello!
hello back

TCP client socket with Python: connecting to a server

Again, the syntax is quite similar to what you do in the C programming language.
  1. (socket) Create a socket by calling socket().
  2. (connect) Connect to a server by calling connect().
Like the server socket, after the connection has been established, one can use send() and recv() to exchange the data. You need to use the data of bytes type.

One can close the socket by calling close().

Note: If it's not a server socket (i.e., if it's a client socket), you don't need to call bind(), in which case the OS will choose a random port for the client socket.

#!/usr/bin/python3
# conn_simple.py

from socket import *

sock = socket()
sock.connect(("127.0.0.1", 8000))
sock.send(b"Hello!\n")
data = sock.recv(1024)
print(data)
A sample run:
~$ nc -l 8000
Hello!
test
~$ ./conn_simple.py
b'test\n'

select()

Now, let's try to write a program that implements a simple 1:1 chatting.

Concurrent tasks --- use select()

For this, we need to perform the following tasks concurrently. For this, we need to use select() function contained in the module select.

stdin as a Socket

As you learned from the systems programming course, Linux treats every I/O as a file each of which the unique file identifier. Similar concepts are applied here -- that is, you can treat sys.stdin as a socket.

Code


#!/usr/bin/python3
# serv_tcp.py

import socket
import sys
import select

sock = socket.socket()
sock.bind(("0.0.0.0",8000))
sock.listen()
(newsock, addr) = sock.accept()
print("connection from", addr)

while True:
  socklist = [newsock, sys.stdin]  ## focus here!!!
  (r_sockets, w_sockets, e_sockets) = select.select(socklist, [], [])

  if newsock in r_sockets:   # we have something to read from new sock!
    data = newsock.recv(1024)
    if not data:  # no data means the connection has been closed.
      break
    print(data)

  if sys.stdin in r_sockets:  # we have something to read from sys.stdin!
    s = input()
    s += "\n"
    newsock.send(s.encode())


newsock.close()
sock.close()
Note:

Test the code

Test this program

Activity 2

Create a UDP netcat program for the server part. To test your program, launch a UDP client as follows:
nc -u 127.0.0.1 9000
Refer to the socket manual page.