慶應義塾大学
2007年度 秋学期

ネットワーク・プログラミング(C言語)
Network Programming in C

2007年度秋学期 火曜日2時限
科目コード: 13070 / 2単位
カテゴリ:
開講場所:SFC
授業形態:講義
担当: Rodney Van Meter
E-mail: rdv@sfc.keio.ac.jp

第5回 11月6日
Lecture 5, November 6: Basic socket programming 2

Outline of This Lecture

Last Week's Homework

I forgot to point out last week that using Wireshark or tcpdump requires privileges on a Unix system. If you are doing your programming using your CNS account, you won't be able to use it there. Unfortunately, if you do your programming on your CNS account and install Wireshark on your laptop, you won't be able to see the packets.

Using fork() on Windows boxes is difficult; as with the sockets code, it really requires cygwin.

make

I've noticed that some of you are still compiling each program by hand. You should be using a simple Makefile and the command make if you are working on a Unix-like system (including cygwin).

Below is the Makefile that I am currently using for the programs for this class. You should make one like it, replacing the program names with your own program names for the four programs.

CFLAGS=-g

TARGETS=client-rdv server-wiki-rdv server-udp-rdv client-udp-rdv \
			client-udp-wiki

all:			${TARGETS}

server-wiki-rdv:	server-wiki-rdv.c

client-rdv:		client-rdv.c

server-udp-rdv:		server-udp-rdv.c

client-udp-rdv:		client-udp-rdv.c

client-udp-wiki:	client-udp-wiki.c

clean:
	rm ${TARGETS}

TCP Client Example Code

int get_stream(char *host, char *service)
{
  int error, fd;
  struct addrinfo req, *ai, *ai2, hints;
  char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
  memset((void *)&req, 0, sizeof(struct addrinfo));
  memset((void *)&hints, 0, sizeof(struct addrinfo));
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_family |= PF_UNSPEC;
  // hints.ai_flags |= AI_NUMERICSERV;

  if (error = getaddrinfo(host, service, &hints, &ai)) {
    fprintf(stderr, "getaddrinfo(%s, %s, ...): %s(%d)", gai_strerror(error),
	    error);
    return -1;
  }
  for (ai2 = ai; ai; ai = ai->ai_next) {
    if (error = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf),
			    sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) {
      fprintf(stderr, "getnameinfo(%p, %d, %p, %d, %p, %d, %d): %s(%d)\n",
	      ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
	      NI_NUMERICHOST | NI_NUMERICSERV, gai_strerror(error), error);
      continue;
    }
    fprintf(stdout, "Trying %s port %s...\n", hbuf, sbuf);
    if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
      fprintf(stderr, "socket(%d, %d, %d): ", ai->ai_family,
	      ai->ai_socktype, ai->ai_protocol);
      perror("get_stream");
      continue;
    }
    if (connect(fd, ai->ai_addr, ai->ai_addrlen) < 0) {
      fprintf(stderr, "connect(%d, %p, %d): \n", fd, ai->ai_addr,
	      ai->ai_addrlen);
      perror("get_stream");
      close(fd);
      continue;
    }
    freeaddrinfo(ai2);
    return fd;
  }
  freeaddrinfo(ai2);
  fprintf(stderr, "No connections result.\n");
  return -1;
}

UDP Server

// Server code in C
// adapted from http://en.wikipedia.org/wiki/Berkeley_sockets
 
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <signal.h>

#define MAXMSGSIZE 500		/* arbitrary, biggest we will use */

