📄 socket.cc
字号:
// -*- mode: c++; c-basic-offset: 2 -*-/* * socket.{cc,hh} -- transports packets via sockets * Mark Huang <mlhuang@cs.princeton.edu> * * Copyright (c) 2004 The Trustees of Princeton University (Trustees). * Copyright (c) 2006-2007 Regents of the University of California * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the conditions * listed in the Click LICENSE file. These conditions include: you must * preserve this copyright notice, and you cannot mention the copyright * holders in advertising related to the Software without their permission. * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This * notice is a summary of the Click LICENSE file; the license in that file is * legally binding. */#include <click/config.h>#include <click/error.hh>#include <click/confparse.hh>#include <click/glue.hh>#include <click/standard/scheduleinfo.hh>#include <click/packet_anno.hh>#include <click/packet.hh>#include <unistd.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/un.h>#include <arpa/inet.h>#include <netinet/tcp.h>#include <fcntl.h>#include "socket.hh"#ifdef HAVE_PROPER#include <proper/prop.h>#endifCLICK_DECLSSocket::Socket() : _task(this), _timer(this), _fd(-1), _active(-1), _rq(0), _wq(0), _local_port(0), _local_pathname(""), _timestamp(true), _sndbuf(-1), _rcvbuf(-1), _snaplen(2048), _headroom(Packet::default_headroom), _nodelay(1), _verbose(false), _client(false), _proper(false), _allow(0), _deny(0){}Socket::~Socket(){}intSocket::configure(Vector<String> &conf, ErrorHandler *errh){ String socktype; _client = (noutputs() == 0); if (cp_va_kparse(conf, this, errh, "TYPE", cpkP+cpkM, cpString, &socktype, cpIgnoreRest, cpEnd) < 0) return -1; socktype = socktype.upper(); // remove keyword arguments Element *allow = 0, *deny = 0; if (cp_va_kparse_remove_keywords(conf, this, errh, "VERBOSE", 0, cpBool, &_verbose, "SNAPLEN", 0, cpUnsigned, &_snaplen, "HEADROOM", 0, cpUnsigned, &_headroom, "TIMESTAMP", 0, cpBool, &_timestamp, "RCVBUF", 0, cpUnsigned, &_rcvbuf, "SNDBUF", 0, cpUnsigned, &_sndbuf, "NODELAY", 0, cpUnsigned, &_nodelay, "CLIENT", 0, cpBool, &_client, "PROPER", 0, cpBool, &_proper, "ALLOW", 0, cpElement, &allow, "DENY", 0, cpElement, &deny, cpEnd) < 0) return -1; if (allow && !(_allow = (IPRouteTable *)allow->cast("IPRouteTable"))) return errh->error("%s is not an IPRouteTable", allow->name().c_str()); if (deny && !(_deny = (IPRouteTable *)deny->cast("IPRouteTable"))) return errh->error("%s is not an IPRouteTable", deny->name().c_str()); if (socktype == "TCP" || socktype == "UDP") { _family = AF_INET; _socktype = socktype == "TCP" ? SOCK_STREAM : SOCK_DGRAM; _protocol = socktype == "TCP" ? IPPROTO_TCP : IPPROTO_UDP; CpVaParseCmd portcmd = (socktype == "TCP" ? cpTCPPort : cpUDPPort); if (cp_va_kparse(conf, this, errh, "TYPE", cpkP+cpkM, cpIgnore, "ADDR", cpkP+cpkM, cpIPAddress, &_remote_ip, "PORT", cpkP+cpkM, portcmd, &_remote_port, "LOCAL_ADDR", cpkP, cpIPAddress, &_local_ip, "LOCAL_PORT", cpkP, portcmd, &_local_port, cpEnd) < 0) return -1; } else if (socktype == "UNIX" || socktype == "UNIX_DGRAM") { _family = AF_UNIX; _socktype = socktype == "UNIX" ? SOCK_STREAM : SOCK_DGRAM; _protocol = 0; if (cp_va_kparse(conf, this, errh, "TYPE", cpkP+cpkM, cpIgnore, "FILENAME", cpkP+cpkM, cpFilename, &_remote_pathname, "LOCAL_FILENAME", cpkP, cpFilename, &_local_pathname, cpEnd) < 0) return -1; int max_path = (int)sizeof(((struct sockaddr_un *)0)->sun_path); // if not in the abstract namespace (begins with zero byte), // reserve room for trailing NUL if ((_remote_pathname[0] && _remote_pathname.length() >= max_path) || (_remote_pathname[0] == 0 && _remote_pathname.length() > max_path)) return errh->error("remote filename '%s' too long", _remote_pathname.printable().c_str()); if ((_local_pathname[0] && _local_pathname.length() >= max_path) || (_local_pathname[0] == 0 && _local_pathname.length() > max_path)) return errh->error("local filename '%s' too long", _local_pathname.printable().c_str()); } else return errh->error("unknown socket type `%s'", socktype.c_str()); return 0;}intSocket::initialize_socket_error(ErrorHandler *errh, const char *syscall){ int e = errno; // preserve errno if (_fd >= 0) { remove_select(_fd, SELECT_READ | SELECT_WRITE); close(_fd); _fd = -1; } return errh->error("%s: %s", syscall, strerror(e));}intSocket::initialize(ErrorHandler *errh){ // open socket, set options _fd = socket(_family, _socktype, _protocol); if (_fd < 0) return initialize_socket_error(errh, "socket"); if (_family == AF_INET) { _remote.in.sin_family = _family; _remote.in.sin_port = htons(_remote_port); _remote.in.sin_addr = _remote_ip.in_addr(); _remote_len = sizeof(_remote.in); _local.in.sin_family = _family; _local.in.sin_port = htons(_local_port); _local.in.sin_addr = _local_ip.in_addr(); _local_len = sizeof(_local.in); } else { _remote.un.sun_family = _family; _remote_len = offsetof(struct sockaddr_un, sun_path) + _remote_pathname.length(); if (_remote_pathname[0]) { strcpy(_remote.un.sun_path, _remote_pathname.c_str()); _remote_len++; } else memcpy(_remote.un.sun_path, _remote_pathname.c_str(), _remote_pathname.length()); _local.un.sun_family = _family; _local_len = offsetof(struct sockaddr_un, sun_path) + _local_pathname.length(); if (_local_pathname[0]) { strcpy(_local.un.sun_path, _local_pathname.c_str()); _local_len++; } else memcpy(_local.un.sun_path, _local_pathname.c_str(), _local_pathname.length()); } // enable timestamps if (_timestamp) {#ifdef SO_TIMESTAMP int one = 1; if (setsockopt(_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0) return initialize_socket_error(errh, "setsockopt(SO_TIMESTAMP)");#else return initialize_socket_error(errh, "TIMESTAMP not supported on this platform");#endif }#ifdef TCP_NODELAY // disable Nagle algorithm if (_protocol == IPPROTO_TCP && _nodelay) if (setsockopt(_fd, IP_PROTO_TCP, TCP_NODELAY, &_nodelay, sizeof(_nodelay)) < 0) return initialize_socket_error(errh, "setsockopt(TCP_NODELAY)");#endif // set socket send buffer size if (_sndbuf >= 0) if (setsockopt(_fd, SOL_SOCKET, SO_SNDBUF, &_sndbuf, sizeof(_sndbuf)) < 0) return initialize_socket_error(errh, "setsockopt(SO_SNDBUF)"); // set socket receive buffer size if (_rcvbuf >= 0) if (setsockopt(_fd, SOL_SOCKET, SO_RCVBUF, &_rcvbuf, sizeof(_rcvbuf)) < 0) return initialize_socket_error(errh, "setsockopt(SO_RCVBUF)"); // if a server, then the first arguments should be interpreted as // the address/port/file to bind() to, not to connect() to if (!_client) { memcpy(&_local, &_remote, _remote_len); _local_len = _remote_len; } // if a server, or if the optional local arguments have been // specified, bind() to the specified address/port/file if (!_client || _local_port != 0 || _local_pathname != "") {#ifdef HAVE_PROPER int ret = -1; if (_proper) { ret = prop_bind_socket(_fd, (struct sockaddr *)&_local, _local_len); if (ret < 0) errh->warning("prop_bind_socket: %s", strerror(errno)); } if (ret < 0)#endif if (bind(_fd, (struct sockaddr *)&_local, _local_len) < 0) return initialize_socket_error(errh, "bind"); } if (_client) { // connect if (_socktype == SOCK_STREAM) { if (connect(_fd, (struct sockaddr *)&_remote, _remote_len) < 0) return initialize_socket_error(errh, "connect"); if (_verbose) click_chatter("%s: opened connection %d to %s:%d", declaration().c_str(), _fd, IPAddress(_remote.in.sin_addr).unparse().c_str(), ntohs(_remote.in.sin_port)); } _active = _fd; } else { // start listening if (_socktype == SOCK_STREAM) { if (listen(_fd, 2) < 0) return initialize_socket_error(errh, "listen"); if (_verbose) { if (_family == AF_INET) click_chatter("%s: listening for connections on %s:%d (%d)", declaration().c_str(), IPAddress(_local.in.sin_addr).unparse().c_str(), ntohs(_local.in.sin_port), _fd); else click_chatter("%s: listening for connections on %s (%d)", declaration().c_str(), _local.un.sun_path, _fd); } } else { _active = _fd; } } // nonblocking I/O and close-on-exec for the socket fcntl(_fd, F_SETFL, O_NONBLOCK); fcntl(_fd, F_SETFD, FD_CLOEXEC); if (noutputs()) add_select(_fd, SELECT_READ); if (ninputs() && input_is_pull(0)) { ScheduleInfo::join_scheduler(this, &_task, errh); _signal = Notifier::upstream_empty_signal(this, 0, &_task); add_select(_fd, SELECT_WRITE); _timer.initialize(this); } return 0;}void
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -