new socket service and socket lib instead of old connection, and rewrite redis lib

This commit is contained in:
云风
2013-06-21 13:02:51 +08:00
parent 0ffd9b8b8e
commit 277b6957cb
16 changed files with 1182 additions and 1248 deletions

View File

@@ -21,14 +21,14 @@ all : \
service/logger.so \
service/gate.so \
service/client.so \
service/connection.so \
service/master.so \
service/multicast.so \
service/tunnel.so \
service/harbor.so \
service/localcast.so \
service/socket.so \
luaclib/skynet.so \
luaclib/socket.so \
luaclib/socketbuffer.so \
luaclib/int64.so \
luaclib/mcast.so \
client
@@ -72,7 +72,7 @@ service/snlua.so : service-src/service_lua.c
gcc $(CFLAGS) $(SHARED) -Iluacompat $^ -o $@ -Iskynet-src
service/gate.so : gate/mread.c gate/ringbuffer.c gate/main.c
gcc $(CFLAGS) $(SHARED) $^ -o $@ -Igate -Iskynet-src
gcc $(CFLAGS) $(SHARED) $^ -o $@ -Igate -Iskynet-src -Iservice-src
service/localcast.so : service-src/service_localcast.c
gcc $(CFLAGS) $(SHARED) $^ -o $@ -Iskynet-src
@@ -83,11 +83,11 @@ luaclib/skynet.so : lualib-src/lua-skynet.c lualib-src/lua-seri.c lualib-src/lua
service/client.so : service-src/service_client.c
gcc $(CFLAGS) $(SHARED) $^ -o $@ -Iskynet-src
service/connection.so : connection/connection.c connection/main.c
gcc $(CFLAGS) $(SHARED) $^ -o $@ -Iskynet-src -Iconnection
service/socket.so : service-src/service_socket.c
gcc $(CFLAGS) $(SHARED) $^ -o $@ -Iskynet-src
luaclib/socket.so : connection/lua-socket.c | luaclib
gcc $(CFLAGS) $(SHARED) -Iluacompat $^ -o $@ -Iskynet-src -Iconnection
luaclib/socketbuffer.so : lualib-src/lua-socket.c | luaclib
gcc $(CFLAGS) $(SHARED) -Iluacompat $^ -o $@ -Iskynet-src
luaclib/int64.so : lua-int64/int64.c | luaclib
gcc $(CFLAGS) $(SHARED) -Iluacompat -O2 $^ -o $@

View File

@@ -1,145 +0,0 @@
#include "connection.h"
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
/* Test for polling API */
#ifdef __linux__
#define HAVE_EPOLL 1
#endif
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
#define HAVE_KQUEUE 1
#endif
#if !defined(HAVE_EPOLL) && !defined(HAVE_KQUEUE)
#error "system does not support epoll or kqueue API"
#endif
/* ! Test for polling API */
#ifdef HAVE_EPOLL
#include <sys/epoll.h>
#elif HAVE_KQUEUE
#include <sys/event.h>
#endif
#define EPOLLQUEUE 32
struct connection_pool {
#ifdef HAVE_EPOLL
int epoll_fd;
#elif HAVE_KQUEUE
int kqueue_fd;
#endif
int queue_len;
int queue_head;
#ifdef HAVE_EPOLL
struct epoll_event ev[EPOLLQUEUE];
#elif HAVE_KQUEUE
struct kevent ev[EPOLLQUEUE];
#endif
};
struct connection_pool *
connection_newpool(int max) {
#ifdef HAVE_EPOLL
int epoll_fd = epoll_create(max);
if (epoll_fd == -1) {
return NULL;
}
#elif HAVE_KQUEUE
int kqueue_fd = kqueue();
if (kqueue_fd == -1) {
return NULL;
}
#endif
struct connection_pool * pool = malloc(sizeof(*pool));
#ifdef HAVE_EPOLL
pool->epoll_fd = epoll_fd;
#elif HAVE_KQUEUE
pool->kqueue_fd = kqueue_fd;
#endif
pool->queue_len = 0;
pool->queue_head = 0;
return pool;
}
void
connection_deletepool(struct connection_pool * pool) {
#if HAVE_EPOLL
close(pool->epoll_fd);
#elif HAVE_KQUEUE
close(pool->kqueue_fd);
#endif
free(pool);
}
int
connection_add(struct connection_pool * pool, int fd, void *ud) {
#if HAVE_EPOLL
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = ud;
if (epoll_ctl(pool->epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
return 1;
}
#elif HAVE_KQUEUE
struct kevent ke;
EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, ud);
if (kevent(pool->kqueue_fd, &ke, 1, NULL, 0, NULL) == -1) {
return 1;
}
#endif
return 0;
}
void
connection_del(struct connection_pool * pool, int fd) {
#if HAVE_EPOLL
struct epoll_event ev;
epoll_ctl(pool->epoll_fd, EPOLL_CTL_DEL, fd , &ev);
#elif HAVE_KQUEUE
struct kevent ke;
EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
kevent(pool->kqueue_fd, &ke, 1, NULL, 0, NULL);
#endif
}
static int
_read_queue(struct connection_pool * pool, int timeout) {
pool->queue_head = 0;
#if HAVE_EPOLL
int n = epoll_wait(pool->epoll_fd , pool->ev, EPOLLQUEUE, timeout);
#elif HAVE_KQUEUE
struct timespec timeoutspec;
timeoutspec.tv_sec = timeout / 1000;
timeoutspec.tv_nsec = (timeout % 1000) * 1000000;
int n = kevent(pool->kqueue_fd, NULL, 0, pool->ev, EPOLLQUEUE, &timeoutspec);
#endif
if (n == -1) {
pool->queue_len = 0;
return -1;
}
pool->queue_len = n;
return n;
}
void *
connection_poll(struct connection_pool * pool, int timeout) {
if (pool->queue_head >= pool->queue_len) {
if (_read_queue(pool, timeout) <= 0) {
return NULL;
}
}
#if HAVE_EPOLL
return pool->ev[pool->queue_head ++].data.ptr;
#elif HAVE_KQUEUE
return pool->ev[pool->queue_head ++].udata;
#endif
}

View File

@@ -1,16 +0,0 @@
#ifndef SKYNET_CONNECTION_H
#define SKYNET_CONNECTION_H
#include <stddef.h>
struct connection_pool;
struct connection_pool * connection_newpool(int max);
void connection_deletepool(struct connection_pool *);
int connection_add(struct connection_pool *, int fd, void *ud);
void connection_del(struct connection_pool *, int fd);
void * connection_poll(struct connection_pool *, int timeout);
#endif

View File

