connection.c
来自「xen 3.2.2 源码」· C语言 代码 · 共 413 行
C
413 行
/* * Copyright (C) 2003 - 2004 Mike Wray <mike.wray@hp.com>. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. This library is * distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <stdlib.h>#include <errno.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include "allocate.h"#include "connection.h"#include "file_stream.h"#include "socket_stream.h"#define MODULE_NAME "conn"#define DEBUG 1#undef DEBUG#include "debug.h"/** Initialize a file stream from a file desciptor. * * @param fd file descriptor * @param mode file mode * @param buffered make the stream buffered if 1, unbuffered if 0 * @param io return parameter for the stream * @return 0 on success, error code otherwise */int stream_init(int fd, const char *mode, int buffered, IOStream **io){ int err = 0; *io = file_stream_fdopen(fd, mode); if(!*io){ err = -errno; perror("fdopen"); goto exit; } if(!buffered){ // Make unbuffered. err = file_stream_setvbuf(*io, NULL, _IONBF, 0); if(err){ err = -errno; perror("setvbuf"); goto exit; } } exit: if(err && *io){ IOStream_close(*io); *io = NULL; } return err;}ConnList * ConnList_add(ConnList *l, Conn *conn){ ConnList *v; v = ALLOCATE(ConnList); v->conn = conn; v->next =l; return v;}ConnList * ConnList_del(ConnList *l, Conn *conn){ ConnList *prev, *curr, *next; for(prev = NULL, curr = l; curr; prev = curr, curr = next){ next = curr->next; if(curr->conn == conn){ if(prev){ prev->next = curr->next; } else { l = curr->next; } } } return l;}void ConnList_close(ConnList *l){ for( ; l; l = l->next){ Conn_close(l->conn); }} void ConnList_select(ConnList *l, SelectSet *set){ for( ; l; l = l->next){ Conn_select(l->conn, set); }}/** Handle connections according to a select set. * * @param set indicates ready connections */ConnList * ConnList_handle(ConnList *l, SelectSet *set){ ConnList *prev, *curr, *next; Conn *conn; int err; for(prev = NULL, curr = l; curr; prev = curr, curr = next){ next = curr->next; conn = curr->conn; err = Conn_handle(conn, set); if(err){ if(prev){ prev->next = curr->next; } else { l = curr->next; } } } return l;}Conn *Conn_new(int (*fn)(Conn *conn, int mode), void *data){ Conn *conn; conn = ALLOCATE(Conn); conn->fn = fn; conn->data = data; return conn;}int Conn_handler(Conn *conn, int mode){ int err = 0; dprintf(">\n"); if(conn->fn){ err = conn->fn(conn, mode); } else { dprintf("> no handler\n"); err = -ENOSYS; } if(err < 0){ dprintf("> err=%d, closing %d\n", err, conn->sock); Conn_close(conn); } dprintf("< err=%d\n", err); return err;}int Conn_handle(Conn *conn, SelectSet *set){ int err = 0; int mode = SelectSet_in(set, conn->sock); dprintf("> sock=%d mode=%d\n", conn->sock, mode); if(mode){ err = Conn_handler(conn, mode); } return err;}void Conn_select(Conn *conn, SelectSet *set){ dprintf("> sock=%d\n", conn->sock); SelectSet_add(set, conn->sock, conn->mode);}/** Initialize a connection. * * @param conn connection * @param sock socket * @param ipaddr ip address * @return 0 on success, error code otherwise */int Conn_init(Conn *conn, int sock, int type, int mode, struct sockaddr_in addr){ int err = 0; conn->addr = addr; conn->type = type; conn->mode = mode; conn->sock = sock; if(type == SOCK_STREAM){ err = stream_init(sock, "r", 0, &conn->in); if(err) goto exit; err = stream_init(sock, "w", 0, &conn->out); if(err) goto exit; } else { conn->in = socket_stream_new(sock); conn->out = socket_stream_new(sock); socket_stream_set_addr(conn->out, &addr); } exit: if(err) eprintf("< err=%d\n", err); return err;}/** Open a connection. * * @param conn connection * @param socktype socket type * @param ipaddr ip address to connect to * @param port port * @return 0 on success, error code otherwise */int Conn_connect(Conn *conn, int socktype, struct in_addr ipaddr, uint16_t port){ int err = 0; int sock; struct sockaddr_in addr_in; struct sockaddr *addr = (struct sockaddr *)&addr_in; socklen_t addr_n = sizeof(addr_in); dprintf("> addr=%s:%d\n", inet_ntoa(ipaddr), ntohs(port)); sock = socket(AF_INET, socktype, 0); if(sock < 0){ err = -errno; goto exit; } addr_in.sin_family = AF_INET; addr_in.sin_addr = ipaddr; addr_in.sin_port = port; err = connect(sock, addr, addr_n); if(err) goto exit; err = Conn_init(conn, sock, socktype, 0, addr_in); exit: if(err){ perror("Conn_connect"); eprintf("< err=%d\n", err); } return err;}/** Close a connection. * * @param conn connection */void Conn_close(Conn *conn){ if(!conn) return; if(conn->in) IOStream_close(conn->in); if(conn->out) IOStream_close(conn->out); shutdown(conn->sock, 2);}/** Set socket option to reuse address. */int setsock_reuse(int sock, int val){ int err = 0; err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); if(err < 0){ err = -errno; perror("setsockopt SO_REUSEADDR"); } return err;}/** Set socket broadcast option. */int setsock_broadcast(int sock, int val){ int err = 0; err = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)); if(err < 0){ err = -errno; perror("setsockopt SO_BROADCAST"); } return err;}/** Join a socket to a multicast group. */int setsock_multicast(int sock, uint32_t iaddr, uint32_t maddr){ int err = 0; struct ip_mreqn mreq = {}; int mloop = 0; // See 'man 7 ip' for these options. mreq.imr_multiaddr.s_addr = maddr; // IP multicast address. mreq.imr_address.s_addr = iaddr; // Interface IP address. mreq.imr_ifindex = 0; // Interface index (0 means any). err = setsockopt(sock, SOL_IP, IP_MULTICAST_LOOP, &mloop, sizeof(mloop)); if(err < 0){ err = -errno; perror("setsockopt IP_MULTICAST_LOOP"); goto exit; } err = setsockopt(sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); if(err < 0){ err = -errno; perror("setsockopt IP_ADD_MEMBERSHIP"); goto exit; } exit: return err;}/** Set a socket's multicast ttl (default is 1). */int setsock_multicast_ttl(int sock, uint8_t ttl){ int err = 0; err = setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); if(err < 0){ err = -errno; perror("setsockopt IP_MULTICAST_TTL"); } return err;}int setsock_pktinfo(int sock, int val){ int err = 0; err = setsockopt(sock, SOL_IP, IP_PKTINFO, &val, sizeof(val)); if(err < 0){ err = -errno; perror("setsockopt IP_PKTINFO"); } return err;}char * socket_flags(int flags){ static char s[6]; int i = 0; s[i++] = (flags & VSOCK_CONNECT ? 'c' : '-'); s[i++] = (flags & VSOCK_BIND ? 'b' : '-'); s[i++] = (flags & VSOCK_REUSE ? 'r' : '-'); s[i++] = (flags & VSOCK_BROADCAST ? 'B' : '-'); s[i++] = (flags & VSOCK_MULTICAST ? 'M' : '-'); s[i++] = '\0'; return s;}/** Create a socket. * The flags can include VSOCK_REUSE, VSOCK_BROADCAST, VSOCK_CONNECT. * * @param socktype socket type * @param saddr address * @param port port * @param flags flags * @param val return value for the socket connection * @return 0 on success, error code otherwise */int create_socket(int socktype, uint32_t saddr, uint32_t port, int flags, int *val){ int err = 0; int sock = 0; struct sockaddr_in addr_in; struct sockaddr *addr = (struct sockaddr *)&addr_in; socklen_t addr_n = sizeof(addr_in); int reuse, bcast; //dprintf(">\n"); reuse = (flags & VSOCK_REUSE); bcast = (flags & VSOCK_BROADCAST); addr_in.sin_family = AF_INET; addr_in.sin_addr.s_addr = saddr; addr_in.sin_port = port; dprintf("> flags=%s addr=%s port=%d\n", socket_flags(flags), inet_ntoa(addr_in.sin_addr), ntohs(addr_in.sin_port)); sock = socket(AF_INET, socktype, 0); if(sock < 0){ err = -errno; goto exit; } if(reuse){ err = setsock_reuse(sock, reuse); if(err < 0) goto exit; } if(bcast){ err = setsock_broadcast(sock, bcast); if(err < 0) goto exit; } if(flags & VSOCK_CONNECT){ err = connect(sock, addr, addr_n); if(err < 0){ err = -errno; perror("connect"); goto exit; } } if(flags & VSOCK_BIND){ err = bind(sock, addr, addr_n); if(err < 0){ err = -errno; perror("bind"); goto exit; } } { struct sockaddr_in self = {}; socklen_t self_n = sizeof(self); getsockname(sock, (struct sockaddr *)&self, &self_n); dprintf("> sockname sock=%d addr=%s port=%d reuse=%d bcast=%d\n", sock, inet_ntoa(self.sin_addr), ntohs(self.sin_port), reuse, bcast); } exit: *val = (err ? -1 : sock); //dprintf("< err=%d\n", err); return err;}int Conn_socket(int socktype, uint32_t saddr, uint32_t port, int flags, Conn **val){ int err; int sock; struct sockaddr_in addr_in; Conn *conn; err = create_socket(socktype, saddr, port, flags, &sock); if(err) goto exit; conn = Conn_new(NULL, NULL); addr_in.sin_family = AF_INET; addr_in.sin_addr.s_addr = saddr; addr_in.sin_port = port; Conn_init(conn, sock, socktype, 0, addr_in); exit: *val = (err ? NULL : conn); return err;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?