add ws_chat and ws_connect sub commands to ws
This commit is contained in:
		| @@ -115,4 +115,3 @@ set( IXWEBSOCKET_INCLUDE_DIRS | ||||
| target_include_directories( ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS} ) | ||||
|  | ||||
| add_subdirectory(ws) | ||||
| add_subdirectory(examples) | ||||
|   | ||||
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							| @@ -15,7 +15,7 @@ communication channels over a single TCP connection. *IXWebSocket* is a C++ libr | ||||
|  | ||||
| ## Examples | ||||
|  | ||||
| The examples folder countains a simple chat program, using a node.js broadcast server. | ||||
| The ws folder countains many interactive programs for chat and file transfers demonstrating client and server usage. | ||||
|  | ||||
| Here is what the client API looks like. | ||||
|  | ||||
| @@ -144,16 +144,6 @@ Large frames are broken up into smaller chunks or messages to avoid filling up t | ||||
| * Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that. | ||||
| * The server code is using select to detect incoming data, and creates one OS thread per connection. This isn't as scalable as strategies using epoll or kqueue. | ||||
|  | ||||
| ## Examples | ||||
|  | ||||
| 1. Bring up a terminal and jump to the examples folder. | ||||
| 2. Compile the example C++ code. `sh build.sh` | ||||
| 3. Install node.js from [here](https://nodejs.org/en/download/). | ||||
| 4. Type `npm install` to install the node.js dependencies. Then `node broadcast-server.js` to run the server. | ||||
| 5. Bring up a second terminal. `./cmd_websocket_chat bob` | ||||
| 6. Bring up a third terminal. `./cmd_websocket_chat bill` | ||||
| 7. Start typing things in any of those terminals. Hopefully you should see your message being received on the other end. | ||||
|  | ||||
| ## C++ code organization | ||||
|  | ||||
| Here's a simplistic diagram which explains how the code is structured in term of class/modules. | ||||
|   | ||||
							
								
								
									
										4
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								makefile
									
									
									
									
									
								
							| @@ -1,10 +1,10 @@ | ||||
| # | ||||
| # This makefile is just used to easily work with docker (linux build) | ||||
| # | ||||
| all: run | ||||
| all: brew | ||||
|  | ||||
| brew: | ||||
| 	mkdir -p ws/build && (cd ws/build ; cmake .. ; make) | ||||
| 	mkdir -p build && (cd build ; cmake .. ; make) | ||||
|  | ||||
| .PHONY: docker | ||||
| docker: | ||||
|   | ||||
| @@ -14,6 +14,7 @@ set (CMAKE_CXX_STANDARD 14) | ||||
| option(USE_TLS "Add TLS support" ON) | ||||
|  | ||||
| include_directories(ws .) | ||||
| include_directories(ws ..) | ||||
| include_directories(ws ../third_party) | ||||
|  | ||||
| add_executable(ws  | ||||
| @@ -22,6 +23,8 @@ add_executable(ws | ||||
|   ixcrypto/IXHash.cpp | ||||
|   ixcrypto/IXUuid.cpp | ||||
|  | ||||
|   ws_chat.cpp | ||||
|   ws_connect.cpp | ||||
|   ws_transfer.cpp | ||||
|   ws_send.cpp | ||||
|   ws_receive.cpp | ||||
|   | ||||
| @@ -28,6 +28,8 @@ g++ --std=c++14 \ | ||||
|     ixcrypto/IXBase64.cpp \ | ||||
|     ixcrypto/IXHash.cpp \ | ||||
|     ixcrypto/IXUuid.cpp \ | ||||
|     ws_chat.cpp \ | ||||
|     ws_connect.cpp \ | ||||
|     ws_transfer.cpp \ | ||||
|     ws_send.cpp \ | ||||
|     ws_receive.cpp \ | ||||
|   | ||||
							
								
								
									
										27
									
								
								ws/ws.cpp
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								ws/ws.cpp
									
									
									
									
									
								
							| @@ -16,13 +16,18 @@ | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     int ws_chat_main(const std::string& url, | ||||
|                      const std::string& user); | ||||
|  | ||||
|     int ws_connect_main(const std::string& url); | ||||
|  | ||||
|     int ws_receive_main(const std::string& url, | ||||
|                         bool enablePerMessageDeflate); | ||||
|  | ||||
|     extern int ws_transfer_main(int port); | ||||
|     int ws_transfer_main(int port); | ||||
|  | ||||
|     extern int ws_send_main(const std::string& url, | ||||
|                             const std::string& path); | ||||
|     int ws_send_main(const std::string& url, | ||||
|                      const std::string& path); | ||||
| } | ||||
|  | ||||
| int main(int argc, char** argv) | ||||
| @@ -32,6 +37,7 @@ int main(int argc, char** argv) | ||||
|  | ||||
|     std::string url; | ||||
|     std::string path; | ||||
|     std::string user; | ||||
|     int port = 8080; | ||||
|  | ||||
|     CLI::App* sendApp = app.add_subcommand("send", "Send a file"); | ||||
| @@ -44,6 +50,13 @@ int main(int argc, char** argv) | ||||
|     CLI::App* transferApp = app.add_subcommand("transfer", "Broadcasting server"); | ||||
|     transferApp->add_option("--port", port, "Connection url"); | ||||
|  | ||||
|     CLI::App* connectApp = app.add_subcommand("connect", "Connect to a remote server"); | ||||
|     connectApp->add_option("url", url, "Connection url")->required(); | ||||
|  | ||||
|     CLI::App* chatApp = app.add_subcommand("chat", "Group chat"); | ||||
|     chatApp->add_option("url", url, "Connection url")->required(); | ||||
|     chatApp->add_option("user", user, "User name")->required(); | ||||
|  | ||||
|     CLI11_PARSE(app, argc, argv); | ||||
|  | ||||
|     if (app.got_subcommand("transfer")) | ||||
| @@ -59,6 +72,14 @@ int main(int argc, char** argv) | ||||
|         bool enablePerMessageDeflate = false; | ||||
|         return ix::ws_receive_main(url, enablePerMessageDeflate); | ||||
|     } | ||||
|     else if (app.got_subcommand("connect")) | ||||
|     { | ||||
|         return ix::ws_connect_main(url); | ||||
|     } | ||||
|     else if (app.got_subcommand("chat")) | ||||
|     { | ||||
|         return ix::ws_chat_main(url, user); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         assert(false); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| /*
 | ||||
|  *  cmd_websocket_chat.cpp | ||||
|  *  ws_chat.cpp | ||||
|  *  Author: Benjamin Sergeant | ||||
|  *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. | ||||
|  *  Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| //
 | ||||
| @@ -20,19 +20,13 @@ | ||||
| // for convenience
 | ||||
| using json = nlohmann::json; | ||||
| 
 | ||||
| using namespace ix; | ||||
| 
 | ||||
| namespace | ||||
| namespace ix | ||||
| { | ||||
|     void log(const std::string& msg) | ||||
|     { | ||||
|         std::cout << msg << std::endl; | ||||
|     } | ||||
| 
 | ||||
|     class WebSocketChat | ||||
|     { | ||||
|         public: | ||||
|             WebSocketChat(const std::string& user); | ||||
|             WebSocketChat(const std::string& url, | ||||
|                           const std::string& user); | ||||
| 
 | ||||
|             void subscribe(const std::string& channel); | ||||
|             void start(); | ||||
| @@ -46,19 +40,27 @@ namespace | ||||
|             std::pair<std::string, std::string> decodeMessage(const std::string& str); | ||||
| 
 | ||||
|         private: | ||||
|             std::string _url; | ||||
|             std::string _user; | ||||
| 
 | ||||
|             ix::WebSocket _webSocket; | ||||
| 
 | ||||
|             std::queue<std::string> _receivedQueue; | ||||
| 
 | ||||
|             void log(const std::string& msg); | ||||
|     }; | ||||
| 
 | ||||
|     WebSocketChat::WebSocketChat(const std::string& user) : | ||||
|     WebSocketChat::WebSocketChat(const std::string& url, | ||||
|                                  const std::string& user) : | ||||
|         _url(url), | ||||
|         _user(user) | ||||
|     { | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     void WebSocketChat::log(const std::string& msg) | ||||
|     { | ||||
|         std::cout << msg << std::endl; | ||||
|     } | ||||
| 
 | ||||
|     size_t WebSocketChat::getReceivedMessagesCount() const | ||||
|     { | ||||
|         return _receivedQueue.size(); | ||||
| @@ -76,11 +78,10 @@ namespace | ||||
| 
 | ||||
|     void WebSocketChat::start() | ||||
|     { | ||||
|         std::string url("ws://localhost:8080/"); | ||||
|         _webSocket.setUrl(url); | ||||
|         _webSocket.setUrl(_url); | ||||
| 
 | ||||
|         std::stringstream ss; | ||||
|         log(std::string("Connecting to url: ") + url); | ||||
|         log(std::string("Connecting to url: ") + _url); | ||||
| 
 | ||||
|         _webSocket.setOnMessageCallback( | ||||
|             [this](ix::WebSocketMessageType messageType, | ||||
| @@ -164,10 +165,11 @@ namespace | ||||
|         _webSocket.send(encodeMessage(text)); | ||||
|     } | ||||
| 
 | ||||
|     void interactiveMain(const std::string& user) | ||||
|     void interactiveMain(const std::string& url, | ||||
|                          const std::string& user) | ||||
|     { | ||||
|         std::cout << "Type Ctrl-D to exit prompt..." << std::endl; | ||||
|         WebSocketChat webSocketChat(user); | ||||
|         WebSocketChat webSocketChat(url, user); | ||||
|         webSocketChat.start(); | ||||
| 
 | ||||
|         while (true) | ||||
| @@ -187,17 +189,13 @@ namespace | ||||
|         std::cout << std::endl; | ||||
|         webSocketChat.stop(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char** argv) | ||||
| { | ||||
|     std::string user("user"); | ||||
|     if (argc == 2) | ||||
|     int ws_chat_main(const std::string& url, | ||||
|                      const std::string& user) | ||||
|     { | ||||
|         user = argv[1]; | ||||
|         Socket::init(); | ||||
|         interactiveMain(url, user); | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     Socket::init(); | ||||
|     interactiveMain(user); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| @@ -9,15 +9,8 @@ | ||||
| #include <ixwebsocket/IXWebSocket.h> | ||||
| #include <ixwebsocket/IXSocket.h> | ||||
| 
 | ||||
| using namespace ix; | ||||
| 
 | ||||
| namespace | ||||
| namespace ix | ||||
| { | ||||
|     void log(const std::string& msg) | ||||
|     { | ||||
|         std::cout << msg << std::endl; | ||||
|     } | ||||
| 
 | ||||
|     class WebSocketConnect | ||||
|     { | ||||
|         public: | ||||
| @@ -32,6 +25,8 @@ namespace | ||||
|         private: | ||||
|             std::string _url; | ||||
|             ix::WebSocket _webSocket; | ||||
| 
 | ||||
|             void log(const std::string& msg); | ||||
|     }; | ||||
| 
 | ||||
|     WebSocketConnect::WebSocketConnect(const std::string& url) : | ||||
| @@ -40,6 +35,11 @@ namespace | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     void WebSocketConnect::log(const std::string& msg) | ||||
|     { | ||||
|         std::cout << msg << std::endl; | ||||
|     } | ||||
| 
 | ||||
|     void WebSocketConnect::stop() | ||||
|     { | ||||
|         _webSocket.stop(); | ||||
| @@ -148,18 +148,12 @@ namespace | ||||
|         std::cout << std::endl; | ||||
|         webSocketChat.stop(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char** argv) | ||||
| { | ||||
|     if (argc != 2) | ||||
|     int ws_connect_main(const std::string& url) | ||||
|     { | ||||
|         std::cerr << "Usage: ws_connect <url>" << std::endl; | ||||
|         return 1; | ||||
|         Socket::init(); | ||||
|         interactiveMain(url); | ||||
|         return 0; | ||||
|     } | ||||
|     std::string url = argv[1]; | ||||
| 
 | ||||
|     Socket::init(); | ||||
|     interactiveMain(url); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
		Reference in New Issue
	
	Block a user