@@ -1,327 +0,0 @@
#include "skynet.h"
#include "luacompat52.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <netinet/in.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <lua.h>
#include <lauxlib.h>
static int
_open(lua_State *L) {
const char * ip = luaL_checkstring(L,1);
int port = luaL_checkinteger(L,2);
struct sockaddr_in my_addr;
memset(&my_addr, 0, sizeof(struct sockaddr_in));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(port);
my_addr.sin_addr.s_addr=inet_addr(ip);
int fd = socket(AF_INET,SOCK_STREAM,0);
int r = connect(fd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr_in));
if (r == -1) {
close(fd);
return 0;
}
lua_pushinteger(L,fd);
return 1;
}
static int
_blockwrite(int fd, const char * buffer, size_t sz) {
while (sz > 0) {
int bytes = send(fd, buffer, sz, 0);
if (bytes < 0) {
switch (errno) {
case EAGAIN:
case EINTR:
continue;
}
return -1;
}
sz -= bytes;
buffer += bytes;
sleep(0);
}
return 0;
}
static int
_write(lua_State *L) {
int fd = -1;
if (!lua_isnil(L,1)) {
fd = luaL_checkinteger(L,1);
}
int type = lua_type(L,2);
const char * buffer = NULL;
size_t sz;
if (type == LUA_TSTRING) {
buffer = lua_tolstring(L,2,&sz);
} else {
luaL_checktype(L,2, LUA_TLIGHTUSERDATA);
buffer = lua_touserdata(L,2);
sz = luaL_checkinteger(L,3);
}
if (fd >= 0) {
if (_blockwrite(fd, buffer, sz) == 0) {
return 0;
}
}
if (type == LUA_TSTRING) {
lua_settop(L,2);
} else {
lua_pushlstring(L, buffer, sz);
}
return 1;
}
static int
_writeblock(lua_State *L) {
luaL_Buffer b;
int fd = -1;
if (!lua_isnil(L,1)) {
fd = luaL_checkinteger(L,1);
}
int header = luaL_checkinteger(L,2);
int type = lua_type(L,3);
const char * buffer = NULL;
size_t sz;
if (type == LUA_TSTRING) {
buffer = lua_tolstring(L,3,&sz);
} else {
luaL_checktype(L, 3, LUA_TLIGHTUSERDATA);
buffer = lua_touserdata(L,3);
sz = luaL_checkinteger(L,4);
}
if (header == 2) {
if (sz > 65535) {
luaL_error(L, "Too big package %d", (int)sz);
}
} else {
if (header != 4) {
luaL_error(L, "block header must be 2 or 4 bytes");
}
}
uint8_t head[4];
if (header == 2) {
// send big-endian header
head[0] = sz >> 8 & 0xff;
head[1] = sz & 0xff ;
} else {
head[0] = sz >> 24 & 0xff;
head[1] = sz >> 16 & 0xff;
head[2] = sz >> 8 & 0xff;
head[3] = sz & 0xff;
header = 4;
}
if (fd >= 0) {
if (_blockwrite(fd, (const char * )head, header)) {
goto _error;
}
if (_blockwrite(fd , buffer, sz)) {
goto _error;
}
return 0;
}
_error:
luaL_buffinitsize(L,&b, header + sz);
luaL_addlstring(&b, (const char *)head, header);
luaL_addlstring(&b, buffer, sz);
luaL_pushresult(&b);
return 1;
}
struct buffer {
int cap;
int size;
char * buffer;
int read;
};
static int
_new(lua_State *L) {
struct buffer * buffer = lua_newuserdata(L, sizeof(struct buffer));
buffer->cap = 0;
buffer->size = 0;
buffer->buffer = NULL;
buffer->read = 0;
return 1;
}
static int
_delete(lua_State *L) {
struct buffer * buffer = lua_touserdata(L,1);
free(buffer->buffer);
buffer->buffer = NULL;
return 0;
}
static int
_push(lua_State *L) {
luaL_checktype(L,1,LUA_TUSERDATA);
struct buffer * buffer = lua_touserdata(L,1);
int type = lua_type(L,2);
const char * buf;
size_t size;
if (type == LUA_TSTRING) {
buf = lua_tolstring(L,2,&size);
} else {
luaL_checktype(L,2,LUA_TLIGHTUSERDATA);
buf = lua_touserdata(L,2);
size = luaL_checkinteger(L,3);
}
int old_size = buffer->size - buffer->read;
if (size + old_size > buffer->cap) {
if (buffer->cap == 0) {
lua_rawgetp(L, LUA_REGISTRYINDEX, _delete);
lua_setmetatable(L, 1);
}
if (buffer->read == 0) {
buffer->buffer = realloc(buffer->buffer, size+old_size);
buffer->cap = size + old_size;
memcpy(buffer->buffer + old_size , buf , size);
buffer->size += size;
} else {
char * new_buffer = malloc(size + old_size);
memcpy(new_buffer, buffer->buffer + buffer->read, old_size);
memcpy(new_buffer + old_size , buf, size);
free(buffer->buffer);
buffer->buffer = new_buffer;
buffer->read = 0;
buffer->size = old_size + size;
}
} else if (buffer->size + size > buffer->cap) {
memmove(buffer->buffer, buffer->buffer + buffer->read, old_size);
memcpy(buffer->buffer + old_size, buf, size);
buffer->read = 0;
buffer->size = old_size + size;
} else {
memcpy(buffer->buffer + buffer->size, buf, size);
buffer->size += size;
}
return 0;
}
static int
_read(lua_State *L) {
luaL_checktype(L,1,LUA_TUSERDATA);
struct buffer * buffer = lua_touserdata(L,1);
int need = luaL_checkinteger(L,2);
int size = buffer->size - buffer->read;
if (need <= size) {
lua_pushlstring(L, buffer->buffer + buffer->read, need);
buffer->read += need;
return 1;
}
return 0;
}
static int
_readline(lua_State *L) {
luaL_checktype(L,1,LUA_TUSERDATA);
struct buffer * buffer = lua_touserdata(L,1);
size_t sz;
const char * sep = luaL_checklstring(L,2,&sz);
int i;
for (i=buffer->read;i<=buffer->size - (int)sz;i++) {
if (memcmp(buffer->buffer+i,sep,sz) == 0) {
lua_pushlstring(L, buffer->buffer + buffer->read, i-buffer->read);
buffer->read = i+sz;
return 1;
}
}
return 0;
}
/*
userdata buffer
intteger header
function (msg, sz, ...)
...
*/
static int
_readblock(lua_State *L) {
luaL_checktype(L,1,LUA_TUSERDATA);
struct buffer * buffer = lua_touserdata(L,1);
int top = lua_gettop(L);
int size = buffer->size - buffer->read;
if (size < 2) {
return 0;
}
int header_size = luaL_checkinteger(L,2);
uint8_t * buf = (uint8_t *)buffer->buffer + buffer->read;
uint16_t len;
if (header_size == 2) {
len = buf[0] << 8 | buf[1];
} else {
if (header_size != 4) {
return luaL_error(L, "header size must be 2 or 4");
}
len = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
}
if (size < header_size + len) {
return 0;
}
if (top == 3) {
lua_pushlightuserdata(L, buffer->buffer + buffer->read + header_size);
lua_pushinteger(L, len);
lua_call(L,2,LUA_MULTRET);
buffer->read += len + header_size;
return lua_gettop(L) - 2;
} else {
lua_pushvalue(L,3);
lua_replace(L,1);
lua_pushlightuserdata(L, buffer->buffer + buffer->read + header_size);
lua_replace(L,2);
lua_pushinteger(L, len);
lua_replace(L,3);
lua_call(L,top-1,LUA_MULTRET);
buffer->read += len + header_size;
return lua_gettop(L);
}
}
int
luaopen_socket_c(lua_State *L) {
luaL_Reg l[] = {
{ "open", _open },
{ "write", _write },
{ "new", _new },
{ "push", _push },
{ "read", _read },
{ "readline", _readline },
{ "readblock", _readblock },
{ "writeblock", _writeblock },
{ NULL, NULL },
};
luaL_checkversion(L);
luaL_newlib(L,l);
lua_createtable(L,0,1);
lua_pushcfunction(L, _delete);
lua_setfield(L, -2, "__gc");
lua_rawsetp(L, LUA_REGISTRYINDEX, _delete);
return 1;
}

View File

