⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 local.c

📁 Libnet is a cross-platform library aimed at game developers. It has an abstract high level API, whic
💻 C
字号:
/*---------------------------------------------------------------- * local.c - Local Host driver for libnet *---------------------------------------------------------------- *  libnet is (c) Copyright Chad Catlett and George Foot 1997-1999 * *  Please look in `docs' for details, documentation and *  distribution conditions. */#include <ctype.h>#include <string.h>#include <stdlib.h>#include <stdio.h>#include "libnet.h"#include "internal.h"#include "config.h"#include "threads.h"#include "platdefs.h"#define MAX_PACKET		(16+1)#define MAX_PACKET_SIZE		512typedef struct packet {    int from;    int size;    char data[MAX_PACKET_SIZE];} packet_t;typedef struct port {    int num;    int users;    packet_t packets[MAX_PACKET];    int head, tail;    struct port *next;    } port_t;typedef struct channel_data {    port_t *port;    int target;		/* may get destroyed so we can't just point to it */} channel_data_t;static port_t *port_list;MUTEX_DEFINE_STATIC(port_list);static int disable_driver = 0;/* * Port list helpers */static port_t *find_port(int num){    port_t *p = port_list;    while (p) {	if (p->num == num)	    return p;	p = p->next;    }    return NULL;}static int get_free_port(void){    static int i = 1;    while (find_port(i)) i++;    return i;}static port_t *create_port(int num){    port_t *p = find_port(num);    if (!p) {	    p = malloc(sizeof *p);	    if (p) {		p->num = num;		p->users = 1;		p->head = p->tail = 0;		p->next = port_list;		port_list = p;	    }    }    else	p->users++;    return p;}static void destroy_port(port_t *match){    port_t *p = port_list, *prev = NULL;            while (p) {	if (p == match) {	    p->users--;	    if (!p->users) {		if (!prev)		    port_list = p->next;		else		    prev->next = p->next;	    		free(p);	    }	    return;	}		prev = p;	p = p->next;    }}/* detect: *  This function returns one of the following: * *  - NET_DETECT_YES    (if the network driver can definitely work) *  - NET_DETECT_MAYBE  (if the network driver might be able to work) *  - NET_DETECT_NO     (if the network driver definitely cannot work) */static int local_detect(void){    return (!disable_driver) ? NET_DETECT_YES : NET_DETECT_NO;}/* init: *  Initialises the driver.  Return zero if successful, non-zero if not. */static int local_init(void){    port_list = NULL;    MUTEX_CREATE(port_list);    return 0;}/* exit: *  Shuts down the driver, freeing allocated resources, etc.  All channels *  will already have been destroyed by the `destroy_channel' function, so  *  normally all this needs to do is tidy up anything `init' left untidy. *  Return zero if successful, non-zero otherwise. */static int local_exit(void){    MUTEX_LOCK(port_list);    while (port_list)	destroy_port(port_list);    MUTEX_UNLOCK(port_list);    MUTEX_DESTROY(port_list);    return 0;}static int parse_address(const char *addr){    if (!addr)        return get_free_port();	/* any */    if (*addr == 0) 	return 0;		/* default */    /* else, [:]port is user specified */        if (*addr == ':')		addr++;        if (!isdigit((unsigned char)*addr))	return -1;    else	return atoi(addr);}/* init_channel: *  This function will be called when a channel is created, before any  *  data is sent.  It should at least set the channel's return address to *  something meaningful or at least to a valid string if the driver can't *  supply a return address.  It can allocate memory, pointed to by the *  `data' field of the channel struct.  Return zero if successful, non-zero *  otherwise. */static int local_init_channel(NET_CHANNEL *chan, const char *addr){    channel_data_t *data;    port_t *port;    int num;    MUTEX_LOCK(port_list);    num = parse_address(addr);    if (num < 0) {	MUTEX_UNLOCK(port_list);	return -1;    }        port = create_port(num);    /* We don't need the lock any more because `create_port' incremented     * the usage counter on the port, so it won't vanish, and we're not      * dereferencing the pointer any more. */    MUTEX_UNLOCK(port_list);    if (!port)        return -1;        sprintf(chan->local_addr, "%d", num);    chan->data = data = malloc(sizeof *data);    data->port = port;    data->target = -1;        return 0;}/* destroy_channel: *  Called when a channel is being closed.  This should tidy up anything  *  that init_channel or subsequent functions have left messy, for example  *  it should free any data block that has been allocated.  Return zero if  *  successful, non-zero otherwise. */static int local_destroy_channel(NET_CHANNEL *chan){    channel_data_t *data = chan->data;    MUTEX_LOCK(port_list);    destroy_port(data->port);    MUTEX_UNLOCK (port_list);    free(data);    return 0;}/* update_target: *  This will be called if the target_addr for the channel changes.  *  If necessary, the address could be converted into network format here. *  Return zero if any conversion was possible, but don't bother checking *  whether or not the target is reachable. */static int local_update_target(NET_CHANNEL *chan){    char *addr;    int target;    addr = chan->target_addr;    if (!addr) return -1;        MUTEX_LOCK(port_list);    target = parse_address(addr);    MUTEX_UNLOCK(port_list);    if (target < 0) return -1;    ((channel_data_t *)chan->data)->target = target;    return 0;}static inline int next_packet(int n){    if (n+1 < MAX_PACKET)	return n+1;    else	return 0;}/* send: *  Send the given data block down the channel.  `size' is in bytes. *  Return zero if successful, otherwise return non-zero.  Don't bother *  checking that the packet arrived at the destination though. */static int local_send(NET_CHANNEL *chan, const void *buf, int size){    channel_data_t *data;    port_t *target;    packet_t *packet;        if (size > MAX_PACKET_SIZE)	return -1;        /* Strictly, we should be locking the port's queue, but this will do. */    MUTEX_LOCK(port_list);    data = chan->data;    target = find_port(data->target);        if (target && (next_packet(target->head) != target->tail)) {	target->head = next_packet(target->head);    	packet = &target->packets[target->head];	packet->from = data->port->num;	packet->size = size;	memcpy(packet->data, buf, size);    }    /* Errors and successes all end up here, because no delivery     * checking occurs on channels. */    MUTEX_UNLOCK(port_list);        return 0;}/* recv: *  Receive a packet into the memory block given.  `size' is the maximum *  number of bytes to receive.  The sender's address will be put in `from'. *  Return the number of bytes received, or -1 if an error occured.  A zero  *  return value means there was no data to receive. * *  Behaviour if more that `size' bytes are waiting is undecided -- either *  return an error, or read `size' bytes and return `size'.  The remainder *  of the packet should probably be discarded, in either case.  For now, *  it's the user's fault for not making the buffer big enough. */static int local_recv(NET_CHANNEL *chan, void *buf, int size, char *from){    port_t *port;    packet_t *packet;    port = ((channel_data_t *)chan->data)->port;    /* Strictly we should be locking the port's queue, but this will do.     * We get the lock this early in case another channel is sharing the     * port.  (I'm particularly concerned about overlapping `recv' calls     * here.) */    MUTEX_LOCK(port_list);    if (port->tail == port->head) {	MUTEX_UNLOCK(port_list);	return -1;    }    port->tail = next_packet(port->tail);    packet = &port->packets[port->tail];            if (packet->size < size)	size = packet->size;    memcpy(buf, packet->data, size);    if (from)	sprintf(from, "%d", packet->from);        MUTEX_UNLOCK(port_list);    return size;}/* query: *  Returns non-zero if there is data waiting to be read from the channel. */static int local_query(NET_CHANNEL *chan){    port_t *port = ((channel_data_t *)chan->data)->port;    /* Again, strictly we should lock the port queue, not the whole list. */    MUTEX_LOCK(port_list);    if (port->head != port->tail) {	MUTEX_UNLOCK(port_list);	return 1;    } else {	MUTEX_UNLOCK(port_list);	return 0;    }}/* load_config: *  This will be called once when the library is initialised, inviting the *  driver to load configuration information from the passed text file. */static void local_load_config(NET_DRIVER *drv, FILE *fp){    char *option, *value;    if (__libnet_internal__seek_section(fp, "localhost"))	return;    while (__libnet_internal__get_setting(fp, &option, &value) == 0) 	if (strcmp(option, "disable") == 0)	    disable_driver = (atoi(value) || (value[0] == 'y'));        if (drv->load_conn_config)	drv->load_conn_config(drv, fp, "localhost");}NET_DRIVER net_driver_local = {    "Local Host",    "Local Host driver",    NET_CLASS_LOCAL,    local_detect,    local_init,    local_exit,	NULL, NULL,    local_init_channel,    local_destroy_channel,    local_update_target,    local_send,    local_recv,    local_query,    NULL, NULL,    NULL, NULL, NULL, NULL,    NULL, NULL, NULL, NULL, NULL,    local_load_config,    NULL, NULL};

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -