socket在公司代码中应用比较广,比如接口调用的IPCRPC机制,经常看到这样的代码,但是一直也没有动手写过。
在某个比较大的进程中创建一个子进程,由于父子进程复制会浪费内存,可以将创建进程的命令通过socket发送到另一个轻量级的进程来创建。
在lighttpd和airplay的源码中,socket的框架是类似的。
下面参照lighttpd和airplay写个简单的回显server,以后有空再完善。
server.c
#include <stdio.h>
#include <string.h>#include <stdlib.h>#include <sys/types.h>#include <sys/socket.h>#include <errno.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/select.h>#include <sys/time.h>#include <unistd.h>#include <fcntl.h>//#define USE_UNIX_SOCKET
#define USE_IPV4_SOCKET#ifdef USE_UNIX_SOCKET#include <sys/un.h>const char * host = "/tmp/fellow.srv.un.addr";#endif#ifdef USE_IPV4_SOCKETconst char *host = "127.0.0.1";const int port = 9090;#endif#define FDEVENT_IN (1<<0)#define FDEVENT_OUT (1<<1)#define FDEVENT_ERR (1<<2)typedef struct _FdEvent{
int maxFd; fd_set select_listen_read_fd;//监听fd fd_set select_listen_write_fd; fd_set select_listen_err_fd; fd_set select_set_read_fd;//每次加入fd到select_set_*_fd,在select前将值赋给监听fd,可以不必每次遍历来初始化监听fd fd_set select_set_write_fd; fd_set select_set_err_fd;}FdEvent;typedef void (*Handle)(void *context);typedef struct _SockSource{//每一个监听到的socket对应一个SockSource int sock; int sockType; Handle handle;//当sock有时间发生时,调用handle处理 struct _SockSource *next;}SockSource;SockSource *gSockSourceList = NULL;FdEvent gFdEvent;SockSource *createSockSource(int sock, int sockType, Handle handle)
{ SockSource *sockSource = (SockSource *)malloc(sizeof(SockSource)); sockSource->sock = sock; sockSource->sockType = sockType; sockSource->handle = handle; sockSource->next = NULL; return sockSource;}void addToSockSourceList(SockSource *sockSource)
{ if (gSockSourceList == NULL) { gSockSourceList = sockSource; } else { sockSource->next = gSockSourceList; gSockSourceList = sockSource; }}void removeFromSockSourceList(SockSource *sockSource)
{ if (NULL == gSockSourceList) return; if (gSockSourceList == sockSource) { gSockSourceList = sockSource; if (sockSource) free(sockSource); } else { SockSource *curSockSource = gSockSourceList; SockSource *nextSockSource = gSockSourceList->next; for (; nextSockSource != NULL; nextSockSource = nextSockSource->next) { if (nextSockSource == sockSource) { curSockSource->next = nextSockSource->next; if(sockSource) free(sockSource); break; } curSockSource = nextSockSource; } }} void fdevent_select_reset(FdEvent *ev){ FD_ZERO(&(ev->select_set_read_fd)); FD_ZERO(&(ev->select_set_write_fd)); FD_ZERO(&(ev->select_set_err_fd)); ev->maxFd = -1;}void fdevent_select_set(FdEvent *ev, int fd, int event)
{ if (fd > (int)FD_SETSIZE) return; if (event & FDEVENT_IN) { FD_SET(fd, &(ev->select_set_read_fd)); } else { FD_CLR(fd, &(ev->select_set_read_fd)); } if (event & FDEVENT_OUT) { FD_SET(fd, &(ev->select_set_write_fd)); } else { FD_CLR(fd, &(ev->select_set_write_fd)); } if (event & FDEVENT_ERR) { FD_SET(fd, &(ev->select_set_err_fd)); } else { FD_CLR(fd, &(ev->select_set_err_fd)); } if (fd > ev->maxFd) ev->maxFd = fd;} int fdevent_select_poll(FdEvent *ev, int time_ms){ struct timeval tv; tv.tv_sec = time_ms /1000; tv.tv_usec = (time_ms % 1000) * 1000; ev->select_listen_read_fd = ev->select_set_read_fd; ev->select_listen_write_fd = ev->select_set_write_fd; ev->select_listen_err_fd = ev->select_set_err_fd; return select(ev->maxFd + 1, &(ev->select_listen_read_fd),&(ev->select_listen_write_fd),&(ev->select_listen_err_fd), &tv); } void accept_handle(void *context){ SockSource *clientSource = (SockSource *)context; char rcvBuf[1024]; char sndBuf[1024]; memset(&rcvBuf, 0, sizeof(rcvBuf)); memset(&sndBuf, 0, sizeof(sndBuf)); int byteRcv = recv(clientSource->sock, rcvBuf, sizeof(rcvBuf), 0); if (byteRcv > 0) { printf("rcv data: %s, len: %d\n", rcvBuf, byteRcv); snprintf(sndBuf, sizeof(sndBuf), "server rcv %s\n", rcvBuf); send(clientSource->sock, sndBuf, strlen(sndBuf)+1, 0); } else { printf("rcv fail, errno:%d\n", errno); fdevent_select_set(&gFdEvent, clientSource->sock, 0); removeFromSockSourceList(clientSource); }} void listen_handle(void *context){ SockSource *listenSource = (SockSource *)context; int client_sock = -1; size_t addr_len = 0;#ifdef USE_UNIX_SOCKET struct sockaddr_un client_addr; addr_len = strlen(host) + sizeof(client_addr.sun_family);#endif#ifdef USE_IPV4_SOCKET struct sockaddr_in client_addr; addr_len = sizeof(struct sockaddr_in);#endif if (-1 == (client_sock = accept(listenSource->sock, (struct sockaddr*)&client_addr, &addr_len))) { switch(errno) { case EAGAIN: #if EAGAIN != EWOULDBLOCK case EWOULDBLOCK; #endif break; default: printf("accept fail, errno:%d", errno); break; } } else { fdevent_select_set(&gFdEvent, client_sock, FDEVENT_IN); SockSource *clientSource = createSockSource(client_sock, (int)FDEVENT_IN, accept_handle); addToSockSourceList(clientSource); }}int setup_listen_socket()
{ int sock = -1; socklen_t addr_len;#ifdef USE_UNIX_SOCKET struct sockaddr_un srv_addr; srv_addr.sun_family = AF_UNIX; if (-1 == (sock = socket(AF_UNIX, SOCK_STREAM, 0))) { printf("un socket fail, errno:%d\n", errno); goto err; } size_t hostLen = strlen(host) + 1; memcpy(srv_addr.sun_path, host, hostLen); addr_len = hostLen + sizeof(srv_addr.sun_family);#endif#ifdef USE_IPV4_SOCKET struct sockaddr_in srv_addr; memset(&srv_addr, 0, sizeof(struct sockaddr_in)); srv_addr.sin_family = AF_INET; if (-1== (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) { printf("in socket fail, errno:%d\n", errno); goto err; } int val = 1; if (-1== setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) { printf("setsockopt fail, errno:%d\n", errno); goto err; } if (0 == inet_aton(host, &(srv_addr.sin_addr))) { printf("inet_aton fail, errno:%d\n", errno); goto err; } srv_addr.sin_port = htons(port); addr_len = sizeof(struct sockaddr_in);#endif if (0 != bind(sock, (struct sockaddr*)&srv_addr, addr_len)) { printf("bind fail, errno:%d\n", errno); goto err; } if (0 != listen(sock, 10)) { printf("inet_aton fail, errno:%d\n", errno); goto err; } fdevent_select_set(&gFdEvent, sock, FDEVENT_IN); SockSource *listenSource = createSockSource(sock, (int)FDEVENT_IN, listen_handle); addToSockSourceList(listenSource); return sock;err: if (-1 != sock) { close(sock); } return -1;}void main(void)
{ int sock = -1; int numOfEvent = 0; fdevent_select_reset(&gFdEvent); if(-1 == (sock = setup_listen_socket())) { return; } printf("listenning sock:%d\n", sock); while (1) { int timeout_ms = 1000; numOfEvent = fdevent_select_poll(&gFdEvent, timeout_ms); if (numOfEvent > 0) { SockSource *source = NULL; for (source = gSockSourceList; source != NULL; source = source->next) { switch(source->sockType) { case FDEVENT_IN: if (FD_ISSET(source->sock, &(gFdEvent.select_listen_read_fd))) { source->handle(source); numOfEvent--; } break; case FDEVENT_OUT: if (FD_ISSET(source->sock, &(gFdEvent.select_listen_write_fd))) { source->handle(source); numOfEvent--; } break; case FDEVENT_ERR: if (FD_ISSET(source->sock, &(gFdEvent.select_listen_err_fd))) { source->handle(source); numOfEvent--; } break; } } } }}
client端测试代码:
#include <sys/types.h>
#include <sys/socket.h>#include <sys/un.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <netinet/in.h>#include <arpa/inet.h>#define USE_IPV4_SOCKET
//#define USE_UNIX_SOCKET#ifdef USE_UNIX_SOCKET#include <sys/un.h>const char * host = "/tmp/fellow.srv.un.addr";#endif#ifdef USE_IPV4_SOCKETconst char *host = "127.0.0.1";const int port = 9090;#endifvoid main(void)
{ int sock = -1; char sndBuf[1024]; char rcvBuf[1024]; socklen_t addr_len;#ifdef USE_UNIX_SOCKET struct sockaddr_un srv_addr; srv_addr.sun_family = AF_UNIX; if (-1 == (sock = socket(AF_UNIX, SOCK_STREAM, 0))) { printf("un socket fail, errno:%d\n", errno); goto err; } size_t hostLen = strlen(host) + 1; memcpy(srv_addr.sun_path, host, hostLen); addr_len = hostLen + sizeof(srv_addr.sun_family);#endif#ifdef USE_IPV4_SOCKET struct sockaddr_in srv_addr; memset(&srv_addr, 0, sizeof(struct sockaddr_in)); srv_addr.sin_family = AF_INET; if (-1== (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) { printf("in socket fail, errno:%d\n", errno); goto err; } if (0 == inet_aton(host, &(srv_addr.sin_addr))) { printf("inet_aton fail, errno:%d\n", errno); goto err; } srv_addr.sin_port = htons(port); addr_len = sizeof(struct sockaddr_in);#endif
if (-1 == connect(sock, (struct sockaddr*)&srv_addr, addr_len)) { printf("connect fail, errno:%d\n", errno); goto err; } int running = 1; int count = 0; while (running) { printf("Send your msg to server, finish with end!\n"); fgets(sndBuf, sizeof(sndBuf), stdin); int dataLen = strlen(sndBuf) + 1; if (-1 == send(sock, (void*)&sndBuf, dataLen, 0)) { printf("send fail, errno:%d\n", errno); goto err; } if(!strncmp(sndBuf, "end", strlen("end"))) { running = 0; } if (-1 == recv(sock, (void*)&rcvBuf, sizeof(rcvBuf), 0)) { printf("recv fail, errno:%d\n", errno); goto err; } printf("data from server: %s\n", rcvBuf); count++; }err: if(-1 != sock) close(sock);}client端测试结果:
server端log