转载:http://www.cnblogs.com/chekliang/p/3222950.html

socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。 虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。 这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。

  UNIX Domain Socket是全双工的,API接口语义丰富,相比其它IPC机制有明显的优越性,目前已成为使用最广泛的IPC机制,比如X Window服务器和GUI程序之间就是通过UNIX Domain Socket通讯的。

  使用UNIX Domain Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。

  UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。

  以下程序将UNIX Domain socket绑定到一个地址。

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h> int main(void)
{
int fd, size;
struct sockaddr_un un; memset(&un, , sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, "foo.socket");
if ((fd = socket(AF_UNIX, SOCK_STREAM, )) < )
{
perror("socket error");
exit();
}
size = offsetof(struct sockaddr_un, sun_path) +strlen(un.sun_path); if (bind(fd, (struct sockaddr *)&un, size) < )
{
perror("bind error");
exit();
} printf("UNIX domain socket bound/n");
exit();
}

  注意程序中的offsetof宏,它在stddef.h头文件中定义:

  #define offsetof(TYPE, MEMBER) ((int)&((TYPE *)0)->MEMBER)      

  offsetof(struct sockaddr_un, sun_path)就是取sockaddr_un结构体的sun_path成员在结构体中的偏移,也就是从结构体的第几个字节开始是sun_path成 员。想一想,这个宏是如何实现这一功能的?(先将TYPE类型的指针首地址设为0,然后取MEMBER成员的地址就是该成员在TYPE中的偏移数。)

该程序的运行结果如下。

$ ./a.out  UNIX domain socket bound  
$ ls -l
foo.socket srwxrwxr-x 1 user 0 Aug 22 12:43 foo.socket
$ ./a.out bind error: Address already in use
$ rm foo.socket
$ ./a.out UNIX domain socket bound

  以下是服务器的listen模块,与网络socket编程类似,在bind之后要listen,表示通过bind的地址(也就是socket文件)提供服务。

#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h> #define QLEN 10 /*
* Create a server endpoint of a connection.
* Returns fd if all OK, <0 on error.
*/ int serv_listen(const char *name)
{
int fd, len, err, rval;
struct sockaddr_un un; /* create a UNIX domain stream socket */
if ((fd = socket(AF_UNIX, SOCK_STREAM, )) < )
return(-);
unlink(name); /* in case it already exists */ /* fill in socket address structure */
memset(&un, , sizeof(un));
un.sun_family = AF_UNIX; strcpy(un.sun_path, name);
len = offsetof(struct sockaddr_un, sun_path) + strlen(name); /* bind the name to the descriptor */
if (bind(fd, (struct sockaddr *)&un, len) < )
{
rval = -;
goto errout;
} if (listen(fd, QLEN) < )
{ /* tell kernel we're a server */
rval = -;
goto errout;
} return(fd); errout:
err = errno;
close(fd);
errno = err;
return(rval);
}

  以下是服务器的accept模块,通过accept得到客户端地址也应该是一个socket文件,如果不是socket文件就返回错误码,如果 是 socket文件,在建立连接后这个文件就没有用了,调用unlink把它删掉,通过传出参数uidptr返回客户端程序的user id。

#include <stddef.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h> int serv_accept(int listenfd, uid_t *uidptr)
{ int clifd, len, err, rval;
  time_t staletime;
  struct sockaddr_un un;
  struct stat statbuf;
  len = sizeof(un);
  if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < )
    return(-);
/* often errno=EINTR, if signal caught */
/* obtain the client's uid from its calling address */
  len -= offsetof(struct sockaddr_un, sun_path); /* len of pathname */
  un.sun_path[len] = ; /* null terminate */
  if (stat(un.sun_path, &statbuf) < )
  {
    rval = -;
    goto errout;
  }
  if (S_ISSOCK(statbuf.st_mode) == )
  {
    rval = -;
    /* not a socket */
    goto errout;
  }
  if (uidptr != NULL)
    *uidptr = statbuf.st_uid; /* return uid of caller */
  unlink(un.sun_path); /* we're done with pathname now */
  
  return(clifd); errout:  
  err = errno;
  close(clifd);
  errno = err;
  return(rval);
}

  以下是客户端的connect模块,与网络socket编程不同的是,UNIX Domain Socket客户端一般要显式调用bind函数,而不依赖系统自动分配的地址。客户端bind一个自己指定的socket文件名的好处是,该文件名可以包 含客户端的pid以便服务器区分不同的客户端。