@@ -1,204 +0,0 @@
#include "connection.h"
#include "skynet.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define DEFAULT_BUFFER_SIZE 1024
#define DEFAULT_CONNECTION 16
struct connection {
int fd;
uint32_t address;
int close;
};
struct connection_server {
int max_connection;
int current_connection;
struct connection_pool *pool;
struct skynet_context *ctx;
struct connection * conn;
};
struct connection_server *
connection_create(void) {
struct connection_server * server = malloc(sizeof(*server));
memset(server,0,sizeof(*server));
return server;
}
void
connection_release(struct connection_server * server) {
if (server->pool) {
connection_deletepool(server->pool);
}
free(server->conn);
free(server);
}
static void
_expand(struct connection_server * server) {
connection_deletepool(server->pool);
server->pool = connection_newpool(server->max_connection * 2);
int i;
for (i=0;i<server->max_connection;i++) {
struct connection * c = &server->conn[i];
int err = connection_add(server->pool, c->fd , c);
assert(err == 0);
}
server->max_connection *= 2;
}
static void
_add(struct connection_server * server, int fd , uint32_t address) {
++server->current_connection;
if (server->current_connection > server->max_connection) {
_expand(server);
}
int i;
for (i=0;i<server->max_connection;i++) {
struct connection * c = &server->conn[i];
if (c->address == 0) {
c->fd = fd;
c->address = address;
c->close = 0;
int err = connection_add(server->pool, fd , c);
assert(err == 0);
return;
}
}
assert(0);
}
static void
_del(struct connection_server * server, int fd) {
int i;
for (i=0;i<server->max_connection;i++) {
struct connection * c = &server->conn[i];
if (c->fd == fd) {
if (c->close == 0) {
skynet_send(server->ctx, 0, c->address, PTYPE_CLIENT | PTYPE_TAG_DONTCOPY, 0, NULL, 0);
connection_del(server->pool, fd);
}
--server->current_connection;
c->address = 0;
c->fd = 0;
c->close = 0;
close(fd);
return;
}
}
skynet_error(server->ctx, "[connection] Delete invalid handle %d", fd);
}
static void
_poll(struct connection_server * server) {
int timeout = 100;
void * buffer = NULL;
for (;;) {
struct connection * c = connection_poll(server->pool, timeout);
if (c==NULL) {
skynet_command(server->ctx,"TIMEOUT","1");
return;
}
timeout = 0;
if (buffer == NULL) {
buffer = malloc(DEFAULT_BUFFER_SIZE);
}
int size = read(c->fd, buffer, DEFAULT_BUFFER_SIZE);
if (size < 0) {
continue;
}
if (size == 0) {
free(buffer);
buffer = NULL;
if (c->close == 0) {
c->close = 1;
skynet_send(server->ctx, 0, c->address, PTYPE_CLIENT | PTYPE_TAG_DONTCOPY, 0, NULL, 0);
connection_del(server->pool, c->fd);
}
} else {
skynet_send(server->ctx, 0, c->address, PTYPE_CLIENT | PTYPE_TAG_DONTCOPY, 0, buffer, size);
buffer = NULL;
}
}
}
static int
_connection_main(struct skynet_context * ctx, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
if (type == PTYPE_RESPONSE) {
_poll(ud);
return 0;
}
assert(type == PTYPE_TEXT);
const char * param = (const char *)msg + 4;
if (memcmp(msg, "ADD ", 4)==0) {
char * endptr;
int fd = strtol(param, &endptr, 10);
if (endptr == NULL) {
skynet_error(ctx, "[connection] Invalid ADD command from %x (session = %d)", source, session);
return 0;
}
int addr_sz = sz - (endptr - (char *)msg);
if (addr_sz <= 1) {
skynet_error(ctx, "[connection] Invalid ADD command from %x (session = %d)", source, session);
return 0;
}
char addr [addr_sz];
memcpy(addr, endptr+1, addr_sz-1);
addr[addr_sz-1] = '\0';
uint32_t address = strtoul(addr+1, NULL, 16);
if (address == 0) {
skynet_error(ctx, "[connection] Invalid ADD command from %x (session = %d)", source, session);
return 0;
}
_add(ud, fd, address);
} else if (memcmp(msg, "DEL ", 4)==0) {
char * endptr;
int fd = strtol(param, &endptr, 10);
if (endptr == NULL) {
skynet_error(ctx, "[connection] Invalid DEL command from %x (session = %d)", source, session);
return 0;
}
_del(ud, fd);
} else {
skynet_error(ctx, "[connection] Invalid command from %x (session = %d)", source, session);
}
return 0;
}
int
connection_init(struct connection_server * server, struct skynet_context * ctx, char * param) {
server->pool = connection_newpool(DEFAULT_CONNECTION);
if (server->pool == NULL)
return 1;
server->max_connection = strtol(param, NULL, 10);
if (server->max_connection == 0) {
server->max_connection = DEFAULT_CONNECTION;
}
server->current_connection = 0;
server->ctx = ctx;
server->conn = malloc(server->max_connection * sizeof(struct connection));
memset(server->conn, 0, server->max_connection * sizeof(struct connection));
skynet_callback(ctx, server, _connection_main);
skynet_command(ctx,"REG",".connection");
skynet_command(ctx,"TIMEOUT","0");
return 0;
}

View File

