Implement both Publisher and Subscriber.
diff --git a/Makefile b/Makefile
index 9031853..6e9f719 100644
--- a/Makefile
+++ b/Makefile
@@ -381,7 +381,8 @@
 interop_client: bins/$(CONFIG)/interop_client
 interop_server: bins/$(CONFIG)/interop_server
 tips_client: bins/$(CONFIG)/tips_client
-tips_client_test: bins/$(CONFIG)/tips_client_test
+tips_publisher_test: bins/$(CONFIG)/tips_publisher_test
+tips_subscriber_test: bins/$(CONFIG)/tips_subscriber_test
 qps_client: bins/$(CONFIG)/qps_client
 qps_server: bins/$(CONFIG)/qps_server
 ruby_plugin: bins/$(CONFIG)/ruby_plugin
@@ -567,7 +568,7 @@
 
 buildtests_c: privatelibs_c bins/$(CONFIG)/alarm_heap_test bins/$(CONFIG)/alarm_list_test bins/$(CONFIG)/alarm_test bins/$(CONFIG)/alpn_test bins/$(CONFIG)/bin_encoder_test bins/$(CONFIG)/census_hash_table_test bins/$(CONFIG)/census_statistics_multiple_writers_circular_buffer_test bins/$(CONFIG)/census_statistics_multiple_writers_test bins/$(CONFIG)/census_statistics_performance_test bins/$(CONFIG)/census_statistics_quick_test bins/$(CONFIG)/census_statistics_small_log_test bins/$(CONFIG)/census_stub_test bins/$(CONFIG)/census_window_stats_test bins/$(CONFIG)/chttp2_status_conversion_test bins/$(CONFIG)/chttp2_stream_encoder_test bins/$(CONFIG)/chttp2_stream_map_test bins/$(CONFIG)/chttp2_transport_end2end_test bins/$(CONFIG)/dualstack_socket_test bins/$(CONFIG)/echo_client bins/$(CONFIG)/echo_server bins/$(CONFIG)/echo_test bins/$(CONFIG)/fd_posix_test bins/$(CONFIG)/fling_client bins/$(CONFIG)/fling_server bins/$(CONFIG)/fling_stream_test bins/$(CONFIG)/fling_test bins/$(CONFIG)/gpr_cancellable_test bins/$(CONFIG)/gpr_cmdline_test bins/$(CONFIG)/gpr_histogram_test bins/$(CONFIG)/gpr_host_port_test bins/$(CONFIG)/gpr_log_test bins/$(CONFIG)/gpr_slice_buffer_test bins/$(CONFIG)/gpr_slice_test bins/$(CONFIG)/gpr_string_test bins/$(CONFIG)/gpr_sync_test bins/$(CONFIG)/gpr_thd_test bins/$(CONFIG)/gpr_time_test bins/$(CONFIG)/gpr_useful_test bins/$(CONFIG)/grpc_base64_test bins/$(CONFIG)/grpc_byte_buffer_reader_test bins/$(CONFIG)/grpc_channel_stack_test bins/$(CONFIG)/grpc_completion_queue_test bins/$(CONFIG)/grpc_credentials_test bins/$(CONFIG)/grpc_json_token_test bins/$(CONFIG)/grpc_stream_op_test bins/$(CONFIG)/hpack_parser_test bins/$(CONFIG)/hpack_table_test bins/$(CONFIG)/httpcli_format_request_test bins/$(CONFIG)/httpcli_parser_test bins/$(CONFIG)/httpcli_test bins/$(CONFIG)/lame_client_test bins/$(CONFIG)/message_compress_test bins/$(CONFIG)/metadata_buffer_test bins/$(CONFIG)/murmur_hash_test bins/$(CONFIG)/no_server_test bins/$(CONFIG)/poll_kick_posix_test bins/$(CONFIG)/resolve_address_test bins/$(CONFIG)/secure_endpoint_test bins/$(CONFIG)/sockaddr_utils_test bins/$(CONFIG)/tcp_client_posix_test bins/$(CONFIG)/tcp_posix_test bins/$(CONFIG)/tcp_server_posix_test bins/$(CONFIG)/time_averaged_stats_test bins/$(CONFIG)/time_test bins/$(CONFIG)/timeout_encoding_test bins/$(CONFIG)/transport_metadata_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fake_security_census_simple_request_test bins/$(CONFIG)/chttp2_fake_security_disappearing_server_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fake_security_invoke_large_request_test bins/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fake_security_no_op_test bins/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test bins/$(CONFIG)/chttp2_fake_security_simple_request_test bins/$(CONFIG)/chttp2_fake_security_thread_stress_test bins/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fullstack_no_op_test bins/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_no_op_test bins/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test
 
-buildtests_cxx: privatelibs_cxx bins/$(CONFIG)/channel_arguments_test bins/$(CONFIG)/credentials_test bins/$(CONFIG)/end2end_test bins/$(CONFIG)/interop_client bins/$(CONFIG)/interop_server bins/$(CONFIG)/tips_client bins/$(CONFIG)/tips_client_test bins/$(CONFIG)/qps_client bins/$(CONFIG)/qps_server bins/$(CONFIG)/status_test bins/$(CONFIG)/sync_client_async_server_test bins/$(CONFIG)/thread_pool_test
+buildtests_cxx: privatelibs_cxx bins/$(CONFIG)/channel_arguments_test bins/$(CONFIG)/credentials_test bins/$(CONFIG)/end2end_test bins/$(CONFIG)/interop_client bins/$(CONFIG)/interop_server bins/$(CONFIG)/tips_client bins/$(CONFIG)/tips_publisher_test bins/$(CONFIG)/tips_subscriber_test bins/$(CONFIG)/qps_client bins/$(CONFIG)/qps_server bins/$(CONFIG)/status_test bins/$(CONFIG)/sync_client_async_server_test bins/$(CONFIG)/thread_pool_test
 
 test: test_c test_cxx
 
@@ -969,8 +970,10 @@
 	$(Q) ./bins/$(CONFIG)/credentials_test || ( echo test credentials_test failed ; exit 1 )
 	$(E) "[RUN]     Testing end2end_test"
 	$(Q) ./bins/$(CONFIG)/end2end_test || ( echo test end2end_test failed ; exit 1 )
-	$(E) "[RUN]     Testing tips_client_test"
-	$(Q) ./bins/$(CONFIG)/tips_client_test || ( echo test tips_client_test failed ; exit 1 )
+	$(E) "[RUN]     Testing tips_publisher_test"
+	$(Q) ./bins/$(CONFIG)/tips_publisher_test || ( echo test tips_publisher_test failed ; exit 1 )
+	$(E) "[RUN]     Testing tips_subscriber_test"
+	$(Q) ./bins/$(CONFIG)/tips_subscriber_test || ( echo test tips_subscriber_test failed ; exit 1 )
 	$(E) "[RUN]     Testing qps_client"
 	$(Q) ./bins/$(CONFIG)/qps_client || ( echo test qps_client failed ; exit 1 )
 	$(E) "[RUN]     Testing qps_server"
@@ -2210,7 +2213,8 @@
     gens/examples/tips/label.pb.cc \
     gens/examples/tips/empty.pb.cc \
     gens/examples/tips/pubsub.pb.cc \
-    examples/tips/client.cc \
+    examples/tips/publisher.cc \
+    examples/tips/subscriber.cc \
 
 
 LIBTIPS_CLIENT_LIB_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBTIPS_CLIENT_LIB_SRC))))
@@ -2228,7 +2232,8 @@
 examples/tips/label.proto: $(OPENSSL_DEP)
 examples/tips/empty.proto: $(OPENSSL_DEP)
 examples/tips/pubsub.proto: $(OPENSSL_DEP)
-examples/tips/client.cc: $(OPENSSL_DEP)
+examples/tips/publisher.cc: $(OPENSSL_DEP)
+examples/tips/subscriber.cc: $(OPENSSL_DEP)
 endif
 
 libs/$(CONFIG)/libtips_client_lib.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBTIPS_CLIENT_LIB_OBJS)
@@ -2255,7 +2260,8 @@
 
 
 
-objs/$(CONFIG)/examples/tips/client.o:     gens/examples/tips/label.pb.cc    gens/examples/tips/empty.pb.cc    gens/examples/tips/pubsub.pb.cc
+objs/$(CONFIG)/examples/tips/publisher.o:     gens/examples/tips/label.pb.cc    gens/examples/tips/empty.pb.cc    gens/examples/tips/pubsub.pb.cc
+objs/$(CONFIG)/examples/tips/subscriber.o:     gens/examples/tips/label.pb.cc    gens/examples/tips/empty.pb.cc    gens/examples/tips/pubsub.pb.cc
 
 
 LIBEND2END_FIXTURE_CHTTP2_FAKE_SECURITY_SRC = \
@@ -5565,7 +5571,7 @@
 
 
 TIPS_CLIENT_SRC = \
-    examples/tips/client_main.cc \
+    examples/tips/main.cc \
 
 TIPS_CLIENT_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_CLIENT_SRC))))
 
@@ -5584,7 +5590,7 @@
 
 endif
 
-objs/$(CONFIG)/examples/tips/client_main.o:  libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+objs/$(CONFIG)/examples/tips/main.o:  libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
 
 deps_tips_client: $(TIPS_CLIENT_OBJS:.o=.dep)
 
@@ -5595,33 +5601,64 @@
 endif
 
 
-TIPS_CLIENT_TEST_SRC = \
-    examples/tips/client_test.cc \
+TIPS_PUBLISHER_TEST_SRC = \
+    examples/tips/publisher_test.cc \
 
-TIPS_CLIENT_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_CLIENT_TEST_SRC))))
+TIPS_PUBLISHER_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_PUBLISHER_TEST_SRC))))
 
 ifeq ($(NO_SECURE),true)
 
 # You can't build secure targets if you don't have OpenSSL with ALPN.
 
-bins/$(CONFIG)/tips_client_test: openssl_dep_error
+bins/$(CONFIG)/tips_publisher_test: openssl_dep_error
 
 else
 
-bins/$(CONFIG)/tips_client_test: $(TIPS_CLIENT_TEST_OBJS) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+bins/$(CONFIG)/tips_publisher_test: $(TIPS_PUBLISHER_TEST_OBJS) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) $(TIPS_CLIENT_TEST_OBJS) $(GTEST_LIB) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/tips_client_test
+	$(Q) $(LDXX) $(LDFLAGS) $(TIPS_PUBLISHER_TEST_OBJS) $(GTEST_LIB) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/tips_publisher_test
 
 endif
 
-objs/$(CONFIG)/examples/tips/client_test.o:  libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+objs/$(CONFIG)/examples/tips/publisher_test.o:  libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
 
-deps_tips_client_test: $(TIPS_CLIENT_TEST_OBJS:.o=.dep)
+deps_tips_publisher_test: $(TIPS_PUBLISHER_TEST_OBJS:.o=.dep)
 
 ifneq ($(NO_SECURE),true)
 ifneq ($(NO_DEPS),true)
--include $(TIPS_CLIENT_TEST_OBJS:.o=.dep)
+-include $(TIPS_PUBLISHER_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+TIPS_SUBSCRIBER_TEST_SRC = \
+    examples/tips/subscriber_test.cc \
+
+TIPS_SUBSCRIBER_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_SUBSCRIBER_TEST_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+bins/$(CONFIG)/tips_subscriber_test: openssl_dep_error
+
+else
+
+bins/$(CONFIG)/tips_subscriber_test: $(TIPS_SUBSCRIBER_TEST_OBJS) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(TIPS_SUBSCRIBER_TEST_OBJS) $(GTEST_LIB) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/tips_subscriber_test
+
+endif
+
+objs/$(CONFIG)/examples/tips/subscriber_test.o:  libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+
+deps_tips_subscriber_test: $(TIPS_SUBSCRIBER_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(TIPS_SUBSCRIBER_TEST_OBJS:.o=.dep)
 endif
 endif
 
diff --git a/build.json b/build.json
index bf780b7..625d577 100644
--- a/build.json
+++ b/build.json
@@ -426,7 +426,8 @@
         "examples/tips/label.proto",
         "examples/tips/empty.proto",
         "examples/tips/pubsub.proto",
-        "examples/tips/client.cc"
+        "examples/tips/publisher.cc",
+        "examples/tips/subscriber.cc"
       ],
       "deps": [
         "grpc++",
@@ -1524,7 +1525,7 @@
       "run": false,
       "language": "c++",
       "src": [
-        "examples/tips/client_main.cc"
+        "examples/tips/main.cc"
       ],
       "deps": [
         "tips_client_lib",
@@ -1537,11 +1538,28 @@
       ]
     },
     {
-      "name": "tips_client_test",
+      "name": "tips_publisher_test",
       "build": "test",
       "language": "c++",
       "src": [
-        "examples/tips/client_test.cc"
+        "examples/tips/publisher_test.cc"
+      ],
+      "deps": [
+        "tips_client_lib",
+        "grpc++_test_util",
+        "grpc_test_util",
+        "grpc++",
+        "grpc",
+        "gpr_test_util",
+        "gpr"
+      ]
+    },
+    {
+      "name": "tips_subscriber_test",
+      "build": "test",
+      "language": "c++",
+      "src": [
+        "examples/tips/subscriber_test.cc"
       ],
       "deps": [
         "tips_client_lib",
diff --git a/examples/tips/client_main.cc b/examples/tips/main.cc
similarity index 71%
rename from examples/tips/client_main.cc
rename to examples/tips/main.cc
index 5a3a0da..94a0bc7 100644
--- a/examples/tips/client_main.cc
+++ b/examples/tips/main.cc
@@ -46,7 +46,8 @@
 #include <grpc++/credentials.h>
 #include <grpc++/status.h>
 
-#include "examples/tips/client.h"
+#include "examples/tips/publisher.h"
+#include "examples/tips/subscriber.h"
 #include "test/cpp/util/create_test_channel.h"
 
 DEFINE_int32(server_port, 443, "Server port.");
@@ -54,7 +55,17 @@
               "pubsub-staging.googleapis.com", "Server host to connect to");
 DEFINE_string(service_account_key_file, "",
               "Path to service account json key file.");
-DEFINE_string(oauth_scope, "", "Scope for OAuth tokens.");
+DEFINE_string(oauth_scope,
+              "https://www.googleapis.com/auth/cloud-platform",
+              "Scope for OAuth tokens.");
+
+namespace {
+
+const char kTopic[] = "/topics/stoked-keyword-656/testtopics";
+const char kSubscriptionName[] = "stoked-keyword-656/testsubscription";
+const char kMessageData[] = "Message Data";
+
+}  // namespace
 
 grpc::string GetServiceAccountJsonKey() {
   static grpc::string json_key;
@@ -94,20 +105,40 @@
           true,                // use prod roots
           creds));
 
-  grpc::examples::tips::Client client(channel);
+  grpc::examples::tips::Publisher publisher(channel);
+  grpc::examples::tips::Subscriber subscriber(channel);
 
-  grpc::Status s = client.CreateTopic("/topics/stoked-keyword-656/testtopics");
-  gpr_log(GPR_INFO, "return code %d, %s", s.code(), s.details().c_str());
+  grpc::string topic = kTopic;
+
+  if (publisher.GetTopic(topic).IsOk()) publisher.DeleteTopic(topic);
+
+  grpc::Status s = publisher.CreateTopic(topic);
+  gpr_log(GPR_INFO, "Create topic returns code %d, %s",
+          s.code(), s.details().c_str());
   GPR_ASSERT(s.IsOk());
 
-  s = client.GetTopic("/topics/stoked-keyword-656/testtopics");
-  gpr_log(GPR_INFO, "return code %d, %s", s.code(), s.details().c_str());
+  s = publisher.GetTopic(topic);
+  gpr_log(GPR_INFO, "Get topic returns code %d, %s",
+          s.code(), s.details().c_str());
   GPR_ASSERT(s.IsOk());
 
-  s = client.DeleteTopic("/topics/stoked-keyword-656/testtopics");
-  gpr_log(GPR_INFO, "return code %d, %s", s.code(), s.details().c_str());
+  s = publisher.Publish(topic, kMessageData);
+  gpr_log(GPR_INFO, "Publish returns code %d, %s",
+          s.code(), s.details().c_str());
   GPR_ASSERT(s.IsOk());
 
+  s = subscriber.CreateSubscription(kTopic, kSubscriptionName);
+  gpr_log(GPR_INFO, "create subscrption returns code %d, %s",
+          s.code(), s.details().c_str());
+  GPR_ASSERT(s.IsOk());
+
+  s = publisher.DeleteTopic(kTopic);
+  gpr_log(GPR_INFO, "Delete topic returns code %d, %s",
+          s.code(), s.details().c_str());
+  GPR_ASSERT(s.IsOk());
+
+  subscriber.Shutdown();
+  publisher.Shutdown();
   channel.reset();
   grpc_shutdown();
   return 0;
diff --git a/examples/tips/client.cc b/examples/tips/publisher.cc
similarity index 78%
rename from examples/tips/client.cc
rename to examples/tips/publisher.cc
index f9d5319..238ea59 100644
--- a/examples/tips/client.cc
+++ b/examples/tips/publisher.cc
@@ -33,7 +33,7 @@
 
 #include <grpc++/client_context.h>
 
-#include "examples/tips/client.h"
+#include "examples/tips/publisher.h"
 
 using tech::pubsub::Topic;
 using tech::pubsub::DeleteTopicRequest;
@@ -41,16 +41,22 @@
 using tech::pubsub::PublisherService;
 using tech::pubsub::ListTopicsRequest;
 using tech::pubsub::ListTopicsResponse;
+using tech::pubsub::PublishRequest;
+using tech::pubsub::PubsubMessage;
 
 namespace grpc {
 namespace examples {
 namespace tips {
 
-Client::Client(std::shared_ptr<ChannelInterface> channel)
+Publisher::Publisher(std::shared_ptr<ChannelInterface> channel)
     : stub_(PublisherService::NewStub(channel)) {
 }
 
-Status Client::CreateTopic(grpc::string topic) {
+void Publisher::Shutdown() {
+  stub_.reset();
+}
+
+Status Publisher::CreateTopic(grpc::string topic) {
   Topic request;
   Topic response;
   request.set_name(topic);
@@ -59,7 +65,7 @@
   return stub_->CreateTopic(&context, request, &response);
 }
 
-Status Client::ListTopics() {
+Status Publisher::ListTopics() {
   ListTopicsRequest request;
   ListTopicsResponse response;
   ClientContext context;
@@ -67,7 +73,7 @@
   return stub_->ListTopics(&context, request, &response);
 }
 
-Status Client::GetTopic(grpc::string topic) {
+Status Publisher::GetTopic(grpc::string topic) {
   GetTopicRequest request;
   Topic response;
   ClientContext context;
@@ -77,7 +83,7 @@
   return stub_->GetTopic(&context, request, &response);
 }
 
-Status Client::DeleteTopic(grpc::string topic) {
+Status Publisher::DeleteTopic(grpc::string topic) {
   DeleteTopicRequest request;
   proto2::Empty response;
   ClientContext context;
@@ -87,6 +93,18 @@
   return stub_->DeleteTopic(&context, request, &response);
 }
 
+Status Publisher::Publish(const grpc::string& topic,
+                          const grpc::string& data) {
+  PublishRequest request;
+  proto2::Empty response;
+  ClientContext context;
+
+  request.mutable_message()->set_data(data);
+  request.set_topic(topic);
+
+  return stub_->Publish(&context, request, &response);
+}
+
 }  // namespace tips
 }  // namespace examples
 }  // namespace grpc
diff --git a/examples/tips/client.h b/examples/tips/publisher.h
similarity index 86%
rename from examples/tips/client.h
rename to examples/tips/publisher.h
index 661ee5c..a97dbe6 100644
--- a/examples/tips/client.h
+++ b/examples/tips/publisher.h
@@ -31,8 +31,8 @@
  *
  */
 
-#ifndef __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
-#define __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
+#ifndef __GRPCPP_EXAMPLES_TIPS_PUBLISHER_H_
+#define __GRPCPP_EXAMPLES_TIPS_PUBLISHER_H_
 
 #include <grpc++/channel_interface.h>
 #include <grpc++/status.h>
@@ -43,14 +43,18 @@
 namespace examples {
 namespace tips {
 
-class Client {
+class Publisher {
  public:
-  Client(std::shared_ptr<grpc::ChannelInterface> channel);
+  Publisher(std::shared_ptr<grpc::ChannelInterface> channel);
+  void Shutdown();
+
   Status CreateTopic(grpc::string topic);
   Status GetTopic(grpc::string topic);
   Status DeleteTopic(grpc::string topic);
   Status ListTopics();
 
+  Status Publish(const grpc::string& topic, const grpc::string& data);
+
  private:
   std::unique_ptr<tech::pubsub::PublisherService::Stub> stub_;
 };
@@ -59,4 +63,4 @@
 }  // namespace examples
 }  // namespace grpc
 
-#endif  // __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
+#endif  // __GRPCPP_EXAMPLES_TIPS_PUBLISHER_H_
diff --git a/examples/tips/client_test.cc b/examples/tips/publisher_test.cc
similarity index 64%
copy from examples/tips/client_test.cc
copy to examples/tips/publisher_test.cc
index 69238f2..7f845fe 100644
--- a/examples/tips/client_test.cc
+++ b/examples/tips/publisher_test.cc
@@ -41,7 +41,7 @@
 #include <grpc++/status.h>
 #include <gtest/gtest.h>
 
-#include "examples/tips/client.h"
+#include "examples/tips/publisher.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
 
@@ -52,8 +52,9 @@
 namespace {
 
 const char kTopic[] = "test topic";
+const char kMessageData[] = "test message data";
 
-class PublishServiceImpl : public tech::pubsub::PublisherService::Service {
+class PublisherServiceImpl : public tech::pubsub::PublisherService::Service {
  public:
   Status CreateTopic(::grpc::ServerContext* context,
                      const ::tech::pubsub::Topic* request,
@@ -61,34 +62,71 @@
     EXPECT_EQ(request->name(), kTopic);
     return Status::OK;
   }
+
+  Status Publish(ServerContext* context,
+                 const ::tech::pubsub::PublishRequest* request,
+                 ::proto2::Empty* response) override {
+    EXPECT_EQ(request->message().data(), kMessageData);
+    return Status::OK;
+  }
+
+  Status GetTopic(ServerContext* context,
+                  const ::tech::pubsub::GetTopicRequest* request,
+                  ::tech::pubsub::Topic* response) override {
+    EXPECT_EQ(request->topic(), kTopic);
+    return Status::OK;
+  }
+
+ Status ListTopics(ServerContext* context,
+                   const ::tech::pubsub::ListTopicsRequest* request,
+                   ::tech::pubsub::ListTopicsResponse* response) override {
+    return Status::OK;
+ }
+
+ Status DeleteTopic(ServerContext* context,
+                    const ::tech::pubsub::DeleteTopicRequest* request,
+                    ::proto2::Empty* response) override {
+    EXPECT_EQ(request->topic(), kTopic);
+    return Status::OK;
+ }
+
 };
 
-class End2endTest : public ::testing::Test {
+class PublisherTest : public ::testing::Test {
  protected:
+  // Setup a server and a client for PublisherService.
   void SetUp() override {
     int port = grpc_pick_unused_port_or_die();
     server_address_ << "localhost:" << port;
-    // Setup server
     ServerBuilder builder;
     builder.AddPort(server_address_.str());
     builder.RegisterService(service_.service());
     server_ = builder.BuildAndStart();
 
     channel_ = CreateChannel(server_address_.str(), ChannelArguments());
+
+    publisher_.reset(new grpc::examples::tips::Publisher(channel_));
   }
 
-  void TearDown() override { server_->Shutdown(); }
+  void TearDown() override {
+    server_->Shutdown();
+    publisher_->Shutdown();
+  }
 
-  std::unique_ptr<Server> server_;
   std::ostringstream server_address_;
-  PublishServiceImpl service_;
+  std::unique_ptr<Server> server_;
+  PublisherServiceImpl service_;
 
   std::shared_ptr<ChannelInterface> channel_;
+
+  std::unique_ptr<grpc::examples::tips::Publisher> publisher_;
 };
 
-TEST_F(End2endTest, CreateTopic) {
-  grpc::examples::tips::Client client(channel_);
-  client.CreateTopic(kTopic);
+TEST_F(PublisherTest, TestPublisher) {
+  EXPECT_TRUE(publisher_->CreateTopic(kTopic).IsOk());
+  EXPECT_TRUE(publisher_->Publish(kTopic, kMessageData).IsOk());
+  EXPECT_TRUE(publisher_->GetTopic(kTopic).IsOk());
+  EXPECT_TRUE(publisher_->ListTopics().IsOk());
 }
 
 }  // namespace
diff --git a/examples/tips/client.cc b/examples/tips/subscriber.cc
similarity index 67%
copy from examples/tips/client.cc
copy to examples/tips/subscriber.cc
index f9d5319..a482ad6 100644
--- a/examples/tips/client.cc
+++ b/examples/tips/subscriber.cc
@@ -33,58 +33,52 @@
 
 #include <grpc++/client_context.h>
 
-#include "examples/tips/client.h"
+#include "examples/tips/subscriber.h"
 
 using tech::pubsub::Topic;
 using tech::pubsub::DeleteTopicRequest;
 using tech::pubsub::GetTopicRequest;
-using tech::pubsub::PublisherService;
+using tech::pubsub::SubscriberService;
 using tech::pubsub::ListTopicsRequest;
 using tech::pubsub::ListTopicsResponse;
+using tech::pubsub::PublishRequest;
+using tech::pubsub::PubsubMessage;
 
 namespace grpc {
 namespace examples {
 namespace tips {
 
-Client::Client(std::shared_ptr<ChannelInterface> channel)
-    : stub_(PublisherService::NewStub(channel)) {
+Subscriber::Subscriber(std::shared_ptr<ChannelInterface> channel)
+    : stub_(SubscriberService::NewStub(channel)) {
 }
 
-Status Client::CreateTopic(grpc::string topic) {
-  Topic request;
-  Topic response;
-  request.set_name(topic);
-  ClientContext context;
-
-  return stub_->CreateTopic(&context, request, &response);
+void Subscriber::Shutdown() {
+  stub_.reset();
 }
 
-Status Client::ListTopics() {
-  ListTopicsRequest request;
-  ListTopicsResponse response;
-  ClientContext context;
-
-  return stub_->ListTopics(&context, request, &response);
-}
-
-Status Client::GetTopic(grpc::string topic) {
-  GetTopicRequest request;
-  Topic response;
+Status Subscriber::CreateSubscription(const grpc::string& topic,
+                          const grpc::string& name) {
+  tech::pubsub::Subscription request;
+  tech::pubsub::Subscription response;
   ClientContext context;
 
   request.set_topic(topic);
+  request.set_name(name);
 
-  return stub_->GetTopic(&context, request, &response);
+  return stub_->CreateSubscription(&context, request, &response);
 }
 
-Status Client::DeleteTopic(grpc::string topic) {
-  DeleteTopicRequest request;
-  proto2::Empty response;
+Status Subscriber::GetSubscription(const grpc::string& name,
+                                   grpc::string* topic) {
+  tech::pubsub::GetSubscriptionRequest request;
+  tech::pubsub::Subscription response;
   ClientContext context;
 
-  request.set_topic(topic);
+  request.set_subscription(name);
 
-  return stub_->DeleteTopic(&context, request, &response);
+  Status s = stub_->GetSubscription(&context, request, &response);
+  *topic = response.topic();
+  return s;
 }
 
 }  // namespace tips
diff --git a/examples/tips/client.h b/examples/tips/subscriber.h
similarity index 78%
copy from examples/tips/client.h
copy to examples/tips/subscriber.h
index 661ee5c..e0491ff 100644
--- a/examples/tips/client.h
+++ b/examples/tips/subscriber.h
@@ -31,8 +31,8 @@
  *
  */
 
-#ifndef __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
-#define __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
+#ifndef __GRPCPP_EXAMPLES_TIPS_SUBSCRIBER_H_
+#define __GRPCPP_EXAMPLES_TIPS_SUBSCRIBER_H_
 
 #include <grpc++/channel_interface.h>
 #include <grpc++/status.h>
@@ -43,20 +43,22 @@
 namespace examples {
 namespace tips {
 
-class Client {
+class Subscriber {
  public:
-  Client(std::shared_ptr<grpc::ChannelInterface> channel);
-  Status CreateTopic(grpc::string topic);
-  Status GetTopic(grpc::string topic);
-  Status DeleteTopic(grpc::string topic);
-  Status ListTopics();
+  Subscriber(std::shared_ptr<grpc::ChannelInterface> channel);
+  void Shutdown();
+
+  Status CreateSubscription(const grpc::string& topic,
+                            const grpc::string& name);
+
+  Status GetSubscription(const grpc::string& name, grpc::string* topic);
 
  private:
-  std::unique_ptr<tech::pubsub::PublisherService::Stub> stub_;
+  std::unique_ptr<tech::pubsub::SubscriberService::Stub> stub_;
 };
 
 }  // namespace tips
 }  // namespace examples
 }  // namespace grpc
 
-#endif  // __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
+#endif  // __GRPCPP_EXAMPLES_TIPS_SUBSCRIBER_H_
diff --git a/examples/tips/client_test.cc b/examples/tips/subscriber_test.cc
similarity index 66%
rename from examples/tips/client_test.cc
rename to examples/tips/subscriber_test.cc
index 69238f2..4894814 100644
--- a/examples/tips/client_test.cc
+++ b/examples/tips/subscriber_test.cc
@@ -41,7 +41,7 @@
 #include <grpc++/status.h>
 #include <gtest/gtest.h>
 
-#include "examples/tips/client.h"
+#include "examples/tips/subscriber.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
 
@@ -52,43 +52,66 @@
 namespace {
 
 const char kTopic[] = "test topic";
+const char kSubscriptionName[] = "subscription name";
 
-class PublishServiceImpl : public tech::pubsub::PublisherService::Service {
+class SubscriberServiceImpl : public tech::pubsub::SubscriberService::Service {
  public:
-  Status CreateTopic(::grpc::ServerContext* context,
-                     const ::tech::pubsub::Topic* request,
-                     ::tech::pubsub::Topic* response) override {
-    EXPECT_EQ(request->name(), kTopic);
+  Status CreateSubscription(ServerContext* context,
+                            const tech::pubsub::Subscription* request,
+                            tech::pubsub::Subscription* response) override {
+    EXPECT_EQ(request->topic(), kTopic);
+    EXPECT_EQ(request->name(), kSubscriptionName);
     return Status::OK;
   }
+
+  Status GetSubscription(ServerContext* context,
+                         const tech::pubsub::GetSubscriptionRequest* request,
+                         tech::pubsub::Subscription* response) override {
+    EXPECT_EQ(request->subscription(), kSubscriptionName);
+    response->set_topic(kTopic);
+    return Status::OK;
+  }
+
 };
 
-class End2endTest : public ::testing::Test {
+class SubscriberTest : public ::testing::Test {
  protected:
+  // Setup a server and a client for SubscriberService.
   void SetUp() override {
     int port = grpc_pick_unused_port_or_die();
     server_address_ << "localhost:" << port;
-    // Setup server
     ServerBuilder builder;
     builder.AddPort(server_address_.str());
     builder.RegisterService(service_.service());
     server_ = builder.BuildAndStart();
 
     channel_ = CreateChannel(server_address_.str(), ChannelArguments());
+
+    subscriber_.reset(new grpc::examples::tips::Subscriber(channel_));
   }
 
-  void TearDown() override { server_->Shutdown(); }
+  void TearDown() override {
+    server_->Shutdown();
+    subscriber_->Shutdown();
+  }
 
-  std::unique_ptr<Server> server_;
   std::ostringstream server_address_;
-  PublishServiceImpl service_;
+  std::unique_ptr<Server> server_;
+  SubscriberServiceImpl service_;
 
   std::shared_ptr<ChannelInterface> channel_;
+
+  std::unique_ptr<grpc::examples::tips::Subscriber> subscriber_;
 };
 
-TEST_F(End2endTest, CreateTopic) {
-  grpc::examples::tips::Client client(channel_);
-  client.CreateTopic(kTopic);
+TEST_F(SubscriberTest, TestSubscriber) {
+  EXPECT_TRUE(subscriber_->CreateSubscription(kTopic,
+                                              kSubscriptionName).IsOk());
+
+  grpc::string topic;
+  EXPECT_TRUE(subscriber_->GetSubscription(kSubscriptionName,
+                                           &topic).IsOk());
+  EXPECT_EQ(topic, kTopic);
 }
 
 }  // namespace
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index a610e92..8dde2ae 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -263,7 +263,11 @@
   }, 
   {
     "language": "c++", 
-    "name": "tips_client_test"
+    "name": "tips_publisher_test"
+  }, 
+  {
+    "language": "c++", 
+    "name": "tips_subscriber_test"
   }, 
   {
     "language": "c++",