/*
* ss.c
*
* Created on: 2013-7-29
* Author: Administrator
*/
#include <stdio.h>
#include <stddef.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h> #define CLI_PATH "/var/tmp/" /* +5 for pid = 14 chars */
/*
* Create a client endpoint and connect to a server.
* Returns fd if all OK, <0 on error.
*/
int cli_conn(const char *name)
{
int fd, len, err, rval;
struct sockaddr_un un;
/* create a UNIX domain stream socket */
if ((fd = socket(AF_UNIX, SOCK_STREAM, )) < )
return(-); /* fill socket address structure with our address */
memset(&un, , sizeof(un));
un.sun_family = AF_UNIX;
sprintf(un.sun_path, "%s%05d", CLI_PATH, getpid());
len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
unlink(un.sun_path);
/* in case it already exists */
if (bind(fd, (struct sockaddr *)&un, len) < )
{
rval = -;
goto errout;
}
/* fill socket address structure with server's address */
memset(&un, , sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, name);
len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
if (connect(fd, (struct sockaddr *)&un, len) < )
{
rval = -;
goto errout;
} return(fd);
errout: err = errno;
close(fd);
errno = err;
return(rval);
}

  服务器端:

#include <stdio.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <stddef.h>
#include <string.h> // the max connection number of the server
#define MAX_CONNECTION_NUMBER 5 /* * Create a server endpoint of a connection. * Returns fd if all OK, <0 on error. */
int unix_socket_listen(const char *servername)
{
int fd;
struct sockaddr_un un;
if ((fd = socket(AF_UNIX, SOCK_STREAM, )) < )
{
return(-);
}
int len, rval;
unlink(servername); /* in case it already exists */
memset(&un, , sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, servername);
len = offsetof(struct sockaddr_un, sun_path) + strlen(servername);
/* bind the name to the descriptor */
if (bind(fd, (struct sockaddr *)&un, len) < )
{
rval = -;
}
else
{
if (listen(fd, MAX_CONNECTION_NUMBER) < )
{
rval = -;
}
else
{
return fd;
}
}
int err;
err = errno;
close(fd);
errno = err;
return rval;
} int unix_socket_accept(int listenfd, uid_t *uidptr)
{
int clifd, len, rval;
time_t staletime;
struct sockaddr_un un;
struct stat statbuf;
len = sizeof(un);
if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < )
{
return(-);
}
/* obtain the client's uid from its calling address */
len -= offsetof(struct sockaddr_un, sun_path); /* len of pathname */
un.sun_path[len] = ; /* null terminate */
if (stat(un.sun_path, &statbuf) < )
{
rval = -;
}
else
{
if (S_ISSOCK(statbuf.st_mode) )
{
if (uidptr != NULL) *uidptr = statbuf.st_uid; /* return uid of caller */
unlink(un.sun_path); /* we're done with pathname now */
return clifd;
}
else
{
rval = -; /* not a socket */
}
}
int err;
err = errno;
close(clifd);
errno = err;
return(rval);
} void unix_socket_close(int fd)
{
close(fd);
} int main(void)
{
int listenfd,connfd;
listenfd = unix_socket_listen("foo.sock");
if(listenfd<)
{
printf("Error[%d] when listening...\n",errno);
return ;
}
printf("Finished listening...\n",errno);
uid_t uid;
connfd = unix_socket_accept(listenfd, &uid);
unix_socket_close(listenfd);
if(connfd<)
{
printf("Error[%d] when accepting...\n",errno);
return ;
}
printf("Begin to recv/send...\n");
int i,n,size;
char rvbuf[];
for(i=;i<;i++)
{
//===========接收==============
size = recv(connfd, rvbuf, , );
if(size>=)
{
// rvbuf[size]='\0';
printf("Recieved Data[%d]:%c...%c\n",size,rvbuf[],rvbuf[size-]);
}
if(size==-)
{
printf("Error[%d] when recieving Data:%s.\n",errno,strerror(errno));
break;
}
/*
//===========发送==============
memset(rvbuf, 'c', 2048);
size = send(connfd, rvbuf, 2048, 0);
if(size>=0)
{
printf("Data[%d] Sended.\n",size);
}
if(size==-1)
{
printf("Error[%d] when Sending Data.\n",errno);
break;
}
*/
sleep();
}
unix_socket_close(connfd);
printf("Server exited.\n");
}

     客户端:

#include <stdio.h>
#include <stddef.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h> /* Create a client endpoint and connect to a server. Returns fd if all OK, <0 on error. */
int unix_socket_conn(const char *servername)
{
int fd;
if ((fd = socket(AF_UNIX, SOCK_STREAM, )) < ) /* create a UNIX domain stream socket */
{
return(-);
}
int len, rval;
struct sockaddr_un un;
memset(&un, , sizeof(un)); /* fill socket address structure with our address */
un.sun_family = AF_UNIX;
sprintf(un.sun_path, "scktmp%05d", getpid());
len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
unlink(un.sun_path); /* in case it already exists */
if (bind(fd, (struct sockaddr *)&un, len) < )
{
rval= -;
}
else
{
/* fill socket address structure with server's address */
memset(&un, , sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, servername);
len = offsetof(struct sockaddr_un, sun_path) + strlen(servername);
if (connect(fd, (struct sockaddr *)&un, len) < )
{
rval= -;
}
else
{
return (fd);
}
}
int err;
err = errno;
close(fd);
errno = err;
return rval;
} void unix_socket_close(int fd)
{
close(fd);
} int main(void)
{
srand((int)time());
int connfd;
connfd = unix_socket_conn("foo.sock");
if(connfd<)
{
printf("Error[%d] when connecting...",errno);
return ;
}
printf("Begin to recv/send...\n");
int i,n,size;
char rvbuf[];
for(i=;i<;i++)
{
/*
//=========接收=====================
size = recv(connfd, rvbuf, 800, 0); //MSG_DONTWAIT
if(size>=0)
{
printf("Recieved Data[%d]:%c...%c\n",size,rvbuf[0],rvbuf[size-1]);
}
if(size==-1)
{
printf("Error[%d] when recieving Data.\n",errno);
break;
}
if(size < 800) break;
*/
//=========发送======================
memset(rvbuf,'a',);
rvbuf[]='b';
size = send(connfd, rvbuf, , );
if(size>=)
{
printf("Data[%d] Sended:%c.\n",size,rvbuf[]);
}
if(size==-)
{
printf("Error[%d] when Sending Data:%s.\n",errno,strerror(errno));
break;
}
sleep();
}
unix_socket_close(connfd);
printf("Client exited.\n");
}

参考链接:

http://blog.csdn.net/guxch/article/details/7041052


基于DRAM实现的UNIX域套接字

服务器端

#include <stdio.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h> #define NAME "/tmp/foo.sock"
#define N 256 typedef struct sockaddr SA; int main(void)
{
int sockfd;
int size;
struct sockaddr_un un;
char rvbuf[];
int len; if ((sockfd = socket(PF_UNIX, SOCK_DGRAM, )) < )
{
return(-);
}
unlink(NAME); /* in case it already exists */
memset(&un, , sizeof(un));
un.sun_family = PF_UNIX;
strcpy(un.sun_path, NAME);
len = offsetof(struct sockaddr_un, sun_path) + strlen(NAME);
/* bind the name to the descriptor */
if (bind(sockfd, (struct sockaddr *)&un, len) < )
{
perror("fail to bind!\n");
exit(-);
} printf("Begin to recv/send...\n");
while()
{
size = recvfrom(sockfd, rvbuf, N, ,NULL, NULL);
if(size>=)
{
// rvbuf[size]='\0';
printf("Recieved Data[%d]:%c...%c\n",size,rvbuf[],rvbuf[size-]);
}
if(size==-)
{
printf("Error[%d] when recieving Data:%s.\n",errno,strerror(errno));
break;
}
}
close(sockfd);
printf("Server exited.\n");
return ;
}

客户端

#include <stdio.h>
#include <stddef.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h> typedef struct sockaddr SA; #define NAME "/tmp/foo.sock"
#define N 256 int main(void)
{
int sockfd;
struct sockaddr_un un;
int size;
char rvbuf[]; if ((sockfd = socket(PF_UNIX, SOCK_DGRAM, )) < ) /* create a UNIX domain stream socket */
{
return(-);
}
int len;
memset(&un, , sizeof(un));
un.sun_family = PF_UNIX;
strcpy(un.sun_path, NAME);
len = offsetof(struct sockaddr_un, sun_path) + strlen(NAME); printf("Begin to recv/send...\n");
while()
{
memset(rvbuf,'a',);
rvbuf[]='b';
size = sendto(sockfd, rvbuf, N, , (SA *)&un, len); if(size>=)
{
printf("Data[%d] Sended:%c.\n",size,rvbuf[]);
} if(size==-)
{
printf("Error[%d] when Sending Data:%s.\n",errno,strerror(errno));
break;
}
sleep();
}
close(sockfd);
printf("Client exited.\n");
return ;
}

UNIX域套接字——UNIX domain socket(DGRAM)

By fireworks2@foxmail.com

找了大半天的资料,收获也不多,其实还是自己思考更靠谱一些。

1. unix域的数据报服务是否可靠

man unix 手册即可看到,unix domain socket 的数据报既不会丢失也不会乱序
(据我所知,在linux下的确是这样)。不过最新版本的内核,仍然又提供了一个保证次序的类型 “ kernel 2.6.4 SOCK_SEQPACKET
”。

2. STREAM 和 DGRAM 的主要区别

既然数据报不丢失也可靠,那不是和 STREAM 很类似么?我理解也确实是这样,而且我觉得 DGRAM
相对还要好一些,因为发送的数据可以带边界。二者另外的区别在于收发时的数据量不一样,基于 STREAM 的套接字,send 可以传入超过
SO_SNDBUF 长的数据,recv 时同 TCP 类似会存在数据粘连。

采用阻塞方式使用API,在unix domain socket 下调用 sendto
时,如果缓冲队列已满,会阻塞。而UDP因为不是可靠的,无法感知对端的情况,即使对端没有及时收取数据,基本上sendto都能立即返回成功(如果发端
疯狂sendto就另当别论,因为过快地调用sendto在慢速网络的环境下,可能撑爆套接字的缓冲区,导致sendto阻塞)。

3. SO_SNDBUF 和 SO_REVBUF

对于 unix domain socket,设置 SO_SNDBUF 会影响 sendto 最大的报文长度,但是任何针对 SO_RCVBUF 的设置都是无效的
。实际上 unix domain socket 的数据报还是得将数据放入内核所申请的内存块里面,再由另一个进程通过 recvfrom 从内核读取,因此具体可以发送的数据报长度受限于内核的 slab 策略
。在 linux 平台下,早先版本(如 2.6.2)可发送最大数据报长度约为 128 k
,新版本的内核支持更大的长度。

4. 使用 DGRAM 时,缓冲队列的长度

有几个因素会影响缓冲队列的长度,一个是上面提到的 slab 策略,另一个则是系统的内核参数 /proc/sys/net/unix/max_dgram_qlen。缓冲队列长度是这二者共同决定的。

如 max_dgram_qlen 默认为 10,在数据报较小时(如1k),先挂起接收数据的进程后,仍可以 sendto 10 次并顺利返回;

但是如果数据报较大(如120k)时,就要看 slab “size-131072” 的 limit 了。

5. 使用 unix domain socket 进行进程间通信 vs 其他方式

· 需要先确定操作系统类型,以及其所对应的最大 DGRAM 长度,如果有需要传送超过该长度的数据报,建议拆分成几个发送,接收后组装即可(不会乱序,个人觉得这样做比用 STREAM 再切包方便得多)

· 同管道相比,unix 域的数据报不但可以维持数据的边界,还不会碰到在写入管道时的原子性问题。

· 同共享内存相比,不能独立于进程缓存大量数据,但是却避免了同步互斥的考量。

· 同普通 socket 相比,开销相对较小(不用计算报头),DGRAM 的报文长度可以大于 64k,不过不能像普通 socket 那样将进程切换到不同机器

6. 其他

其实在本机 IPC 时,同普通 socket 的 UDP 相比,unix domain socket 的数据报只不过是在收发时分别少计算了一下校验和而已,本机的 UDP 会走 lo 接口,不会进行 IP 分片,也不会真正跑到网卡的链路层上去(不会占用网卡硬件)
。也就是说,在本机上使用普通的 socket UDP,只是多耗了一些 CPU(之所以说一些,是因为校验和的计算很简单),此外本机的 UDP 也可以保证数据不丢失、不乱序

从我个人的经验来看,即便是高并发的网络服务器,单纯因为收发包造成的 CPU 占用其实并不算多(其中收包占用的 CPU 从 %si 可见一斑,因为收包需通过软中断实现的),倒是网卡带宽、磁盘IO、后台逻辑、内存使用等问题往往成为主要矛盾。

所以,在没有长时缓存通信数据的需求时,可以考虑通过 UDP 来实现本地进程间 IPC,这样也便于切换机器。

(也不必去操心 unix domain socket 数据报的最大长度了,呵呵)对于较长的报文,可以切分成小的,再重新组装,不过这样做仅适用于本机通信,如果还要考虑以后迁移机器,那还是老老实实地 TCP 吧。
(本地 TCP 耗用资源的情况不得而知,没有做过充分的测试,不过本地 TCP 自身的一些收发包操作一般也不构成瓶颈)

---在 google 时查找到的其他资源也一并列在这里---

setsockopt 设置 SO_SNDBUF SO_RCVBUF 时的冗余分配:

http://www.tux.org/lkml/

unix域套接字的使用:

http://www.cnblogs.com/skynet/archive/2010/12/04/1881236.html

http://hi.baidu.com/studyarea/blog/item/b007f4450a2a9e38879473ca.html

Unix domain socket的更多相关文章

  1. 问题解决:psql: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?

    错误提示: psql: could not connect to server: No such file or directory Is the server running locally and ...

  2. ndk学习16: unix domain socket

    一.UNIX Domain Socket 概念: UNIX Domain Socket是在socket架构上发展起来的用于同一台主机的进程间通讯(IPC) 特点: 1. 它不需要经过网络协议栈,不需要 ...

  3. Unix Domain Socket 域套接字实现

    主要注意流程: STREAM SOCKET: Server :  socket() --->  bind() ---> listen()  ---> accept() Client: ...

  4. 由一个简单需求到Linux环境下的syslog、unix domain socket

    本文记录了因为一个简单的日志需求,继而对linux环境下syslog.rsyslog.unix domain socket的学习.本文关注使用层面,并不涉及rsyslog的实现原理,感兴趣的读者可以参 ...

  5. libpqxx接口的在linux下的使用,解决psql:connections on Unix domain socket "/tmp/.s.PGSQL.5432"错误

    在项目中使用postgresql数据库时要求在windows和linux双平台兼容.于是在windows下使用的接口在linux下爆出异常: psql:connections on Unix doma ...

  6. UNIX DOMAIN SOCKET效率

    关于UNIX DOMAIN SOCKET和普通udp socket的对比 在TX1(4核A57 1.7GHz)的板卡上进行测试,每个包大小设置为1024,全速收发,UDS的速度在90Mbps左右,UD ...

  7. nginx、php-fpm默认配置与性能–TCP socket还是unix domain socket【转】

    原文地址:https://www.cnxct.com/default-configuration-and-performance-of-nginx-phpfpm-and-tcp-socket-or-u ...

  8. [dev][socket] unix domain socket删除socket文件

    问题 在使用unix domain socket的时候,bind之后,会在本地路径里 产生一个与path对应的socket文件. 如何正确的在用完socket之后,对其销毁呢? 方案 使用 unlin ...

  9. Unix domain socket 简介

    Unix domain socket 又叫 IPC(inter-process communication 进程间通信) socket,用于实现同一主机上的进程间通信.socket 原本是为网络通讯设 ...

  10. mysql unix domain socket and network socket, ssh key

    当主机填写为localhost时mysql会采用 unix domain socket连接 当主机填写为127.0.0.1时mysql会采用tcp方式连接 这是linux套接字网络的特性,win平台不 ...