@@ -1,28 +1,6 @@
#include "mread.h"
#include "ringbuffer.h"
/* Test for polling API */
#ifdef __linux__
#define HAVE_EPOLL 1
#endif
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
#define HAVE_KQUEUE 1
#endif
#if !defined(HAVE_EPOLL) && !defined(HAVE_KQUEUE)
#error "system does not support epoll or kqueue API"
#endif
/* ! Test for polling API */
#include <sys/types.h>
#ifdef HAVE_EPOLL
#include <sys/epoll.h>
#elif HAVE_KQUEUE
#include <sys/event.h>
#endif
#include "event.h"
#include <netinet/in.h>
#include <sys/socket.h>
@@ -37,7 +15,6 @@
#include <fcntl.h>
#define BACKLOG 32
#define READQUEUE 32
#define READBLOCKSIZE 2048
#define RINGBUFFER_DEFAULT 1024 * 1024
@@ -75,11 +52,7 @@ struct socket {
struct mread_pool {
int listen_fd;
#ifdef HAVE_EPOLL
int epoll_fd;
#elif HAVE_KQUEUE
int kqueue_fd;
#endif
int efd;
int max_connection;
int closed;
int active;
@@ -88,11 +61,7 @@ struct mread_pool {
struct socket * free_socket;
int queue_len;
int queue_head;
#ifdef HAVE_EPOLL
struct epoll_event ev[READQUEUE];
#elif HAVE_KQUEUE
struct kevent ev[READQUEUE];
#endif
struct event ev[MAX_EVENT];
struct ringbuffer * rb;
};
@@ -103,16 +72,7 @@ turn_on(struct mread_pool *self, struct socket * s) {
if (s->status < SOCKET_ALIVE || s->enablewrite) {
return;
}
#ifdef HAVE_EPOLL
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLOUT;
ev.data.ptr = s;
epoll_ctl(self->epoll_fd, EPOLL_CTL_MOD, s->fd, &ev);
#elif HAVE_KQUEUE
struct kevent ke;
EV_SET(&ke, s->fd, EVFILT_WRITE, EV_ENABLE, 0, 0, s);
kevent(self->kqueue_fd, &ke, 1, NULL, 0, NULL);
#endif
event_write(self->efd, s->fd, s, true);
s->enablewrite = 1;
}
@@ -122,19 +82,9 @@ turn_off(struct mread_pool *self, struct socket * s) {
return;
}
s->enablewrite = 0;
#ifdef HAVE_EPOLL
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = s;
epoll_ctl(self->epoll_fd, EPOLL_CTL_MOD, s->fd, &ev);
#elif HAVE_KQUEUE
struct kevent ke;
EV_SET(&ke, s->fd, EVFILT_WRITE, EV_DISABLE, 0, 0, s);
kevent(self->kqueue_fd, &ke, 1, NULL, 0, NULL);
#endif
event_write(self->efd, s->fd, s, false);
}
static void
free_buffer(struct send_client * sc) {
struct send_buffer * sb = sc->head;
@@ -301,46 +251,22 @@ mread_create(uint32_t addr, int port , int max , int buffer_size) {
return NULL;
}
#ifdef HAVE_EPOLL
int epoll_fd = epoll_create(max + 1);
if (epoll_fd == -1) {
int efd = event_init(max+1);
if (efd == -1) {
close(listen_fd);
return NULL;
}
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = LISTENSOCKET;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev) == -1) {
if (event_add(efd, listen_fd, LISTENSOCKET) == -1) {
close(listen_fd);
close(epoll_fd);
close(efd);
return NULL;
}
#elif HAVE_KQUEUE
int kqueue_fd = kqueue();
if (kqueue_fd == -1) {
close(listen_fd);
return NULL;
}
struct kevent ke;
EV_SET(&ke, listen_fd, EVFILT_READ, EV_ADD, 0, 0, LISTENSOCKET);
if (kevent(kqueue_fd, &ke, 1, NULL, 0, NULL) == -1) {
close(listen_fd);
close(kqueue_fd);
return NULL;
}
#endif
struct mread_pool * self = malloc(sizeof(*self));
self->listen_fd = listen_fd;
#ifdef HAVE_EPOLL
self->epoll_fd = epoll_fd;
#elif HAVE_KQUEUE
self->kqueue_fd = kqueue_fd;
#endif
self->efd = efd;
self->max_connection = max;
self->closed = 0;
self->active = -1;
@@ -373,11 +299,7 @@ mread_close(struct mread_pool *self) {
free_buffer(&s->client);
free(s);
mread_close_listen(self);
#ifdef HAVE_EPOLL
close(self->epoll_fd);
#elif HAVE_KQUEUE
close(self->kqueue_fd);
#endif
close(self->efd);
_release_rb(self->rb);
free(self);
}
@@ -396,14 +318,7 @@ mread_close_listen(struct mread_pool *self) {
static int
_read_queue(struct mread_pool * self, int timeout) {
self->queue_head = 0;
#ifdef HAVE_EPOLL
int n = epoll_wait(self->epoll_fd , self->ev, READQUEUE, timeout);
#elif HAVE_KQUEUE
struct timespec timeoutspec;
timeoutspec.tv_sec = timeout / 1000;
timeoutspec.tv_nsec = (timeout % 1000) * 1000000;
int n = kevent(self->kqueue_fd, NULL, 0, self->ev, READQUEUE, &timeoutspec);
#endif
int n = event_wait(self->efd, self->ev, timeout);
if (n == -1) {
self->queue_len = 0;
return -1;
@@ -424,13 +339,7 @@ try_close(struct mread_pool * self, struct socket * s) {
s->status = SOCKET_CLOSED;
s->node = NULL;
s->temp = NULL;
#ifdef HAVE_EPOLL
epoll_ctl(self->epoll_fd, EPOLL_CTL_DEL, s->fd , NULL);
#elif HAVE_KQUEUE
struct kevent ke;
EV_SET(&ke, s->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
kevent(self->kqueue_fd, &ke, 1, NULL, 0, NULL);
#endif
event_del(self->efd, s->fd);
close(s->fd);
// printf("MREAD close %d (fd=%d)\n",id,s->fd);
++self->closed;
@@ -443,20 +352,9 @@ _read_one(struct mread_pool * self) {
if (self->queue_head >= self->queue_len) {
return NULL;
}
struct socket * ret = NULL;
int writeflag = 0;
int readflag = 0;
#ifdef HAVE_EPOLL
ret = self->ev[self->queue_head].data.ptr;
uint32_t flag = self->ev[self->queue_head].events;
writeflag = flag & EPOLLOUT;
readflag = flag & EPOLLIN;
#elif HAVE_KQUEUE
ret = self->ev[self->queue_head].udata;
short flag = self->ev[self->queue_head].filter;
writeflag = flag & EVFILT_WRITE;
readflag = flag & EVFILT_READ;
#endif
struct socket * ret = self->ev[self->queue_head].s;
bool writeflag = self->ev[self->queue_head].write;
bool readflag = self->ev[self->queue_head].read;
++ self->queue_head;
if (ret == LISTENSOCKET) {
return ret;
@@ -493,23 +391,10 @@ _add_client(struct mread_pool * self, int fd) {
close(fd);
return NULL;
}
#ifdef HAVE_EPOLL
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = s;
if (epoll_ctl(self->epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
if (event_add(self->efd, fd, s)) {
close(fd);
return NULL;
}
#elif HAVE_KQUEUE
struct kevent ke;
EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, s);
if (kevent(self->kqueue_fd, &ke, 1, NULL, 0, NULL) == -1) {
close(fd);
return NULL;
}
#endif
s->fd = fd;
s->node = NULL;
s->status = SOCKET_SUSPEND;

250
lualib-src/lua-socket.c Normal file
View File

@@ -0,0 +1,250 @@
#include <stdlib.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include "luacompat52.h"
struct socket_buffer {
int size;
int head;
int tail;
};
static struct socket_buffer *
new_buffer(lua_State *L, int sz) {
struct socket_buffer * buffer = lua_newuserdata(L, sizeof(*buffer) + sz);
buffer->size = sz;
buffer->head = 0;
buffer->tail = 0;
return buffer;
}
static void
copy_buffer(struct socket_buffer * dest, struct socket_buffer * src) {
char * ptr = (char *)(src+1);
if (src->tail >= src->head) {
int sz = src->tail - src->head;
memcpy(dest+1, ptr+ src->head, sz);
dest->tail = sz;
} else {
char * d = (char *)(dest+1);
int part = src->size - src->head;
memcpy(d, ptr + src->head, part);
memcpy(d + part, ptr , src->tail);
dest->tail = src->tail + part;
}
}
static void
append_buffer(struct socket_buffer * buffer, char * msg, int sz) {
char * dst = (char *)(buffer + 1);
if (sz + buffer->tail < buffer->size) {
memcpy(dst + buffer->tail , msg, sz);
buffer->tail += sz;
} else {
int part = buffer->size - buffer->tail;
memcpy(dst + buffer->tail , msg, part);
memcpy(dst, msg + part, sz - part);
buffer->tail = sz - part;
}
}
static int
lpop(lua_State *L) {
struct socket_buffer * buffer = lua_touserdata(L, 1);
if (buffer == NULL) {
return 0;
}
int sz = luaL_checkinteger(L, 2);
int bytes = buffer->tail - buffer->head;
if (bytes < 0) {
bytes += buffer->size;
}
if (sz > bytes || bytes == 0) {
lua_pushnil(L);
lua_pushinteger(L, bytes);
return 2;
}
if (sz == 0) {
sz = bytes;
}
char * ptr = (char *)(buffer+1);
if (buffer->size - buffer->head >=sz) {
lua_pushlstring(L, ptr + buffer->head, sz);
buffer->head+=sz;
} else {
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addlstring(&b, ptr + buffer->head, buffer->size - buffer->head);
buffer->head = sz - (buffer->size - buffer->head);
luaL_addlstring(&b, ptr, buffer->head);
luaL_pushresult(&b);
}
bytes -= sz;
lua_pushinteger(L,bytes);
if (bytes == 0) {
buffer->head = buffer->tail = 0;
}
return 2;
}
static inline int
check_sep(struct socket_buffer *buffer, int from, const char * sep, int sz) {
const char * ptr = (const char *)(buffer+1);
int i;
for (i=0;i<sz;i++) {
int index = from + i;
if (index >= buffer->size) {
index = 0;
}
if (ptr[index] != sep[i]) {
return 0;
}
}
return 1;
}
static int
lreadline(lua_State *L) {
struct socket_buffer * buffer = lua_touserdata(L, 1);
if (buffer == NULL) {
return 0;
}
size_t len = 0;
const char *sep = luaL_checklstring(L,2,&len);
int read = !lua_toboolean(L,3);
int bytes = buffer->tail - buffer->head;
if (bytes < 0) {
bytes += buffer->size;
}
int i;
for (i=0;i<=bytes-(int)len;i++) {
int index = buffer->head + i;
if (index >= buffer->size) {
index -= buffer->size;
}
if (check_sep(buffer, index, sep, (int)len)) {
if (read == 0) {
lua_pushboolean(L,1);
} else {
if (i==0) {
lua_pushlstring(L, "", 0);
} else {
const char * ptr = (const char *)(buffer+1);
if (--index < 0) {
index = buffer->size -1;
}
if (index < buffer->head) {
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addlstring(&b, ptr + buffer->head, buffer->size-buffer->head);
luaL_addlstring(&b, ptr, index+1);
luaL_pushresult(&b);
} else {
lua_pushlstring(L, ptr + buffer->head, index-buffer->head+1);
}
++index;
}
index+=len;
if (index >= buffer->size) {
index-=buffer->size;
}
buffer->head = index;
}
return 1;
}
}
return 0;
}
static int
lpush(lua_State *L) {
struct socket_buffer * buffer = lua_touserdata(L, 1);
int bytes = 0;
if (buffer) {
bytes = buffer->tail - buffer->head;
if (bytes < 0) {
bytes += buffer->size;
}
}
void * msg = lua_touserdata(L,2);
if (msg == NULL) {
lua_settop(L,1);
lua_pushinteger(L,bytes);
return 2;
}
int sz = luaL_checkinteger(L,3);
if (buffer == NULL) {
struct socket_buffer * nbuf = new_buffer(L, sz * 2);
append_buffer(nbuf, msg, sz);
} else if (sz + bytes >= buffer->size) {
struct socket_buffer * nbuf = new_buffer(L, (sz + bytes) * 2);
copy_buffer(nbuf, buffer);
append_buffer(nbuf, msg, sz);
} else {
lua_settop(L,1);
append_buffer(buffer, msg, sz);
}
lua_pushinteger(L, sz + bytes);
return 2;
}
static int
lunpack(lua_State *L) {
int * msg = lua_touserdata(L,1);
int sz = luaL_checkinteger(L,2);
if (msg == NULL || sz < 4) {
return luaL_error(L, "Invalid socket message");
}
lua_pushinteger(L, *msg);
lua_pushlightuserdata(L, msg+1);
lua_pushinteger(L, sz - 4);
return 3;
}
static int
lpack(lua_State *L) {
int fd = luaL_checkinteger(L, 1);
const void *buffer;
size_t sz;
if (lua_isuserdata(L,2)) {
buffer = lua_touserdata(L,2);
sz = luaL_checkinteger(L, 3);
} else {
buffer = luaL_checklstring(L,2,&sz);
}
int * b = malloc(4 + sz);
*b = fd;
memcpy(b+1, buffer, sz);
lua_pushlightuserdata(L, b);
lua_pushinteger(L, 4+sz);
return 2;
}
int
luaopen_socketbuffer(lua_State *L) {
luaL_checkversion(L);
luaL_Reg l[] = {
{ "push", lpush },
{ "pop", lpop },
{ "readline", lreadline },
{ "unpack", lunpack },
{ "pack", lpack },
{ NULL, NULL },
};
luaL_newlib(L,l);
return 1;
}

View File

@@ -1,65 +1,168 @@
local skynet = require "skynet"
local string = string
local table = table
local unpack = unpack
local assert = assert
local socket = require "socket"
local config = require "config"
local redis_conf = skynet.getenv "redis"
local name = config (redis_conf)
local redis_manager
local batch = false
local readline = socket.readline
local readbytes = socket.read
local table = table
local string = string
local redis = {}
local command = {}
local meta = {
__index = command,
__gc = function(self)
socket.close(self.__handle)
end,
}
function redis.connect(dbname)
local fd = socket.open(name[dbname])
return setmetatable( { __handle = fd, __mode = false }, meta )
end
function command:disconnect()
socket.close(self.__handle)
setmetatable(self, nil)
end
local function compose_message(msg)
local lines = { "*" .. #msg }
for _,v in ipairs(msg) do
local t = type(v)
if t == "number" then
v = tostring(v)
elseif t == "userdata" then
v = int64.tostring(int64.new(v),10)
end
table.insert(lines,"$"..#v)
table.insert(lines,v)
end
table.insert(lines,"")
local cmd = table.concat(lines,"\r\n")
return cmd
end
local redcmd = {}
redcmd[42] = function(fd, data) -- '*'
local n = tonumber(data)
if n < 0 then
return true, nil
end
local bulk = {}
for i = 1,n do
local line = readline(fd,"\r\n")
local bytes = tonumber(string.sub(line,2))
if bytes < 0 then
table.insert(bulk, nil)
else
local data = readbytes(fd, bytes + 2)
table.insert(bulk, string.sub(data,1,-3))
end
end
return true, bulk
end
redcmd[36] = function(fd, data) -- '$'
local bytes = tonumber(data)
if bytes < 0 then
return true,nil
end
local firstline = readbytes(fd, bytes+2)
return true,string.sub(firstline,1,-3)
end
redcmd[43] = function(fd, data) -- '+'
return true,data
end
redcmd[45] = function(fd, data) -- '-'
return false,data
end
redcmd[58] = function(fd, data) -- ':'
-- todo: return string later
return true, tonumber(data)
end
local function read_response(fd)
local result = readline(fd, "\r\n")
local firstchar = string.byte(result)
local data = string.sub(result,2)
return redcmd[firstchar](fd,data)
end
setmetatable(command, { __index = function(t,k)
local cmd = string.upper(k)
local f = function(self, ...)
if batch then
skynet.send( self.__handle, "lua", cmd , ...)
local f = function (self, ...)
local fd = self.__handle
if self.__mode then
socket.write(fd, compose_message { cmd, ... })
self.__batch = self.__batch + 1
else
local err, result = skynet.call( self.__handle, "lua", cmd, ...)
assert(err, result)
return result
socket.lock(fd)
socket.write(fd, compose_message { cmd, ... })
local ok, ret = read_response(fd)
socket.unlock(fd)
return ok, ret
end
end
t[k] = f
t[k] = f
return f
end})
function command:exists(key)
assert(not batch, "exists can't used in batch mode")
local result , exists = skynet.call( self.__handle, "lua" , "EXISTS", key)
assert(result, exists)
assert(not self.__mode, "exists can't used in batch mode")
local fd = self.__handle
socket.lock(fd)
socket.write(fd, compose_message { "EXISTS", key })
local ok, exists = read_response(fd)
socket.unlock(fd)
assert(ok, exists)
exists = exists ~= 0
return exists
end
function command:batch(mode)
if mode == "end" then
assert(batch, "Open batch mode first")
batch = false
local err, result = skynet.unpack(skynet.rawcall( self.__handle, "text", mode))
assert(err, result)
return result
local fd = self.__handle
if self.__mode == "read" then
local allok = true
local allret = {}
for i = 1, self.__batch do
local ok, ret = read_response(fd)
allok = allok and ok
allret[i] = ret
end
assert(allok, "batch read failed")
socket.unlock(self.__handle)
self.__mode = false
return allret
else
local allok = true
local allret = true
for i = 1, self.__batch do
local ok, ret = read_response(fd)
allok = allok and ok
if ret~="OK" then
allret = false
end
end
socket.unlock(self.__handle)
self.__mode = false
assert(allret, "reading in batch write")
return allok
end
else
assert(mode == "read" or mode == "write")
batch = mode
skynet.send( self.__handle, "text", mode)
self.__mode = mode
self.__batch = 0
socket.lock(self.__handle)
end
end
local meta = {
__index = command
}
function redis.connect(dbname)
local handle = skynet.call(redis_manager, "lua", dbname)
assert(handle ~= nil)
return setmetatable({ __handle = handle } , meta)
end
skynet.init(function()
redis_manager = skynet.uniqueservice("redis-mgr")
end, "redis")
return redis

View File

@@ -1,79 +1,189 @@
local buffer = require "socketbuffer"
local skynet = require "skynet"
local c = require "socket.c"
local table = table
local next = next
local assert = assert
local coroutine = coroutine
local type = type
local READBUF = {} -- fd:buffer
local READREQUEST = {} -- fd:request_size
local READSESSION = {} -- fd:session
local READLOCK = {} -- fd:queue(session)
local READTHREAD= {} -- fd:thread
local selfaddr = skynet.self()
local function response(session)
skynet.redirect(selfaddr , 0, "response", session, "")
end
skynet.register_protocol {
name = "client",
id = 3, -- PTYPE_CLIENT
pack = buffer.pack,
unpack = buffer.unpack,
dispatch = function (_, _, fd, msg, sz)
local qsz = READREQUEST[fd]
local buf = READBUF[fd]
local bsz
if sz == 0 or buf == true then
buf,bsz = true, qsz
else
buf,bsz = buffer.push(buf, msg, sz)
end
READBUF[fd] = buf
if qsz == nil then
return
end
if type(qsz) == "number" then
if qsz > bsz then
return
end
else
-- request readline
if buffer.readline(buf, qsz, true) == nil then
return
end
end
response(READSESSION[fd])
end
}
skynet.register_protocol {
name = "system",
id = 4, -- PTYPE_SYSTEM
pack = skynet.pack,
unpack = function (...) return ... end,
dispatch = function (session, addr, msg, sz)
fd, t, sz = skynet.unpack(msg,sz)
assert(addr == selfaddr, "PTYPE_SYSTEM message must send by self")
if t == 0 then
-- lock fd
if READTHREAD[fd] == nil then
skynet.ret()
return
end
local q = READLOCK[fd]
if q == nil then
READLOCK[fd] = { session }
else
table.insert(q, session)
end
else
-- request bytes or readline
local buf = READBUF[fd]
if buf == true then
skynet.ret()
return
end
local _,bsz = buffer.push(buf)
if t == 1 then
-- sz is size
if bsz >= sz then
skynet.ret()
return
end
else
-- sz is sep
if buffer.readline(buf, sz, true) then -- don't real read
skynet.ret()
return
end
end
READSESSION[fd] = session
end
end
}
local socket = {}
local fd
local object
local data = {}
local function presend()
if next(data) then
local tmp = data
data = {}
for _,v in ipairs(tmp) do
socket.write(fd, v)
function socket.open(addr, port)
local cmd = "open" .. " " .. (port and (addr..":"..port) or addr)
local r = skynet.call(".socket", "text", cmd)
if r == "" then
error(cmd .. " failed")
end
return tonumber(r)
end
function socket.stdin()
local r = skynet.call(".socket", "text", "bind 1")
if r == "" then
error("stdin bind failed")
end
return tonumber(r)
end
function socket.close(fd)
socket.lock(fd)
skynet.call(".socket", "text", "close", fd)
READBUF[fd] = true
READLOCK[fd] = nil
end
function socket.read(fd, sz)
assert(coroutine.running() == READTHREAD[fd], "call socket.lock first")
local str = buffer.pop(READBUF[fd],sz)
if str then
return str
end
READREQUEST[fd] = sz
skynet.call(selfaddr, "system",fd,1,sz) -- singal size 1
READREQUEST[fd] = nil
str = buffer.pop(READBUF[fd],sz)
return str
end
function socket.readline(fd, sep)
assert(coroutine.running() == READTHREAD[fd], "call socket.lock first")
local str = buffer.readline(READBUF[fd],sep)
if str then
return str
end
READREQUEST[fd] = sep
skynet.call(selfaddr, "system",fd,2,sep) -- singal sep 2
READREQUEST[fd] = nil
str = buffer.readline(READBUF[fd],sep)
return str
end
function socket.write(fd, msg, sz)
assert(coroutine.running() == READTHREAD[fd], "call socket.lock first")
skynet.send(".socket", "client", fd, msg, sz)
end
function socket.lock(fd)
local lt = READTHREAD[fd]
local ct = coroutine.running()
if lt then
assert(lt ~= ct, "already lock")
skynet.call(selfaddr, "system",fd,0)
end
READTHREAD[fd] = ct
end
function socket.unlock(fd)
READTHREAD[fd] = nil
local q = READLOCK[fd]
if q then
if q[1] then
response(q[1])
end
table.remove(q,1)
if q[1] == nil then
READLOCK[fd] = nil
end
end
end
function socket.connect(addr)
local ip, port = string.match(addr,"([^:]+):(.+)")
port = tonumber(port)
socket.close()
fd = c.open(ip,port)
if fd == nil then
return true
end
skynet.send(".connection", "text", "ADD", fd , skynet.address(skynet.self()))
object = c.new()
presend()
end
function socket.stdin()
skynet.send(".connection", "text", "ADD", 1 , skynet.address(skynet.self()))
object = c.new()
end
function socket.push(msg,sz)
if msg then
c.push(object, msg, sz)
end
end
function socket.read(bytes)
return c.read(object, bytes)
end
function socket.readline(sep)
return c.readline(object, sep)
end
function socket.readblock(...)
return c.readblock(object,...)
end
function socket.write(...)
local str = c.write(fd, ...)
if str then
socket.close()
table.insert(data, str)
end
end
function socket.writeblock(...)
local str = c.writeblock(fd, ...)
if str then
socket.close()
table.insert(data, str)
end
end
function socket.close()
if fd then
skynet.send(".connection","text", "DEL", fd)
fd = nil
end
end
return socket

136
service-src/event.h Normal file
View File

@@ -0,0 +1,136 @@
#ifndef skynet_event_h
#define skynet_event_h
#include <stdbool.h>
#include <stdlib.h>
/* Test for polling API */
#ifdef __linux__
#define HAVE_EPOLL 1
#endif
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
#define HAVE_KQUEUE 1
#endif
#if !defined(HAVE_EPOLL) && !defined(HAVE_KQUEUE)
#error "system does not support epoll or kqueue API"
#endif
/* ! Test for polling API */
#include <sys/types.h>
#ifdef HAVE_EPOLL
#include <sys/epoll.h>
#elif HAVE_KQUEUE
#include <sys/event.h>
#endif
#ifdef HAVE_EPOLL
#define MAX_EVENT 32
struct event {
void * s;
bool read;
bool write;
};
static int
event_init(int max) {
return epoll_create(max);
}
static int
event_add(int efd, int sock, void *ud) {
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = ud;
if (epoll_ctl(efd, EPOLL_CTL_ADD, sock, &ev) == -1) {
return 1;
}
return 0;
}
static void
event_del(int efd, int fd) {
epoll_ctl(efd, EPOLL_CTL_DEL, fd , NULL);
}
static int
event_wait(int efd, struct event * e, int timeout) {
struct epoll_event ev[MAX_EVENT];
int n = epoll_wait(efd , ev, MAX_EVENT, timeout);
int i;
for (i=0;i<n;i++) {
e[i].s = ev[i].data.ptr;
unsigned flag = ev[i].events;
e[i].write = (flag & EPOLLOUT) != 0;
e[i].read = (flag & EPOLLIN) != 0;
}
return n;
}
static void
event_write(int efd, int fd, void *ud, bool enable) {
struct epoll_event ev;
ev.events = EPOLLIN | (enable ? EPOLLOUT : 0);
ev.data.ptr = ud;
epoll_ctl(efd, EPOLL_CTL_MOD, fd, &ev);
}
#elif HAVE_KQUEUE
static int
event_init(int max) {
return kqueue();
}
static int
event_add(int efd, int sock, void *ud) {
struct kevent ke;
EV_SET(&ke, sock, EVFILT_READ, EV_ADD, 0, 0, ud);
if (kevent(efd, &ke, 1, NULL, 0, NULL) == -1) {
return 1;
}
return 0;
}
static void
event_del(int efd, int fd) {
struct kevent ke;
EV_SET(&ke, fd, EVFILT_READ | EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
kevent(efd, &ke, 1, NULL, 0, NULL);
}
static int
event_wait(int kfd, struct event *e, int timeout) {
struct timespec timeoutspec;
timeoutspec.tv_sec = timeout / 1000;
timeoutspec.tv_nsec = (timeout % 1000) * 1000000;
struct kevent ev[MAX_EVENT];
int n = kevent(kfd, NULL, 0, ev, MAX_EVENT, &timeoutspec);
int i;
for (i=0;i<n;i++) {
e[i].s = ev[i].udata;
unsigned flag = ev[i].filter;
e[i].write = (flag & EVFILT_WRITE) != 0;
e[i].read = (flag & EVFILT_READ) != 0;
}
return n;
}
static void
event_write(int efd, int fd, void *ud, bool enable) {
struct kevent ke;
EV_SET(&ke, fd, EVFILT_WRITE, enable ? EV_ENABLE : EV_DISABLE, 0, 0, ud);
kevent(efd, &ke, 1, NULL, 0, NULL);
}
#endif
#endif

View File

@@ -0,0 +1,439 @@
#include "skynet.h"
#include "event.h"
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#define MAX_ID 0x7fffffff
#define MAX_CONNECTION 256
#define READ_BUFFER 4000
#define STATUS_INVALID 0
#define STATUS_HALFCLOSE 1
#define STATUS_SUSPEND 2
struct write_buffer {
struct write_buffer * next;
char *ptr;
size_t sz;
void *buffer;
};
struct socket {
int fd;
int id;
uint32_t source;
int status;
int session;
struct write_buffer * head;
struct write_buffer * tail;
};
struct socket_pool {
int fd;
struct event ev[MAX_EVENT];
int poll;
int id;
int count;
int cap;
struct socket * s;
};
static void
reply(struct skynet_context * ctx, uint32_t source, int session, char * cmd, int sz) {
if (sz < 0) {
sz = strlen(cmd);
}
skynet_send(ctx, 0, source, PTYPE_RESPONSE, session, cmd, sz);
}
static int
_set_nonblocking(int fd)
{
int flag = fcntl(fd, F_GETFL, 0);
if ( -1 == flag ) {
return -1;
}
return fcntl(fd, F_SETFL, flag | O_NONBLOCK);
}
static int
new_socket(struct socket_pool *p, int sock, uint32_t addr) {
int i;
if (p->count >= p->cap) {
goto _error;
}
for (i=0;i<p->cap;i++) {
int id = p->id + i;
int n = id % p->cap;
struct socket * s = &p->s[n];
if (s->status == STATUS_INVALID) {
if (event_add(p->fd, sock, s)) {
goto _error;
}
s->status = STATUS_SUSPEND;
_set_nonblocking(sock);
int keepalive = 1;
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive));
s->fd = sock;
s->id = id;
s->source = addr;
p->count++;
if (++p->id > MAX_ID) {
p->id = 1;
}
assert(s->head == NULL && s->tail == NULL);
return id;
}
}
_error:
close(sock);
return -1;
}
static void
cmd_bind(struct skynet_context * ctx, struct socket_pool *p, int sock, int session, uint32_t source) {
char ret[10];
int id = new_socket(p, sock, source);
if (id<0) {
reply(ctx, source, session, NULL , 0);
return;
}
if (p->count == 1) {
skynet_command(ctx, "TIMEOUT", "0");
}
int sz = sprintf(ret,"%d",id);
reply(ctx, source, session, ret, sz);
}
static void
cmd_open(struct skynet_context * ctx, struct socket_pool *p, char * cmd, int session, uint32_t source) {
int status;
struct addrinfo ai_hints;
struct addrinfo *ai_list = NULL;
struct addrinfo *ai_ptr = NULL;
char * host = strsep(&cmd, ":");
if (cmd == NULL) {
goto _failed;
}
memset( &ai_hints, 0, sizeof( ai_hints ) );
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_STREAM;
ai_hints.ai_protocol = IPPROTO_TCP;
status = getaddrinfo( host, cmd, &ai_hints, &ai_list );
if ( status != 0 ) {
goto _failed;
}
int sock= -1;
for ( ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next ) {
sock = socket( ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol );
if ( sock < 0 ) {
continue;
}
status = connect( sock, ai_ptr->ai_addr, ai_ptr->ai_addrlen );
if ( status != 0 ) {
close(sock);
sock = -1;
continue;
}
break;
}
freeaddrinfo( ai_list );
if (sock < 0) {
goto _failed;
}
cmd_bind(ctx, p, sock, session, source);
return;
_failed:
reply(ctx, source, session, NULL , 0);
}
static void
force_close(struct socket *s, struct socket_pool *p) {
struct write_buffer *wb = s->head;
while (wb) {
struct write_buffer *tmp = wb;
wb = wb->next;
free(tmp->buffer);
free(tmp);
}
s->head = s->tail = NULL;
s->status = STATUS_INVALID;
event_del(p->fd, s->fd);
close(s->fd);
--p->count;
}
static void
cmd_close(struct skynet_context * ctx, struct socket_pool *p, int id, int session, uint32_t source) {
struct socket * s = &p->s[id % p->cap];
if (id != s->id) {
skynet_error(ctx, "%x close invalid socket %d", source, id);
reply(ctx, source, session, "invalid", -1);
return;
}
if (source != s->source) {
skynet_error(ctx, "%x try to close socket %d:%x", source, id, s->source);
reply(ctx, source, session, "permission", -1);
return;
}
if (s->head == NULL) {
force_close(s,p);
reply(ctx, source, session, NULL, 0);
} else {
s->status = STATUS_HALFCLOSE;
s->session = session;
}
}
static void
_ctrl(struct skynet_context * ctx, struct socket_pool *p, char * command, int id, char * arg, int session, uint32_t source) {
if (strcmp(command, "open")==0) {
cmd_open(ctx, p, arg, session, source);
} else if (strcmp(command, "close")==0) {
cmd_close(ctx, p, id, session, source);
} else if (strcmp(command, "bind")==0) {
cmd_bind(ctx, p, id, session, source);
} else {
skynet_error(ctx, "Unknown command %s", command);
reply(ctx, source, session, NULL, 0);
}
}
static char *
parser(const char * msg, int sz, char * buffer, int *id) {
int i;
for (i=0;i<sz && msg[i] && msg[i]!=' ';i++) {
if (msg[i] != ' ') {
buffer[i] = msg[i];
}
}
buffer[i] = '\0';
if (i+1 < sz) {
*id = strtol(msg+i+1, NULL, 10);
memcpy(buffer+i+1,msg+i+1, sz-i-1);
}
buffer[sz] = '\0';
return buffer+i+1;
}
static void
forward(struct skynet_context * context, struct socket *s, struct socket_pool *p) {
int * buffer = malloc(READ_BUFFER + sizeof(int));
*buffer = s->id; // convert endian ?
int r = 0;
for (;;) {
r = read(s->fd, buffer+1, READ_BUFFER);
if (r == -1) {
switch(errno) {
case EWOULDBLOCK:
free(buffer);
return;
case EINTR:
continue;
}
r = 0;
break;
}
break;
}
if (r == 0) {
force_close(s,p);
}
if (s->status == STATUS_HALFCLOSE) {
free(buffer);
} else {
skynet_send(context, 0, s->source, PTYPE_CLIENT, 0, buffer, r + 4);
}
}
static void
sendout(struct socket_pool *p, struct socket *s) {
while (s->head) {
struct write_buffer * tmp = s->head;
for (;;) {
int sz = write(s->fd, tmp->ptr, tmp->sz);
if (sz < 0) {
switch(errno) {
case EINTR:
continue;
case EAGAIN:
return;
}
force_close(s,p);
return;
}
if (sz != tmp->sz) {
tmp->ptr += sz;
tmp->sz -= sz;
return;
}
break;
}
s->head = tmp->next;
free(tmp->buffer);
free(tmp);
}
s->tail = NULL;
event_write(p->fd, s->fd, s, false);
}
static int
try_send(struct skynet_context *ctx, struct socket_pool *p, uint32_t source, const int * msg, size_t sz) {
if (sz < 4) {
skynet_error(ctx, "%x invalid message", source);
return 0;
}
sz-=4;
int id = *msg;
struct socket * s = &p->s[id % p->cap];
if (id != s->id) {
skynet_error(ctx, "%x write to invalid socket %d", source, id);
return 0;
}
if (source != s->source) {
skynet_error(ctx, "%x try to write socket %d:%x", source, id, s->source);
return 0;
}
if (s->status != STATUS_SUSPEND) {
skynet_error(ctx, "%x write to closed socket %d", source, id);
return 0;
}
if (s->head) {
struct write_buffer * buf = malloc(sizeof(*buf));
buf->next = s->tail;
buf->ptr = (char *)(msg+1);
buf->buffer = (void *)msg;
buf->sz = sz;
s->tail = buf;
return 1;
}
char * ptr = (char *)(msg+1);
for (;;) {
int wt = write(s->fd, ptr, sz);
if (wt < 0) {
switch(errno) {
case EINTR:
continue;
}
break;
}
if (wt == sz) {
return 0;
}
sz-=wt;
ptr+=wt;
break;
}
struct write_buffer * buf = malloc(sizeof(*buf));
buf->next = NULL;
buf->ptr = ptr;
buf->sz = sz;
buf->buffer = (void *)msg;
s->head = s->tail = buf;
event_write(p->fd, s->fd, s, true);
return 1;
}
static int
_cb(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
struct socket_pool *p = ud;
if (type == PTYPE_TEXT) {
char tmp[sz+1];
int id=0;
char * arg = parser(msg, (int)sz, tmp, &id);
_ctrl(context, p , tmp, id, arg, session, source);
return 0;
} else if (type == PTYPE_CLIENT) {
return try_send(context, p, source, msg, sz);
}
if (p->count == 0)
return 0;
assert(type == PTYPE_RESPONSE);
int n = event_wait(p->fd, p->ev, 100); // timeout : 100ms
int i;
for (i=0;i<n;i++) {
struct event *e = &p->ev[i];
if (e->read) {
forward(context, e->s, p);
}
if (e->write) {
struct socket *s = e->s;
sendout(p, s);
if (s->status == STATUS_HALFCLOSE && s->head == NULL) {
force_close(s, p);
reply(context, source, s->session, NULL, 0);
}
}
}
skynet_command(context, "TIMEOUT", n == MAX_EVENT ? "0" : "1");
return 0;
}
int
socket_init(struct socket_pool *pool, struct skynet_context *ctx, const char * args) {
int max = 0;
sscanf(args, "%d",&max);
if (max == 0) {
max = MAX_CONNECTION;
}
pool->cap = max;
int fd = event_init(max);
if (fd < 0) {
return 1;
}
pool->s = malloc(sizeof(struct socket) * max);
memset(pool->s, 0, sizeof(struct socket) * max);
pool->fd = fd;
skynet_callback(ctx, pool, _cb);
skynet_command(ctx,"REG",".socket");
return 0;
}
struct socket_pool *
socket_create(void) {
struct socket_pool *pool = malloc(sizeof(*pool));
memset(pool,0,sizeof(*pool));
pool->id = 1;
pool->fd = -1;
return pool;
}
void
socket_release(struct socket_pool *pool) {
if (pool->fd >= 0) {
close(pool->fd);
}
int i;
for (i=0;i<pool->cap;i++) {
if (pool->s[i].status != STATUS_INVALID && pool->s[i].fd >=0) {
close(pool->s[i].fd);
}
}
free(pool->s);
free(pool);
}

View File

@@ -1,46 +1,15 @@
local skynet = require "skynet"
local socket = require "socket"
local function readline(sep)
while true do
local line = socket.readline(sep)
if line then
return line
end
coroutine.yield()
end
end
local function split_package()
while true do
local cmd = readline "\n"
if cmd ~= "" then
skynet.send(skynet.self(), "text", cmd)
end
end
end
local split_co = coroutine.create(split_package)
skynet.register_protocol {
name = "client",
id = 3,
pack = function(...) return ... end,
unpack = function(msg,sz)
assert(msg , "Stdin closed")
socket.push(msg,sz)
assert(coroutine.resume(split_co))
end,
dispatch = function () end
}
skynet.start(function()
skynet.dispatch("text", function (session, address, cmd)
local handle = skynet.newservice(cmd)
local stdin = socket.stdin()
socket.lock(stdin)
while true do
local cmdline = socket.readline(stdin, "\n")
local handle = skynet.newservice(cmdline)
if handle == nil then
print("Launch error:",cmd)
print("Launch error:",cmdline)
end
end)
socket.stdin()
end
socket.unlock(stdin)
end)

View File

@@ -2,8 +2,8 @@ local skynet = require "skynet"
skynet.start(function()
print("Server start")
skynet.launch("socket",128)
local service = skynet.launch("snlua","service_mgr")
local connection = skynet.launch("connection","256")
local lualog = skynet.launch("snlua","lualog")
local console = skynet.launch("snlua","console")
local remoteroot = skynet.launch("snlua","remote_root")

View File

@@ -1,242 +0,0 @@
local skynet = require "skynet"
local socket = require "socket"
local int64 = require "int64"
local string = string
local table = table
local tonumber = tonumber
local ipairs = ipairs
local unpack = unpack
local batch_mode = {}
local batch_count = {}
local batch_reply = {}
local redis_server, redis_db = ...
local function compose_message(msg)
local lines = { "*" .. #msg }
for _,v in ipairs(msg) do
local t = type(v)
if t == "number" then
v = tostring(v)
elseif t == "userdata" then
v = int64.tostring(int64.new(v),10)
end
table.insert(lines,"$"..#v)
table.insert(lines,v)
end
table.insert(lines,"")
local cmd = table.concat(lines,"\r\n")
return cmd
end
local function select_db(id)
local result , ok = skynet.call(skynet.self(), "lua", "SELECT", tostring(id))
assert(result and ok == "OK")
end
local request_queue = { head = 1, tail = 1 }
local function push_request_queue(reply)
request_queue[request_queue.tail] = reply
request_queue.tail = request_queue.tail + 1
end
local function pop_request_queue()
assert(request_queue.head < request_queue.tail)
local reply = request_queue[request_queue.head]
request_queue[request_queue.head] = nil
request_queue.head = request_queue.head + 1
return reply
end
local function batch_close(address,session)
local reply = batch_reply[address]
skynet.redirect(address,0, "response", session, skynet.pack(not (type(reply) == "string"), reply))
batch_mode[address] = nil
batch_count[address] = nil
batch_reply[address] = nil
end
local function batch_dec(address)
local bc = batch_count[address]
batch_count[address] = bc - 1
if bc == 1 then
local session = batch_mode[address]
if type(session) == "number" then
batch_close(address, session)
end
end
end
local function response(suc, value)
local reply = pop_request_queue()
local mode = reply.batch
local address = reply.address
if mode == "read" then
if suc then
local br = batch_reply[address]
if type(br) == "table" then
br.n = br.n + 1
br[br.n] = value
end
else
batch_reply[address] = value
end
batch_dec(address)
elseif mode == "write" then
if not suc then
batch_reply[address] = value
end
batch_dec(address)
else
skynet.redirect(address,0, "response", reply.session, skynet.pack(suc, value))
end
end
local function readline(sep)
while true do
local line = socket.readline(sep)
if line then
return line
end
coroutine.yield()
end
end
local function readbytes(bytes)
while true do
local block = socket.read(bytes)
if block then
return block
end
coroutine.yield()
end
end
local redcmd = {}
redcmd[42] = function(data) -- '*'
local n = tonumber(data)
if n < 0 then
response(true, nil)
return
end
local bulk = {}
for i = 1,n do
local line = readline "\r\n"
local bytes = tonumber(string.sub(line,2))
if bytes < 0 then
table.insert(bulk, nil)
else
local data = readbytes(bytes + 2)
table.insert(bulk, string.sub(data,1,-3))
end
end
response(true, bulk)
end
redcmd[36] = function(data) -- '$'
local bytes = tonumber(data)
if bytes < 0 then
response(true,nil)
return
end
local firstline = readbytes(bytes+2)
response(true,string.sub(firstline,1,-3))
end
redcmd[43] = function(data) -- '+'
response(true,data)
end
redcmd[45] = function(data) -- '-'
response(false,data)
end
redcmd[58] = function(data) -- ':'
-- todo: return string later
response(true, tonumber(data))
end
local function split_package()
while true do
local result = readline "\r\n"
local firstchar = string.byte(result)
local data = string.sub(result,2)
local f = redcmd[firstchar]
assert(f)
f(data)
end
end
local function init()
while socket.connect(redis_server) do
skynet.sleep(1000)
end
if redis_db then
select_db(redis_db)
end
end
local split_co = coroutine.create(split_package)
local function reconnect()
init()
for i = request_queue.head, request_queue.tail-1 do
local request = request_queue[i]
socket.write(request.cmd)
end
split_co = coroutine.create(split_package)
end
skynet.register_protocol {
name = "client",
id = 3,
pack = function(...) return ... end,
unpack = function(msg,sz)
if sz == 0 then
skynet.timeout(0, reconnect)
return
end
socket.push(msg,sz)
assert(coroutine.resume(split_co))
end,
dispatch = function () end
}
skynet.start(function()
skynet.dispatch("text", function(session, address, mode)
local last = batch_mode[address]
if mode == "end" then
assert(last == "read" or last == "write" , "Invalid end")
if batch_count[address] == 0 then
batch_close(address, session)
else
batch_mode[address] = session
end
else
assert(last == nil, "Already in batch mode")
if mode == "read" then
batch_reply[address] = { n = 0 }
batch_count[address] = 0
elseif mode == "write" then
batch_count[address] = 0
else
error ("Invalid last batch operation : " .. last)
end
batch_mode[address] = mode
end
end)
skynet.dispatch("lua", function(session, address, ...)
local message = { ... }
local cmd = compose_message(message)
socket.write(cmd)
local mode = batch_mode[address]
if mode == "read" or mode == "write" then
batch_count[address] = batch_count[address] + 1
end
push_request_queue { session = session , address = address, cmd = cmd , batch = mode }
end)
init()
end)

View File

@@ -1,26 +0,0 @@
local skynet = require "skynet"
local log = require "log"
local config = require "config"
local redis_conf = skynet.getenv "redis"
local name = config (redis_conf)
local connection = {}
skynet.start(function()
skynet.dispatch("lua", function (session, from, dbname)
if connection[dbname] then
skynet.ret(skynet.pack(connection[dbname]))
return
end
if name[dbname] == nil then
log.Error("Invalid db name : "..dbname)
skynet.ret(skynet.pack(nil))
return
end
local redis_cli = skynet.newservice("redis-cli", name[dbname])
connection[dbname] = redis_cli
skynet.ret(skynet.pack(redis_cli))
end)
end)

View File

@@ -3,10 +3,11 @@ local redis = require "redis"
skynet.start(function()
local db = redis.connect "main"
print(db:select(0))
db:batch "write" -- ignore results
db:set("A", "hello")
db:set("B", "world")
db:batch "end"
print(db:batch "end")
db:batch "read"
db:get("A")
@@ -19,6 +20,7 @@ skynet.start(function()
print(db:get "A")
print(db:set("A","hello world"))
print(db:get("A"))
db:disconnect()
skynet.exit()
end)