int main()
{
  struct addrinfo hint;
  struct sockaddr_storage bindaddr_storage;
  struct sockaddr_in6 *bindaddr6 = (struct sockaddr_in6 *)&bindaddr_storage;
  struct sockaddr_in *bindaddr = (struct sockaddr_in *)&bindaddr_storage;
  struct addrinfo *result;
  struct sockaddr_storage mysockname;
  int mysocknamesize;
  struct sockaddr_storage incoming;
  int incomingsize;
  struct sockaddr_storage peer;
  int peersize;
  char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
  int retval;

  int i32SocketFD = socket(PF_INET6, SOCK_DGRAM, 0);
 
  char msg[] = "Successful message from server to client!\n";

  if(-1 == i32SocketFD)
    {
      perror("can not create socket");
      exit(-1);
    }
  
  bzero(&bindaddr_storage, sizeof(bindaddr_storage));
  bindaddr6->sin6_family = AF_INET6;
  bindaddr6->sin6_addr = in6addr_any;	/* struct assignment */
  bindaddr6->sin6_port = htons(12345);
 
  if(-1 == bind(i32SocketFD,(struct sockaddr*) bindaddr6, sizeof(struct addrinfo)))
    {
      printf("error bind failed");
      perror("main");
      exit(-1);
    }
 
  // Report on where we're waiting for a connection
  mysocknamesize = sizeof(mysockname);
  if (retval = getsockname(i32SocketFD, (struct sockaddr *)&mysockname, &mysocknamesize) != 0) {
    printf("getsockname retval: %d (%s)\n",retval, gai_strerror(retval));
  } else {
    if (retval = getnameinfo((struct sockaddr *)&mysockname,
        mysocknamesize,hbuf,sizeof(hbuf),sbuf,sizeof(sbuf),
        NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
      printf("retval: %d (%s)\n",retval, gai_strerror(retval));
    } else
      printf("My sockname host=%s, serv=%s\n", hbuf, sbuf);
  }


  /* set up to ignore child signals */
  // this is to prevent child processes from becoming "zombies"
  // XXX n.b.: not all Unixes behave the same way!  This works on Linux.
  signal(SIGCHLD,SIG_IGN);

  for(; ;)
    {
      pid_t pid;
      char msgin[MAXMSGSIZE];
      int recvd;
 
      incomingsize = sizeof(incoming);
      printf("waiting for packet...\n"); fflush(stdout);
      if ((recvd = recvfrom(i32SocketFD, (void *)msgin, MAXMSGSIZE, 
           0, (struct sockaddr *)&incoming, (socklen_t *)&incomingsize)) == -1)
	{
	  printf("error recvfrom failed");
	  perror("main");
	  exit(-1);
	}
      printf("received msg: |%s|\n",msgin);

      // Find out who we're talking to
      if (retval = getnameinfo((struct sockaddr *)&incoming,incomingsize,
         hbuf,sizeof(hbuf),sbuf,sizeof(sbuf),NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
	printf("getnameinfo failed, retval: %d (%s)\n",retval, gai_strerror(retval));
      } else {
	printf("remote host=%s, serv=%s\n", hbuf, sbuf);
      }

      // perform any calculations necessary to create the result to send back...

      // then send it back.
      printf("Sending the message |%s|\n",msg);
      sendto(i32SocketFD, msg, sizeof(msg), 0, (struct sockaddr *)&incoming,incomingsize);
    }
  exit(0);
}

UDP Client

// UDP Client code in C
// adapted from http://en.wikipedia.org/wiki/Berkeley_sockets

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>//for close() for socket
 
int main(int argc, char *argv[])
{
  int sock;
  struct sockaddr_in sa;
  int bytes_sent, buffer_length;
  char buffer[200];
 
  sprintf(buffer, "Hello World!");
  buffer_length = strlen(buffer) + 1;
 
  sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
  if(-1 == sock)//if socket failed to initialize, exit
    {
      printf("Error Creating Socket");
      return 0;
    }
 
  sa.sin_family = AF_INET;
  sa.sin_addr.s_addr = htonl(0x7F000001);
  sa.sin_port = htons(12345);
 
  bytes_sent = sendto(sock, buffer, buffer_length, 0,(struct sockaddr*) &sa, sizeof(struct sockaddr_in) );
  if(bytes_sent < 0)
    printf("Error sending packet: %s\n", strerror(errno) );
 
  close(sock);//close the socket
  return 0;
}

宿題
Homework

This week's homework (submit via email):

  1. If you are using a Unix-like environment (including cygwin), and you are not yet using make, create a Makefile like the one above for your programs.
  2. Add a main() to the TCP client example above, and test it with your TCP server.
  3. The UDP client above uses the old-style approach to sockets (AF_INET, htonl(), htons()). Adapt it to use getnameinfo(), as in the TCP client example. This should allow it to operate with IPv6. Don't forget to loop over the possibly multiple return values from getnameinfo().
  4. The UDP client doesn't wait for a response from the server. Adapt it to use recvfrom() to receive a packet from the server, and print out what it gets.

Next Lecture

第6回 11月13日
Lecture 6, November 13: Naming: DNS requests

Additional Information

その他