随机推荐

  1. centos7 安全配置

    CentOS是最多人用来运行服务器的 Linux 版本,最新版本是 CentOS 7.当你兴趣勃勃地在一台主机或 VPS 上安装 CentOS 7 后,首要的工作肯定是加强它的安全性,以下列出的七件事 ...

  2. poj 2229 拆数问题 dp算法

    题意:一个n可以拆成 2的幂的和有多少种 思路:先看实例 1   1 2    1+1     2 3     1+1+1  1+2 4      1+1+1+1  1+1+2  2+2  4 5  ...

  3. poj 2531 分权问题 dfs算法

    题意:一个集合(矩阵) m[i][j]=m[j][i]权值,分成两个集合,使其权值最大.注:在同一个集合中权值只能算一个. 思路:dfs 假设都在集合0 遍历 id 的时候拿到集合1 如果与 id 相 ...

  4. 安装好的IIS,发布成功后打开网站出现错误

      开发web项目时需要安装IIS,在安装好IIS的Windows7本上发布asp.net网站时,web程序已经映射到了本地IIS上,但运行如下错误提示“处理程序“PageHandlerFactory ...

  5. Python的深浅copy

    27.简述Python的深浅拷贝以及应用场景? 深浅拷贝的原理 深浅拷贝用法来自copy模块. 导入模块:import copy 浅拷贝:copy.copy 深拷贝:copy.deepcopy 字面理 ...

  6. Python学习-day6 面向对象概念

    开始学习面向对象,可以说之前的学习和编程思路都是面向过程的,从上到下,一步一步走完. 如果说一个简单的需求,用面向过程实现起来相对容易,但是如果在日常生产,面向对象就可以发挥出他的优势了. 程序的可扩 ...

  7. creat-react-app/dva静态项目,用nginx部署在次级域名路径(如a.com/sub/)需要注意的几点

    因为要把dist文件夹部署在一个域名的次级目录,没想到和运维同学一起折腾了一下午.. 放在这里备忘,也给后来的同学一些可查的中文资料: 1,dva/cra给你的模板index.html是在public ...

  8. Jboss性能调优

    1,Jboss5调优指南 https://www.redhat.com/f/pdf/JB_JEAP5_PerformanceTuning_wp_web.pdf 1,Jboss7.1 性能调优指南 a: ...

  9. 九度oj 题目1494:Dota

    题目描述: 大家都知道在dota游戏中,装备是对于英雄来说十分重要的要素. 英雄们不仅可以购买单个的装备,甚至某些特定的装备组合能够合成更强的装备. 为了简化问题,我们将每个装备对于英雄的功能抽象为一 ...

  10. iOS学习笔记28-系统服务(一)短信和邮件

    一.系统应用 在开发某些应用时,我们可能希望能够调用iOS系统内置的电话.短信.邮件.浏览器应用,或者直接调用安装的第三方应用,这个要怎么实现呢? 这里统一使用UIApplication的一个对象方法 ...