void from_recordset_stream(const std::string &reliable_storage_local_root, const std::string &reliable_storage_remote_root, Input_Stream &input, Output_Stream &output, const std::vector<rel::rlang::token> &tokens) { /* Get the headers. */ record headings(input.parse_headings()); // Get the resource id field id, this is the only field that we're interested // in, the rest are just placeholders. size_t resource_id_field_id = headings.mandatory_find_field( NP1_REL_DISTRIBUTED_RESOURCE_ID_FIELD_NAME); // Create the target recordset. std::string target_recordset_name(rel::rlang::compiler::eval_to_string_only(tokens)); np1::io::reliable_storage::id target_recordset_resource_id(target_recordset_name); np1::io::reliable_storage rs(reliable_storage_local_root, reliable_storage_remote_root); np1::io::reliable_storage::stream target_recordset_stream(rs); NP1_ASSERT(rs.create_wo(target_recordset_resource_id, target_recordset_stream), "Unable to create recordset " + target_recordset_name); // Read all the input recordset chunks ids and write them to the recordset file. input.parse_records( shallow_copy_chunk_record_callback( rs, target_recordset_stream, resource_id_field_id, headings.ref())); NP1_ASSERT(target_recordset_stream.close(), "Unable to close target recordset stream"); }
static void run(io::unbuffered_stream_base &input, io::unbuffered_stream_base &output, const rstd::vector<rel::rlang::token> &tokens) { // Split the arguments into a hostname and the rest. rstd::vector<rstd::vector<rel::rlang::token> > expressions = rel::rlang::compiler::split_expressions(tokens); NP1_ASSERT(expressions.size() >= 2, "meta.remote expects a hostname argument and a script argument"); rstd::string hostname = rel::rlang::compiler::eval_to_string_only(expressions[0]); // Get the rest of the arguments as a string. rstd::vector<rstd::vector<rel::rlang::token> >::const_iterator i = expressions.begin(); ++i; // Skip over the hostname. rstd::vector<rstd::vector<rel::rlang::token> >::const_iterator iz = expressions.end(); rstd::string r17_script; io::string_output_stream r17_script_sos(r17_script); for (; i != iz; ++i) { rel::rlang::io::token_writer::mandatory_write(r17_script_sos, *i); } r17_script_sos.write(';'); // Escape the string for use on a bash command line. rstd::string escaped_r17_script; io::string_output_stream escaped_r17_script_sos(escaped_r17_script); str::write_bash_escaped_string(r17_script, escaped_r17_script_sos); // Prepare the arguments ready for the exec. rstd::vector<rstd::string> exec_args; exec_args.push_back("ssh"); exec_args.push_back(hostname); exec_args.push_back("r17"); exec_args.push_back(escaped_r17_script); // Fork then exec so that we can return just like the other operators do and // so we can do useful things with stderr. int stderr_pipe[2]; process::mandatory_pipe_create(stderr_pipe); pid_t ssh_child_pid = process::mandatory_fork(); if (0 == ssh_child_pid) { // Child. close(stderr_pipe[0]); // If the host is localhost then just execute the script without the cost // of an exec if (str::cmp(hostname, "localhost") == 0) { io::file output_file; dup2(stderr_pipe[1], 2); script_run(input, output, r17_script, false); exit(0); } else { process::mandatory_execvp(exec_args, input.handle(), output.handle(), stderr_pipe[1]); NP1_ASSERT(false, "Unreachable code after mandatory_execvp"); } } // Parent. Read stderr stream. close(stderr_pipe[1]); read_and_prefix_stderr(hostname, stderr_pipe[0]); process::mandatory_wait_for_child(ssh_child_pid); }
inline static dt::string call(vm_heap &heap, const dt::string &command) { // Set stdout to be a temporary file. FILE *tmpfp = tmpfile(); NP1_ASSERT(tmpfp, "Unable to create temporary file for meta.shell function"); ::np1::io::file tmpf; tmpf.from_handle(fileno(tmpfp)); int saved_stdout = dup(1); dup2(tmpf.handle(), 1); // Execute the command. NP1_ASSERT(system(command.to_string().c_str()) >= 0, "system() failed for meta.shell"); // Set stdout back to what it was and read the whole file. dup2(saved_stdout, 1); close(saved_stdout); tmpf.rewind(); ::np1::io::mandatory_input_stream<np1::io::file> mandatory_tmpf(tmpf); ::np1::io::ext_heap_buffer_output_stream<vm_heap> command_output(heap, TEMP_FILE_READ_BUFFER_SIZE); mandatory_tmpf.copy(command_output); // Ensure that the command output is valid UTF-8. char *command_output_p = (char *)command_output.ptr(); size_t command_output_size = command_output.size(); str::replace_invalid_utf8_sequences(command_output_p, command_output_size, '?'); return dt::string(command_output_p, command_output_size); }
bool operator()(const record_ref &r) { // Open the current output chunk stream (if not opened) and write its name to the // file that lists the chunks in the recordset. if (!m_mandatory_current_target_chunk_stream.is_open()) { m_current_target_chunk_id = np1::io::reliable_storage::id::generate(); NP1_ASSERT(m_rs.create_wo(m_current_target_chunk_id, m_current_target_chunk_stream), "Unable to create recordset stream chunk " + m_current_target_chunk_id.to_string()); NP1_ASSERT(m_target_recordset_stream.write(m_current_target_chunk_id) && m_target_recordset_stream.write("\n"), "Unable to write to recordset"); m_headings.write(m_mandatory_current_target_chunk_stream); m_current_target_chunk_size = 0; } // Write the record to the output chunk stream, and close the stream // if it's big enough. size_t record_byte_size = r.byte_size(); r.write(m_mandatory_current_target_chunk_stream); m_current_target_chunk_size += record_byte_size; if (m_current_target_chunk_size > m_approx_max_chunk_size) { m_mandatory_current_target_chunk_stream.close(); } return true; }
void operator()(Input_Stream &input, Output_Stream &output, const rstd::vector<rel::rlang::token> &args) { // Sort out the arguments. NP1_ASSERT(args.size() == 0, "text.utf16_to_utf8 expects no arguments."); io::utf16_input_stream<Input_Stream> utf16_input(input); NP1_ASSERT(str::convert_utf16_to_utf8(utf16_input, output), "Invalid UTF-16 stream"); }
void initialize(const char *str) { const char *colon_p = strchr(str, ':'); NP1_ASSERT(colon_p, "Malformed IP endpoint string: " + rstd::string(str)); int64_t i64 = str::dec_to_int64(colon_p + 1); NP1_ASSERT((i64 > 0) && (i64 < MAX_PORT), "Malformed IP endpoint string: " + rstd::string(str)); *((char *)colon_p) = '\0'; initialize(str, (int)i64); *((char *)colon_p) = ':'; }
void from_data_stream(const std::string &reliable_storage_local_root, const std::string &reliable_storage_remote_root, Input_Stream &input, Output_Stream &output, const std::vector<rel::rlang::token> &tokens) { /* Get the headers. */ record headings(input.parse_headings()); // Interpret the arguments. std::vector<std::pair<std::string, rlang::dt::data_type> > args = rel::rlang::compiler::eval_to_strings(tokens); NP1_ASSERT((args.size() > 0) && (args.size() <= 2), "Incorrect number of arguments to rel.recordset.create"); tokens[0].assert(rlang::dt::data_type::TYPE_STRING == args[0].second, "First argument to rel.recordset.create is not a string"); std::string target_recordset_name = args[0].first; uint64_t approx_max_chunk_size = DEFAULT_APPROX_MAX_CHUNK_SIZE_BYTES; if (args.size() > 1) { tokens[0].assert((rlang::dt::data_type::TYPE_INT == args[1].second) || (rlang::dt::data_type::TYPE_UINT == args[1].second), "Second argument to rel.recordset.create is not an integer"); approx_max_chunk_size = str::dec_to_int64(args[1].first); } // Get the stream that will hold the recordset. np1::io::reliable_storage::id target_recordset_resource_id(target_recordset_name); np1::io::reliable_storage rs(reliable_storage_local_root, reliable_storage_remote_root); np1::io::reliable_storage::stream target_recordset_stream(rs); NP1_ASSERT(rs.create_wo(target_recordset_resource_id, target_recordset_stream), "Unable to create recordset " + target_recordset_name + " in reliable storage '" + reliable_storage_local_root + "'"); np1::io::reliable_storage::stream current_target_chunk_stream(rs); buffered_reliable_storage_stream_type buffered_current_target_chunk_stream( current_target_chunk_stream); mandatory_reliable_storage_stream_type mandatory_current_target_chunk_stream( buffered_current_target_chunk_stream); np1::io::reliable_storage::id current_target_chunk_id; uint64_t current_target_chunk_size = 0; // Read all the input data and redistribute it into recordset chunks. input.parse_records( chunk_data_record_callback( rs, target_recordset_stream, current_target_chunk_id, current_target_chunk_stream, mandatory_current_target_chunk_stream, current_target_chunk_size, approx_max_chunk_size, headings.ref())); // Close everything. mandatory_current_target_chunk_stream.close(); NP1_ASSERT(target_recordset_stream.close(), "Unable to close target recordset stream"); }
static dt::uinteger parse(const str::ref &time_s, const str::ref &format_s) { //TODO: do without this null-char-putting. char *time_ptr = (char *)time_s.ptr(); char *end_time_ptr = time_ptr + time_s.length(); char prev_end_time_char = *end_time_ptr; *end_time_ptr = '\0'; char *format_ptr = (char *)format_s.ptr(); char *end_format_ptr = format_ptr + format_s.length(); char prev_end_format_char = *end_format_ptr; *end_format_ptr = '\0'; struct tm tm_buf; // If we don't memset then any fields uninitialized by the time string won't be initialized by strptime // in release mode. memset(&tm_buf, 0, sizeof(tm_buf)); // We need to set the daylight savings time to "I don't know" otherwise mktime will think that it _is_ in effect // if the string doesn't specify what the timezone is. tm_buf.tm_isdst = -1; char *result = strptime(time_ptr, format_ptr, &tm_buf); *end_time_ptr = prev_end_time_char; *end_format_ptr = prev_end_format_char; NP1_ASSERT(result, "strptime failed on time string: '" + time_s.to_string() + "' format string: '" + format_s.to_string() + "'"); return time::sec_to_usec(mktime(&tm_buf)); }
static void run(io::unbuffered_stream_base &input, io::unbuffered_stream_base &output, const rstd::string &command) { // Set stdin to be the supplied input stream. int saved_stdin = dup(0); dup2(input.handle(), 0); // Set stdout to be the supplied output stream. int saved_stdout = dup(1); dup2(output.handle(), 1); // Execute the command. int system_result = system(command.c_str()); if (system_result < 0) { rstd::string message("meta.shell failed. command: "); message.append(command); NP1_ASSERT(false, message); } // Set stdin & stdout back to what they were. dup2(saved_stdin, 0); dup2(saved_stdout, 1); close(saved_stdin); close(saved_stdout); }
void *alloc(size_t sz) { NP1_ASSERT(m_free_list, "fixed_homogenous_heap out of space!"); void *p = (void *)(m_free_list + 1); m_free_list = m_free_list->m_next; return p; }
bool operator()(const rel::record_ref &r) const { rstd::string file_name = r.mandatory_field(m_file_name_field_id).to_string(); rstd::string host_name = r.mandatory_field(m_host_name_field_id).to_string(); // Prefix the tokens with the host name and a command to read the file. rstd::vector<rel::rlang::token> file_read_tokens; file_read_tokens.push_back(rel::rlang::token(host_name.c_str(), rel::rlang::token::TYPE_STRING)); file_read_tokens.push_back(rel::rlang::token(",", rel::rlang::token::TYPE_COMMA)); file_read_tokens.push_back(rel::rlang::token("io.file.read", rel::rlang::token::TYPE_IDENTIFIER_VARIABLE)); file_read_tokens.push_back(rel::rlang::token("(", rel::rlang::token::TYPE_OPEN_PAREN)); file_read_tokens.push_back(rel::rlang::token(file_name.c_str(), rel::rlang::token::TYPE_STRING)); file_read_tokens.push_back(rel::rlang::token(")", rel::rlang::token::TYPE_CLOSE_PAREN)); file_read_tokens.push_back(rel::rlang::token("|", rel::rlang::token::TYPE_OPERATOR)); rstd::vector<rel::rlang::token> remote_tokens(file_read_tokens); remote_tokens.append(m_tokens); // Create a temporary file for the use of the child. FILE *child_output_fp = tmpfile(); NP1_ASSERT(child_output_fp, "Unable to create temporary file for child process output"); // Add into the process pool, to be processed asap. m_process_pool_map.add( host_name, child_process_f(child_output_fp, remote_tokens), on_child_process_exit(child_output_fp, m_final_output, m_output_headings_written)); return true; }
// Read the file descriptor, copying to stderr and prefixing each line with helpful info. static void read_and_prefix_stderr(const rstd::string &hostname, int fd) { FILE *fp = fdopen(fd, "r"); NP1_ASSERT(fp, "meta.remote: fdopen failed"); char line[256 * 1024]; while (fgets(line, sizeof(line)-1, fp)) { fprintf(stderr, "meta.remote(%s): %s", hostname.c_str(), line); } }
bool operator()(const record_ref &r) { np1::io::reliable_storage::id input_chunk_id(r.mandatory_field(m_resource_id_field_id)); NP1_ASSERT(m_target_recordset_stream.write(input_chunk_id) && m_target_recordset_stream.write("\n"), "Unable to write to recordset"); return true; }
void initialize(const char *str, int port) { memset(&m_addr, 0, sizeof(m_addr)); unsigned long ip_addr_num = inet_addr(str); NP1_ASSERT(INADDR_NONE != ip_addr_num, "Unable to convert IP address string '" + rstd::string(str) + "' to IP address"); initialize(ip_addr_num, port); }
size_t read(void *buf, size_t bytes_to_read) { size_t bytes_read = 0; if (!m_stream.read(buf, bytes_to_read, &bytes_read)) { NP1_ASSERT(false, "Stream " + m_stream.name() + ": Unable to read from stream"); } return bytes_read; }
void operator()(Input_Stream &input, Output_Stream &output, const rstd::vector<rel::rlang::token> &tokens) { NP1_ASSERT(tokens.size() == 0, "rel.record_count accepts no arguments"); // Read & discard the headings. input.parse_headings(); uint64_t number_records = 0; input.parse_records(record_counter_callback(number_records)); output.write(str::to_dec_str(number_records).c_str()); }
void copy(const std::string &file_name, bool overwrite) { io::file file; const char *name = file_name.c_str(); bool open_result = overwrite ? file.create_or_open_wo_trunc(name) : file.create_or_open_wo_append(name); NP1_ASSERT(open_result, "Unable to open output file " + file_name); mandatory_output_stream<io::file> mandatory_output(file); copy(mandatory_output); }
// Just pass the whole program argument list here. static int from_main(int argc, const char *argv[]) { const char *real_program_name = argv[0]; NP1_ASSERT(argc >= 2, get_usage(real_program_name)); NP1_ASSERT(str::is_valid_utf8(argc, argv), "Arguments are not valid UTF-8 strings"); int fake_argc = argc - 1; const char **fake_argv = &argv[1]; std::vector<std::string> args = str::argv_to_string_vector(fake_argc, fake_argv); io::file stdin_f; stdin_f.from_stdin(); io::file stdout_f; stdout_f.from_stdout(); run_once(stdin_f, stdout_f, args); return 0; }
void operator()(Input_Stream &input, Output_Stream &output, const std::vector<rel::rlang::token> &tokens, sort_type_type sort_type, sort_order_type sort_order) { NP1_ASSERT(tokens.size() > 0, "Unexpected empty stream operator argument list"); // Read the first line of input, we need it to add meaning to the arguments. record headings(input.parse_headings()); std::vector<std::string> arg_headings; rlang::compiler::compile_heading_name_list( tokens, headings.ref(), arg_headings); // Create the compare specs. detail::compare_specs comp_specs(headings, arg_headings); // Write out the headings then do the actual sorting. headings.write(output); less_than lt(comp_specs); greater_than gt(comp_specs); switch (sort_type) { case TYPE_MERGE_SORT: switch (sort_order) { case ORDER_ASCENDING: sort<detail::merge_sort>(input, output, lt); break; case ORDER_DESCENDING: sort<detail::merge_sort>(input, output, gt); break; } break; case TYPE_QUICK_SORT: //TODO: why the dickens isn't this quick sort and why is quick sort broken? switch (sort_order) { case ORDER_ASCENDING: sort<detail::merge_sort>(input, output, lt); break; case ORDER_DESCENDING: sort<detail::merge_sort>(input, output, gt); break; } break; } }
// Convert to a string, crash on error. rstd::string to_string() const { char temp[256]; memset(temp, 0, sizeof(temp)); #ifdef _WIN32 /* There's no inet_ntop in WinXP & earlier. TODO: move to Windows vista/7 or define own inet_ntop. The Windows documentation says that the statically allocated buffer is allocated per-thread. */ const char *addr_string = inet_ntoa(m_addr.sin_addr); NP1_ASSERT((strlen(addr_string) < sizeof(temp)-1), "Unable to convert IP endpoint to string, resulting string is too long"); strcpy(temp, addr_string); #else NP1_ASSERT(inet_ntop(AF_INET, &m_addr.sin_addr, temp, sizeof(temp)-1), "Unable to convert IP endpoint to string"); #endif char *p = temp + strlen(temp); NP1_ASSERT((p + 7 < temp + sizeof(temp)), "Unable to add port to end of IP endpoint string"); *p++ = ':'; str::to_dec_str(p, port()); return temp; }
pattern &do_get(const str::ref &pattern_str, bool case_sensitive) { size_t pattern_str_len = pattern_str.length(); uint64_t hval = hash::fnv1a64::add(pattern_str.ptr(), pattern_str_len, hash::fnv1a64::init()); size_t offset = (size_t)hval & (CACHE_HASH_TABLE_SIZE-1); entry *e = &m_entries[offset]; if ((str::cmp(pattern_str, e->m_pattern_string) != 0) || (case_sensitive != e->m_is_case_sensitive)) { // As this is just a cache, overwrite the existing pattern. e->m_pattern.clear(); bool compile_result = case_sensitive ? e->m_pattern.compile(pattern_str) : e->m_pattern.icompile(pattern_str); NP1_ASSERT(compile_result, "Unable to compile pattern: " + pattern_str.to_string()); e->m_pattern_string = pattern_str.to_string(); e->m_is_case_sensitive = case_sensitive; } return e->m_pattern; }
static void run(io::unbuffered_stream_base &input, io::unbuffered_stream_base &output, const std::vector<rel::rlang::token> &tokens) { std::string command = rel::rlang::compiler::eval_to_string_only(tokens); // Set stdin to be the supplied input stream. int saved_stdin = dup(0); dup2(input.handle(), 0); // Set stdout to be the supplied output stream. int saved_stdout = dup(1); dup2(output.handle(), 1); // Execute the command. NP1_ASSERT(system(command.c_str()) >= 0, "system() failed for meta.shell stream operator."); // Set stdin & stdout back to what they were. dup2(saved_stdin, 0); dup2(saved_stdout, 1); close(saved_stdin); close(saved_stdout); }
static bool read_all_line_by_line(Unbuffered_Stream &input, Callback line_callback) { enum { BUFFER_SIZE = 256 * 1024 }; unsigned char buffer[BUFFER_SIZE + 1]; unsigned char *buffer_end = buffer + BUFFER_SIZE; unsigned char *buffer_read_pos = buffer; const unsigned char *start_line = buffer_read_pos; ssize_t number_bytes_read; uint64_t line_number = 1; mandatory_input_stream<Unbuffered_Stream> mandatory_input(input); const unsigned char *buffer_data_end = 0; while ((number_bytes_read = mandatory_input.read_some(buffer_read_pos, buffer_end - buffer_read_pos)) > 0) { buffer_data_end = buffer_read_pos + number_bytes_read; const unsigned char *end_line; // Find the end of the line to make sure that we have the whole thing in the buffer. while ((end_line = (const unsigned char *)memchr(start_line, '\n', buffer_data_end - start_line))) { // We have the whole line. Call the callback to deal with the line. if (!line_callback(str::ref((const char *)start_line, end_line - start_line), line_number)) { return false; } // Now go around again. start_line = end_line + 1; ++line_number; } // There's probably an incomplete line left in the buffer. Move it to the start of the buffer. ssize_t remainder_length = buffer_data_end - start_line; NP1_ASSERT(remainder_length < BUFFER_SIZE, "Stream " + input.name() + ": line is too long. Remainder length: " + str::to_dec_str(remainder_length)); memmove(buffer, start_line, remainder_length); buffer_read_pos = buffer + remainder_length; start_line = buffer; } return true; }
static dt::string format(vm_heap &h, const dt::uinteger &time_epoch, const str::ref &format_s) { //TODO: do without this null-char-putting. char *format_ptr = (char *)format_s.ptr(); char *end_format_ptr = format_ptr + format_s.length(); char prev_end_format_char = *end_format_ptr; *end_format_ptr = '\0'; time_t time_sec = lib::int64::divide(time_epoch, 1000000); struct tm *tm_p = localtime(&time_sec); const size_t size_to_alloc = 1024; char *formatted_buf = h.alloc(size_to_alloc); const size_t max_formatted_length = size_to_alloc - 1; size_t bytes_written = strftime(formatted_buf, max_formatted_length, format_ptr, tm_p); *end_format_ptr = prev_end_format_char; NP1_ASSERT( bytes_written != 0, "Max formatted time length exceeded. Max size is " + str::to_dec_str(max_formatted_length) + " bytes."); return dt::string(formatted_buf, bytes_written); }
void operator()(const std::string &reliable_storage_local_root, const std::string &reliable_storage_remote_root, const std::string &listen_endpoint, Input_Stream &input, Output_Stream &output, const std::vector<rel::rlang::token> &tokens) { log_info("Reading headers and compiling expression against headers."); /* Get the headers. */ record headings(input.parse_headings()); // Compile, just to check that the headings & tokens are likely to work // when we distribute. record empty_headings; rlang::vm vm = rlang::compiler::compile_single_expression( tokens, headings.ref(), empty_headings.ref()); // Check that the expression is actually a boolean expression. NP1_ASSERT(vm.return_type() == rlang::dt::TYPE_BOOL, "Expression is not a boolean expression"); // Do the distribution. distributed::distribute(log_id(), headings, headings, "rel.where", reliable_storage_local_root, reliable_storage_remote_root, listen_endpoint, input, output, tokens); }
/// Write a buffer, returns false on error. bool write_some(const void *buf, size_t bytes_to_write, size_t *bytes_written_p) { NP1_ASSERT(m_buffer_pos <= m_buffer_end, "existing buffer_pos is past buffer_end!"); unsigned char *new_pos = m_buffer_pos + bytes_to_write; if (new_pos > m_buffer_end) { size_t current_size = m_buffer_end - m_buffer; size_t new_size = current_size + m_allocation_size; if (new_pos > m_buffer + new_size) { new_size = current_size + bytes_to_write; } size_t current_used_size = m_buffer_pos - m_buffer; NP1_ASSERT(new_size >= current_used_size, "new buffer is not big enough to hold even the existing data!"); NP1_ASSERT(new_size >= current_used_size + bytes_to_write, "new buffer is not big enough to hold the existing data plus the new data!"); unsigned char *new_buffer = (unsigned char *)m_heap.alloc(new_size); if (new_buffer > m_buffer) { NP1_ASSERT(m_buffer + current_used_size <= new_buffer, "new buffer is not allocated at the correct address!"); } else { NP1_ASSERT(new_buffer + current_used_size <= m_buffer, "new buffer is not big enough or not allocated at the correct address!"); } memcpy(new_buffer, m_buffer, current_used_size); m_buffer_pos = new_buffer + current_used_size; m_buffer_end = new_buffer + new_size; m_heap.free((char *)m_buffer); m_buffer = new_buffer; new_pos = m_buffer_pos + bytes_to_write; } NP1_ASSERT(new_pos <= m_buffer_end, "new_pos is past buffer_end!"); NP1_ASSERT(m_buffer_pos < m_buffer_end, "buffer_pos is past or equal to buffer_end after possible resize"); memcpy(m_buffer_pos, buf, bytes_to_write); m_buffer_pos = new_pos; *bytes_written_p = bytes_to_write; return true; }
// Put one byte back on the stream. Fail if a byte is already ungot. void unget(int c) { NP1_ASSERT(m_ungotc < 0, "Unable to push a byte back on to the stream"); m_ungotc = c; }
const compare_spec &at(size_t n) const { NP1_ASSERT(n < size(), "compare_spec.at(n): n out of bounds"); return *(begin() + n); }
bool close() { NP1_ASSERT(m_stream.close(), "Stream " + m_stream.name() + ": close() failed"); return true; }
void port(int port) { NP1_ASSERT((port > 0) && (port < MAX_PORT), "Invalid port: " + str::to_dec_str(port)); m_addr.sin_port = htons(port); }