
暂无个人介绍
Thread & ExecutorService & ThreadPoolExecutor 总览 ExecutorService 类方法 shutdown允许已经提交的任务(尚未开始执行和已经开始执行的)继续执行 shutdownNow尚未开始执行的任务不再执行,同时尝试终止正在执行的任务 无论是shutdown 还是shutdownNow,两个的执行都会阻止新的任务提交 一个ExecutorService一旦termination,表明没有正在执行的任务,没有等待执行的任务,也不会有新的任务可以被提交。 如果一个ExecutorService不再使用,应该调用shutdown方法来回收资源。 submit方法(三个重载方法) 返回的Future对象可以用来取消任务和等待任务执行完成 invokeAny和invokeAll方法 用户批量执行任务, invokeAny:会阻塞当前线程,直到某个任务完成。并返回这个任务相关的Future对象 invokeAll:会阻塞当前线程,直到所有任务完成。 isShutdown和isTerminated分别对应于两个状态:关闭状态,终结状态 两阶段shutdown 先执行shutdown方法 调用awaitTermination方法 再调用shutdownNow方法 void shutdownAndAwaitTermination(ExecutorService pool) { pool.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being cancelled if (!pool.awaitTermination(60, TimeUnit.SECONDS)) System.err.println("Pool did not terminate"); } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted pool.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); } } Thread interrupt方法 如果执行a.interrupt方法后,如果a线程(注意是a线程,不是调用线程)抛出了InterruptedException异常,那么a的中断状态会被清除。如果不是抛出InterruptedException异常,那么a的中断状态都会被设置。 interrupted方法 执行a.interrupted方法会返回a线程的中断状态,同时会清除a线程的中断状态 isInterrupted方法 执行a.interrupted方法会返回a线程的中断状态,不会清除a线程的中断状态 ThreadPoolExecutor core pool size 及 max pool size 一个新的任务提交哪些情况下回创建新的线程: 1. 已创建的的线程数小于corePoolSize(即便有线程是空闲的) 2. 已创建的线程数大于corePoolSize小于maxPoolSize,同时任务队列已经满的情况下,也会创建新的线程 可以动态改变这两个的值:setCorePoolSize 以及 setMaximumPoolSize 如果corePoolSize==maxinumPoolSize,那么则创建了一个固定大小的线程池 keep alive time 如果已创建的线程大于了corePoolSize,并且如果有线程的空闲时间大于了keepAliveTime,那么这些线程会被kill掉直到剩下corePoolSize个线程。 可以动态设置:setKeepAliveTime方法 默认情况下keep-alive策略只会针对已创建线程数大于corePoolSize的情况下 可以通过执行allowCoreThreadTimeOut(boolean)让keep-alive策略应用在已创建线程数小于corePoolSize的情况下。 BlockingQueue 1. 如果已创建线程数小于corePoolSize,那么会创建新的线程来执行当前提交的任务,而不是进入阻塞队列 2. 如果已创建线程数大于等于corePoolSize,会尝试先进入阻塞队列,如果进入失败(其实就是队列已满),则会在maxPoolSize条件下创建新的线程来执行当前提交的任务。如果不满足maxPoolSize条件,那么就会执行 拒绝执行策略(默认的拒绝执行策略见下) 3. 通常有三种入队列策略 1. 直接传递给线程(Direct handoffs) 比如:SychronousQueue 感觉可以理解为这个入队列会总是失败,就相当于没有这个队列一样。这样就能在maxPoolSize条件下尽可能快的创建(或选择空闲的线程)来执行新提交的任务。 如果提交的任务有互相的依赖性,可以考虑使用这种队列。 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } 2. 无界队列(Unbounded Queue) 比如:LinkedBlockingQueue 可以理解为如果有任务需要入队列,那么总会入队成功。 因此按照创建新线程的条件,理论上不会有超过corePoolSize个数的线程。也就是说理论上线程数最多为corePoolSize,因此maxPoolSize的设置也就显得没有意义了。 如果提交的任务互相间没有依赖性,可以考虑使用这种队列 3. 有界队列(Bounded Queue) 比如:ArrayBlockingQueue 如果使用有限个maxPoolSize,那么使用这种队列可以防止资源的耗尽。 使用长队列和小的线程池,可以降低CPU使用率,降低系统资源的消耗,以及降低线程上下文切换的消耗,但是会导致低吞吐量。如果任务频繁的阻塞,系统可能会创建比允许的线程数多的线程。 使用短队列和大的线程池,可以提高CPU使用率,但也有可能导致吞吐量下降。 拒绝执行策略 我自己的叫法,实际上就是 RejectedExceptionHandler 1. ThreadPoolExecutor.AbortPolicy 抛出RejectedExecutionException异常 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } 2. ThreadPoolExecutor.CallerRunsPolicy 在调用线程上执行(哪个线程提交的任务就哪个线程执行) public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } 3. ThreadPoolExecutor.DiscardPolicy 直接放弃 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } 4. ThreadPoolExecutor.DiscardOldestPolicy 放弃当前队列中第一个任务 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } Finalization 一个在程序中不再被引用的线程池如果同时没有剩余的线程,那么这个线程池会被自动的shutdown. 因此如果你想即便在忘记执行shutdown方法的时候仍能正常关闭线程池,那么建议设置一个有限的keepAliveTime(针对大于线程数大于corePoolSize的那些线程),同时也执行下 allowCoreThreadTimeOut(boolean) . 欢迎关注公众号
线上FullGC频繁的排查 问题 前段时间发现线上的一个dubbo服务Full GC比较频繁,大约每两天就会执行一次Full GC。 Full GC的原因 我们知道Full GC的触发条件大致情况有以下几种情况: 程序执行了System.gc() //建议jvm执行fullgc,并不一定会执行 执行了jmap -histo:live pid命令 //这个会立即触发fullgc 在执行minor gc的时候进行的一系列检查 执行Minor GC的时候,JVM会检查老年代中最大连续可用空间是否大于了当前新生代所有对象的总大小。 如果大于,则直接执行Minor GC(这个时候执行是没有风险的)。 如果小于了,JVM会检查是否开启了空间分配担保机制,如果没有开启则直接改为执行Full GC。 如果开启了,则JVM会检查老年代中最大连续可用空间是否大于了历次晋升到老年代中的平均大小,如果小于则执行改为执行Full GC。 如果大于则会执行Minor GC,如果Minor GC执行失败则会执行Full GC 使用了大对象 //大对象会直接进入老年代 在程序中长期持有了对象的引用 //对象年龄达到指定阈值也会进入老年代 对于我们的情况,可以初步排除1,2两种情况,最有可能是4和5这两种情况。为了进一步排查原因,我们在线上开启了 -XX:+HeapDumpBeforeFullGC。 注意: JVM在执行dump操作的时候是会发生stop the word事件的,也就是说此时所有的用户线程都会暂停运行。 为了在此期间也能对外正常提供服务,建议采用分布式部署,并采用合适的负载均衡算法 JVM参数的设置: 线上这个dubbo服务是分布式部署,在其中一台机子上开启了 -XX:HeapDumpBeforeFullGC,总体JVM参数如下: -Xmx2g -XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=. -Xloggc:gc.log -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100m -XX:HeapDumpOnOutOfMemoryError Dump文件分析 dump下来的文件大约1.8g,用jvisualvm查看,发现用char[]类型的数据占用了41%内存,同时另外一个com.alibaba.druid.stat.JdbcSqlStat类型的数据占用了35%的内存,也就是说整个堆中几乎全是这两类数据。如下图: 查看char[]类型数据,发现几乎全是sql语句。 接下来查看char[]的引用情况: 找到了JdbcSqlStat类,在代码中查看这个类的代码,关键代码如下: 构造函数只有这一个 public JdbcSqlStat(String sql){ this.sql = sql; this.id = DruidDriver.createSqlStatId(); } 查看这个函数的调用情况,找到com.alibaba.druid.stat.JdbcDataSourceStat#createSqlStat方法: public JdbcSqlStat createSqlStat(String sql) { lock.writeLock().lock(); try { JdbcSqlStat sqlStat = sqlStatMap.get(sql); if (sqlStat == null) { sqlStat = new JdbcSqlStat(sql); sqlStat.setDbType(this.dbType); sqlStat.setName(this.name); sqlStatMap.put(sql, sqlStat); } return sqlStat; } finally { lock.writeLock().unlock(); } } 这里用了一个map来存放所有的sql语句。 其实到这里也就知道什么原因造成了这个问题,因为我们使用的数据源是阿里巴巴的druid,这个druid提供了一个sql语句监控功能,同时我们也开启了这个功能。只需要在配置文件中把这个功能关掉应该就能消除这个问题,事实也的确如此,关掉这个功能后到目前为止线上没再触发FullGC 其他 如果用mat工具查看,建议把 "Keep unreachable objects" 勾上,否则mat会把堆中不可达的对象去除掉,这样我们的分析也许会变得没有意义。如下图:Window-->References 。另外jvisualvm对ool的支持不是很好,如果需要oql建议使用mat。
我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。
目标 - 实现返回当前服务器时间的功能 - 采用半双工模式(主要是因为采用telnet为客户端,然后telnet默认情况下是半双工) - 客户端连接后,可以发送 time,stop命令. 只有客户端发送stop命令后,服务段才主动断开链路。 - 不考虑读半包和写半包的情况 代码和注释 废话不多说,尽在代码中。直接拷贝即可运行(jdk7或以上) package com.aio; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.Date; import java.util.concurrent.CountDownLatch; /** * Author : Rocky * Date : 21/12/2016 15:17 * Description : * Test : */ public class TimeServer { public static void main(String[] args) throws InterruptedException { AsynchronousServerSocketChannel assc = null; try { assc = AsynchronousServerSocketChannel.open(); assc.bind(new InetSocketAddress(8888)); } catch (IOException e) { e.printStackTrace(); System.exit(1); } doAccept(assc); CountDownLatch latch = new CountDownLatch(1); latch.await(); } private static void doAccept(AsynchronousServerSocketChannel assc) { assc.accept(assc, new AcceptCompletionHandle()); } private static class AcceptCompletionHandle implements CompletionHandler<AsynchronousSocketChannel, AsynchronousServerSocketChannel> { @Override public void completed(AsynchronousSocketChannel result, AsynchronousServerSocketChannel assc) { //继续监听accept事件 assc.accept(assc, this); //开始监听可读时间 ByteBuffer readBuf = ByteBuffer.allocate(1024); result.read(readBuf, readBuf, new ReadCompletionHandler(result)); } @Override public void failed(Throwable exc, AsynchronousServerSocketChannel assc) { System.out.println("accept异常,继续accept"); assc.accept(assc, this); } } private static class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer> { private AsynchronousSocketChannel asc; public ReadCompletionHandler(AsynchronousSocketChannel asc) { this.asc = asc; } @Override public void completed(Integer result, ByteBuffer readedData) { //如果对端链路关闭 if (result < 0) { try { asc.close(); } catch (IOException e) { e.printStackTrace(); } return; } //如果读取到对端发送过来的数据 if (result > 0) { readedData.flip(); byte[] data = new byte[readedData.remaining()]; readedData.get(data); String command = null; try { command = new String(data, "UTF-8"); if ("time\r\n".equalsIgnoreCase(command)) { doWrite(new Date().toString() + "\r\n"); } else if ("stop\r\n".equalsIgnoreCase(command)) { doWriteAndClose("bye.\r\n"); } else if ("\r\n".equalsIgnoreCase(command)) { doWrite("\r\n"); } else { doWrite("unknown command\r\n"); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); doWrite("server error\r\n"); } } //如果未读取到数据 else { //继续尝试读取对端发送的数据 ByteBuffer readBuf = ByteBuffer.allocate(1024); asc.read(readBuf, readBuf, this); } } private void doWriteAndClose(String response) { ByteBuffer repBuf = null; try { repBuf = ByteBuffer.wrap(response.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } if (repBuf != null) { asc.write(repBuf, repBuf, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer result, ByteBuffer repBuf) { if (repBuf.hasRemaining()) { asc.write(repBuf, repBuf, this); } //写完成后,关闭链路 else { try { asc.close(); } catch (IOException e) { e.printStackTrace(); } } } @Override public void failed(Throwable exc, ByteBuffer repBuf) { exc.printStackTrace(); try { asc.close(); } catch (IOException e) { e.printStackTrace(); } } }); } } private void doWrite(String response) { ByteBuffer repBuf = null; try { repBuf = ByteBuffer.wrap(response.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } if (repBuf != null) { asc.write(repBuf, repBuf, new WriteCompletionHandler(asc, this)); } } @Override public void failed(Throwable exc, ByteBuffer readedData) { exc.printStackTrace(); try { asc.close(); } catch (IOException e) { e.printStackTrace(); } } } private static class WriteCompletionHandler implements CompletionHandler<Integer, ByteBuffer> { private AsynchronousSocketChannel asc; private ReadCompletionHandler rch; public WriteCompletionHandler(AsynchronousSocketChannel asc, ReadCompletionHandler rch) { this.asc = asc; this.rch = rch; } @Override public void completed(Integer result, ByteBuffer repBuf) { if (repBuf.hasRemaining()) { asc.write(repBuf, repBuf, this); } //写完成后(对端读取完成),再尝试读(半双工模式) else { //继续尝试读取对端发送的数据 ByteBuffer readBuf = ByteBuffer.allocate(1024); asc.read(readBuf, readBuf, rch); } } @Override public void failed(Throwable exc, ByteBuffer repBuf) { exc.printStackTrace(); try { asc.close(); } catch (IOException e) { e.printStackTrace(); } } } } 测试 telnet 127.0.0.1 8888 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. time Wed Dec 21 17:05:44 CST 2016 time Wed Dec 21 17:07:44 CST 2016 s unknown command f unknown command stop bye. Connection closed by foreign host.
Linux系统日志管理 日志分类: 1. 连接时间的日志 连接时间日志一般由/var/log/wtmp和/var/run/utmp这两个文件记录,不过这 两个文件无法直接cat查看,并且该文件由系统自动更新,可以通过如下: w/who/finger/id/last/lastlog/ac 进行查看 [root@xhot ~]# who root tty1 2010-10-06 22:56 root pts/0 2010-10-06 22:26 (218.192.87.4) root pts/1 2010-10-06 23:41 (218.192.87.4) root pts/3 2010-10-06 23:18 (218.192.87.4) [root@xhot ~]# w 01:01:02 up 2:36, 4 users, load average: 0.15, 0.03, 0.01 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root tty1 - 22:56 1:20m 0.16s 0.16s -bash root pts/0 218.192.87.4 22:26 2:05m 0.18s 0.18s -bash root pts/1 218.192.87.4 23:41 0.00s 0.41s 0.00s w root pts/3 218.192.87.4 23:18 1:38m 0.03s 0.03s -bash [root@xhot ~]# ac -p //查看每个用户的连接时间 u51 1.23 u55 0.04 root 95.21 //可以看到root连接时间最长吧 xhot 0.06 user1 3.93 total 100.48 [root@xhot ~]# ac -a //查看所有用户的连接时间 total 100.49 [root@xhot ~]# ac -d //查看用户每天的连接时间 Sep 24 total 0.14 Sep 25 total 14.60 Sep 26 total 13.71 Sep 27 total 21.47 Sep 28 total 11.74 Sep 29 total 6.60 Sep 30 total 8.81 Oct 1 total 9.04 Oct 2 total 0.47 //可以看到我国庆3、4、5号出去玩了 Oct 6 total 8.62 Today total 5.29 其他几个命令不做具体介绍了 2. 进程监控日志 进程统计监控日志在监控用户的操作指令是非常有效的。当服务器最近发现经常 无故关机或者无故被人删除文件等现象时,可以通过使用进程统计日志查看: [root@xhot ~]# accton /var/account/pacct //开启进程统计日志监控 [root@xhot ~]# lastcomm //查看进程统计日志情况 accton S root pts/1 0.00 secs Thu Oct 7 01:20 accton root pts/1 0.00 secs Thu Oct 7 01:20 ac root pts/1 0.00 secs Thu Oct 7 01:14 ac root pts/1 0.00 secs Thu Oct 7 01:14 free root pts/1 0.00 secs Thu Oct 7 01:10 lastcomm root pts/1 0.00 secs Thu Oct 7 01:09 bash F root pts/1 0.00 secs Thu Oct 7 01:09 lastcomm root pts/1 0.00 secs Thu Oct 7 01:09 ifconfig root pts/1 0.00 secs Thu Oct 7 01:09 lastcomm root pts/1 0.00 secs Thu Oct 7 01:09 lastcomm root pts/1 0.00 secs Thu Oct 7 01:09 lastcomm root pts/1 0.00 secs Thu Oct 7 01:09 accton S root pts/1 0.00 secs Thu Oct 7 01:09 [root@xhot ~]# accton //关闭进程统计日志监控 3. 系统和服务日志 系统日志服务是由一个名为syslog的服务管理的,如一下日志文件都是由syslog日志服务驱动的: /var/log/lastlog :记录最后一次用户成功登陆的时间、登陆IP等信息 /var/log/messages :记录Linux操作系统常见的系统和服务错误信息 /var/log/secure :Linux系统安全日志,记录用户和工作组变坏情况、用户登陆认证情况 /var/log/btmp :记录Linux登陆失败的用户、时间以及远程IP地址 /var/log/cron :记录crond计划任务服务执行情况 …... [root@xhot ~]# cat /var/log/lastlog Lpts/0218.192.87.4 Lpts/1218.192.87.4 Lpts/1218.192.87.4 Lpts/0218.192.87.46 Lpts/0218.192.87.4 …... Linux日志服务介绍 1. 在Linux系统,大部分日志都是由syslog日志服务驱动和管理的 syslog服务由两个重要的配置文件控制管理,分别是/etc/syslog.conf主配置文件和/etc/sysconfig/syslog辅助 配置文件, /etc/init.d/syslog是启动脚本,这里主讲主配置文件/etc/syslog.conf: /etc/syslog.conf 语句结构: [root@xhot ~]# grep -v "#" /etc/syslog.conf //列出非#打头的每一行 *.info;mail.none;authpriv.none;cron.none /var/log/messages authpriv.* /var/log/secure mail.* -/var/log/maillog cron.* /var/log/cron *.emerg * uucp,news.crit /var/log/spooler local7.* /var/log/boot.log 选择域(消息类型.错误级别) 动作域 2. 消息类型:auth,authpriv,security;cron,daemon,kern,lpr,mail, mark,news,syslog,user,uucp,local0~local7. 错误级别:(8级)debug,info,notice,warning|warn;err|error;crit,alert,emerg|panic 动作域:file,user,console,@remote_ip 举如上的/etc/syslog.conf文件三个例子: *.info;mail.none;authpriv.none;cron.none /var/log/messages 表示info级别的任何消息都发送到/var/log/messages日志文件,但邮件系统、验证系统 和计划任务的错误级别信息就除外,不发送(none表示禁止) cron.* /var/log/cron 表示所有级别的cron信息发到/var/log/cron文件 *.emerg * 表示emerg错误级别(危险状态)的所有消息类型发给所有用户 Linux日志服务器配置 此服务器的配置非常简单,只是修改一个文件的一个地方,然后重启服务即可: [root@xhot ~]# grep -v "#" /etc/sysconfig/syslog SYSLOGD_OPTIONS="-m 0 -r" //只要在这里添加“-r”就行咯 KLOGD_OPTIONS="-x" SYSLOG_UMASK=077 [root@xhot ~]# service syslog restart 关闭内核日志记录器: [确定] 关闭系统日志记录器: [确定] 启动系统日志记录器: [确定] 启动内核日志记录器: [确定] 对于发送消息到服务器的OS,只要在写/etc/syslog.conf主配置文件的时候,作用域 为@server-ip就行了,比如针对218.192.87.24这台日志服务器,把一台ubuntu系统的所有 info级别的auth信息发给日志服务器,那么对于ubuntu系统的/etc/syslog.conf文件最后一 行添加 auth.info @218.192.87.24 就OK了 日志转储服务 系统工作到了一定时间后,日志文件的内容随着时间和访问量的增加而越来越多, 日志文件也越来越大。而且当日志文件超过系统控制范围时候,还会对系统性能 造成影响。转储方式可以设为每年转储、每月转储、每周转储、达到一定大小转储。 在Linux系统,经常使用“logrotate”工具进行日志转储,结合cron计划任务,可以轻松 实现日志文件的转储。转储方式的设置由“/etc/logrotate.conf”配置文件控制: [root@xhot ~]# cat /etc/logrotate.conf # see "man logrotate" for details //可以查看帮助文档 # rotate log files weekly weekly //设置每周转储 # keep 4 weeks worth of backlogs rotate 4 //最多转储4次 # create new (empty) log files after rotating old ones create //当转储后文件不存储时创建它 # uncomment this if you want your log files compressed #compress //以压缩方式转储 # RPM packages drop log rotation information into this directory include /etc/logrotate.d //其他日志文件的转储方式,包含在该目录下 # no packages own wtmp -- we'll rotate them here /var/log/wtmp { //设置/var/log/wtmp日志文件的转储参数 monthly //每月转储 create 0664 root utmp //转储后文件不存在时创建它,文件所有者为root, 所属组为utmp,对应的权限为0664 rotate 1 //转储一次 } # system-specific logs may be also be configured here. 举两个例子: 为/var/log/news/目录下的所有文件设置转储参数,每周转储,转储2次,转储 时将老的日志文件放到/var/log/news/old目录下,若日志文件不存在,则跳过。完成后重启 news新闻组服务,转储时不压缩。那么可以在/etc/logrotate.conf文件的最后添加如下: /var/log/news/*{ monthly rotate 2 olddir /var/log/news/old missingok postrotate kill -HUP `cat /var/run/inn.pid` endscript nocompress } 另一个例子:为/var/log/httpd/access.log和/var/log/httpd/error.log日志设置转储参数。转储 5次,转储时发送邮件给root@localhost用户,当日志文件达到100KB时才转储,转储后重启 httpd服务,那么可以直接在/etc/logrotate.conf文件的最后添加如下: /var/log/httpd/access.log /var/log/http/error.log{ rotate 5 mail root@localhost size=100k sharedscripts /sbin/killall -HUP httpd endscript } 自定义日志转储(/etc/logrotate.d/*) 通过下面一个例子将所有类型错误级别为info的日志转储到/var/log/test.log日志文件中,并设置 /var/log/test.log达到50KB后进行转储,转储10次,转储时压缩,转储后重启syslog服务: 1、修改/etc/syslog.conf文件使得如下: [root@xhot ~]# tail -1 /etc/syslog.conf //查看该文件的最后一行 *.info /var/log/test.log 2、重启syslog服务: [root@xhot ~]# /sbin/service syslog restart 关闭内核日志记录器: [确定] 关闭系统日志记录器: [确定] 启动系统日志记录器: [确定] 启动内核日志记录器: [确定] 3、创建/etc/logrotate.d/test.log日志转储参数配置文件,添加如下: [root@xhot ~]# vim /etc/logrotate.d/test.log [root@xhot ~]# cat /etc/logrotate.d/test.log /var/log/test.log{ rotate 10 size = 50k compress postrotate killall -HUP syslog endscript } 4、查看文件/etc/cron.daily/logrotate确保如下: [root@xhot ~]# cat /etc/cron.daily/logrotate #!/bin/sh /usr/sbin/logrotate /etc/logrotate.conf EXITVALUE=$? if [ $EXITVALUE != 0 ]; then /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]" fi exit 0 5、查看转储后的文件 [root@xhot log]# pwd /var/log [root@xhot log]# ls test.log* …… //结果等要转储的时候会发现压缩文件和原本的test.log文件 原文链接:http://www.cnblogs.com/php5/archive/2010/11/10/1873206.html
BTrace入门 概念性的东西自行网上查阅吧。 安装 现在btrace项目已经迁移到了github上了。这点可以从官网( https://kenai.com/projects/btrace/downloads/directory/releases/latest )上看出。 点开这个链接就会跳转到btrace项目的github地址:https://github.com/btraceio/btrace 下面直接给出下载地址:https://github.com/btraceio/btrace/releases 我这里下载的版本是 https://github.com/btraceio/btrace/releases/download/v1.3.8.1/btrace-bin-1.3.8.1.tgz 下面的安装步骤主要就是设置环境变量,其实也可以不设置。这里设置下只是为方便执行命令而已。另外各种操作系统设置环境变量的方法也有所不同,我这里是采用的 mac 系统,采用的终端软件我也改变了一下,所以需要各自根据自己的实际情况进行设置。 下载完成后,执行: mkdir btrace tar -zxvf btrace-bin-1.3.8.1.tgz -C btrace 假设btrace路径为 : /Applications/btrace 下面只需要设置 BTRACE_HOME 和 PATH 两个环境变量即可。 注意我这里修改的文件是~/.zshrc文件。这个要看各自使用的操作系统以及所使用的terminal客户端而定。具体是哪个文件请各自查阅相关资料。 vim ~/.zshrc 拉到文件末尾,添加以下代码:(建议不要去修改已有的配置,只需要在末尾追加即可) export BTRACE_HOME="/Applications/btrace" export PATH=$BTRACE_HOME/bin:$PATH 保存文件,执行: source ~/.zshrc 基本用法 btrace pid btrace脚本 编写测试类 package com.btrace; import java.util.Random; import java.util.UUID; public class RemoteClass { public String f1(String a, int b) { System.out.println(a + " " + b); return a; } public static void main(String[] args) { RemoteClass rc = new RemoteClass(); while (true) { rc.f1(UUID.randomUUID().toString(), new Random().nextInt()); try { Thread.sleep(500); } catch (InterruptedException e) { } } } } 然后启动main方法,并找到pid jps -ml 我这里的pid 为:13705 编写btrace脚本 我这里的脚本保存路径为:/Users/xxx/Desktop/btrace_script cd /Users/xxx/Desktop/btrace_script Btrace初体验 功能: 每次执行完f1后,打印 “Hello BTrace” import com.sun.btrace.annotations.*; import static com.sun.btrace.BTraceUtils.*; import java.lang.reflect.Field; @BTrace public class HelloBtrace { @OnMethod( clazz="com.btrace.RemoteClass", method="f1" ) public static void onF1() { println("Hello BTrace"); } } 保存为:HelloBtrace.java 执行脚本: btrace 13705 HelloBtrace.java 获取方法参数 功能:得到参数 注意:参数的定义,顺序,个数保持一致。(个数我这边测试是必须保持相同的,至于类型,顺序是否必须保持一致就没有测试了) import com.sun.btrace.annotations.*; import static com.sun.btrace.BTraceUtils.*; import java.lang.reflect.Field; @BTrace public class HelloBtrace { @OnMethod( clazz="com.btrace.RemoteClass", method="f1" ) public static void onF1(String a,int b) { println(str(a)); println(str(b)); println(""); } } 执行脚本: btrace 13705 HelloBtrace.java 获取方法返回值 功能:获取方法的返回值 import com.sun.btrace.annotations.*; import static com.sun.btrace.BTraceUtils.*; import java.lang.reflect.Field; @BTrace public class HelloBtrace { @OnMethod( clazz="com.btrace.RemoteClass", method="f1", location=@Location(Kind.RETURN) ) public static void onF1(String a,int b,@Return String result) { println(str(a)); println(str(b)); println(str(result)); println(""); } } 执行脚本: btrace 13705 HelloBtrace.java 更多功能 参考1:https://kenai.com/projects/btrace/pages/UserGuide 参考2:btrace的安装目录下有个 samples 文件夹,里面有各种示例 总结 对Btrace的用法主要就是学习用提供的各种注解,以及com.sun.btrace.BTraceUtils类提供的各种功能。 如果想要了解各个注解的文档,可以参考:https://btrace.kenai.com/javadoc/1.2/com/sun/btrace/annotations/package-summary.html
Thread & ExecutorService & ThreadPoolExecutor 总览 ExecutorService 类方法 shutdown 允许已经提交的任务(尚未开始执行和已经开始执行的)继续执行 shutdownNow 尚未开始执行的任务不再执行,同时尝试终止正在执行的任务 无论是shutdown 还是shutdownNow,两个的执行都会阻止新的任务提交 一个ExecutorService一旦termination,表明没有正在执行的任务,没有等待执行的任务,也不会有新的任务可以被提交。 如果一个ExecutorService不再使用,应该调用shutdown方法来回收资源。 submit方法(三个重载方法) 返回的Future对象可以用来取消任务和等待任务执行完成 invokeAny和invokeAll方法 用户批量执行任务, invokeAny:会阻塞当前线程,直到某个任务完成。并返回这个任务相关的Future对象 invokeAll:会阻塞当前线程,直到所有任务完成。 两阶段shutdown 先执行shutdown方法 调用awaitTermination方法 再调用shutdownNow方法 void shutdownAndAwaitTermination(ExecutorService pool) { pool.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being cancelled if (!pool.awaitTermination(60, TimeUnit.SECONDS)) System.err.println("Pool did not terminate"); } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted pool.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); } } isShutdown和isTerminated分别对应于两个状态:关闭状态,终结状态 Thread interrupt方法 如果执行a.interrupt方法后,如果a线程(注意是a线程,不是调用线程)抛出了InterruptedException异常,那么a的中断状态会被清除。如果不是抛出InterruptedException异常,那么a的中断状态都会被设置。 interrupted方法 执行a.interrupted方法会返回a线程的中断状态,同时会清除a线程的中断状态 isInterrupted方法 执行a.interrupted方法会返回a线程的中断状态,不会清除a线程的中断状态 ThreadPoolExecutor core pool size 及 max pool size 一个新的任务提交哪些情况下回创建新的线程: 1. 已创建的的线程数小于corePoolSize(即便有线程是空闲的) 2. 已创建的线程数大于corePoolSize小于maxPoolSize,同时任务队列已经满的情况下,也会创建新的线程 可以动态改变这两个的值:setCorePoolSize 以及 setMaximumPoolSize 如果corePoolSize==maxinumPoolSize,那么则创建了一个固定大小的线程池 keep alive time 如果已创建的线程大于了corePoolSize,并且如果有线程的空闲时间大于了keepAliveTime,那么这些线程会被kill掉直到剩下corePoolSize个线程。 可以动态设置:setKeepAliveTime方法 默认情况下keep-alive策略只会针对已创建线程数大于corePoolSize的情况下 可以通过执行allowCoreThreadTimeOut(boolean)让keep-alive策略应用在已创建线程数小于corePoolSize的情况下。 BlockingQueue 如果已创建线程数小于corePoolSize,那么会创建新的线程来执行当前提交的任务,而不是进入阻塞队列 如果已创建线程数大于等于corePoolSize,会尝试先进入阻塞队列,如果进入失败(其实就是队列已满),则会在maxPoolSize条件下创建新的线程来执行当前提交的任务。如果不满足maxPoolSize条件,那么就会执行 拒绝执行策略(默认的拒绝执行策略见下) 通常有三种入队列策略 直接传递给线程(Direct handoffs) 比如:SychronousQueue 感觉可以理解为这个入队列会总是失败,就相当于没有这个队列一样。这样就能在maxPoolSize条件下尽可能快的创建(或选择空闲的线程)来执行新提交的任务。 如果提交的任务有互相的依赖性,可以考虑使用这种队列。 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } 无界队列(Unbounded Queue) 比如:LinkedBlockingQueue 可以理解为如果有任务需要入队列,那么总会入队成功。 因此按照创建新线程的条件,理论上不会有超过corePoolSize个数的线程。也就是说理论上线程数最多为corePoolSize,因此maxPoolSize的设置也就显得没有意义了。 如果提交的任务互相间没有依赖性,可以考虑使用这种队列 有界队列(Bounded Queue) 比如:ArrayBlockingQueue 如果使用有限个maxPoolSize,那么使用这种队列可以防止资源的耗尽。 使用长队列和小的线程池,可以降低CPU使用率,降低系统资源的消耗,以及降低线程上下文切换的消耗,但是会导致低吞吐量。如果任务频繁的阻塞,系统可能会创建比允许的线程数多的线程。 使用短队列和大的线程池,可以提高CPU使用率,但也有可能导致吞吐量下降。 拒绝执行策略(我自己的叫法,实际上就是 RejectedExceptionHandler ) ThreadPoolExecutor.AbortPolicy 抛出RejectedExecutionException异常 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } ThreadPoolExecutor.CallerRunsPolicy 在调用线程上执行(哪个线程提交的任务就哪个线程执行) public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } ThreadPoolExecutor.DiscardPolicy 直接放弃 ``` public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } ``` ThreadPoolExecutor.DiscardOldestPolicy 放弃当前队列中第一个任务 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } Finalization 一个在程序中不再被引用的线程池如果同时没有剩余的线程,那么这个线程池会被自动的shutdown. 因此如果你想即便在忘记执行shutdown方法的时候仍能正常关闭线程池,那么建议设置一个有限的keepAliveTime(针对大于线程数大于corePoolSize的那些线程),同时也执行下 allowCoreThreadTimeOut(boolean) . 欢迎关注公众号
三个角色 客户端 代理服务器 目标服务器 正向代理 目标服务器对客户端是可见的(无论代理服务器是否存在),只是可能存在客户端必须要经过代理服务器才能访问目标服务器的可能(比如访问www.google.com) 反向代理 目标服务器对客户端不可见,客户端只能通过代理服务器才能访问目标服务器,客户端根本不知道代理服务器后端有哪些目标服务器。 图例 上面:正向代理 下面:反向代理
直接上图,一目了然 配置文件 web.xml文件配置 pom.xml文件配置 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>aaa</groupId> <artifactId>aaa</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>aaa Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> <profiles> <!--开发环境--> <profile> <id>dev</id> <!--在这里定义property就相当于在property文件中定义属性一样--> <properties> <env>dev</env> </properties> <build> <filters> <!--每个property文件都需要写一个filter--> <filter>src/main/env/dev/dubbo.properties</filter> <filter>src/main/env/dev/mysql.properties</filter> <filter>src/main/env/dev/sms.properties</filter> </filters> </build> </profile> <!--测试环境--> <profile> <id>test</id> <properties> <env>test</env> </properties> <build> <filters> <filter>src/main/env/test/dubbo.properties</filter> <filter>src/main/env/test/mysql.properties</filter> <filter>src/main/env/test/sms.properties</filter> </filters> </build> </profile> <!--线上环境--> <profile> <id>product</id> <properties> <env>product</env> </properties> <build> <filters> <filter>src/main/env/product/dubbo.properties</filter> <filter>src/main/env/product/mysql.properties</filter> <filter>src/main/env/product/sms.properties</filter> </filters> </build> </profile> </profiles> <!--配置所有profile都公用的信息--> <build> <finalName>aaa</finalName> <!--指定资源目录 配置是否启用资源过滤(就是是否启用占位符替换)--> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <!--通过这个插件可以对web.xml文件进行资源过滤--> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <webResources> <resource> <filtering>true</filtering> <directory>src/main/webapp</directory> <includes> <include>**/web.xml</include> </includes> </resource> </webResources> </configuration> </plugin> </plugins> </build> </project> 执行效果(以开发环境示例)
安装过程参考:http://blog.csdn.net/motian06/article/details/19117407
反复执行下面的命令: dpkg --get-selections | grep mysql 得到一个list,下面的命令就是卸载掉这个list里的软件 libdbd-mysql-perl libmysqlclient18 mysql-client-5.5 mysql-client-core-5.5 mysql-common mysql-server mysql-server-5.5 mysql-server-core-5.5 卸载命令: #注:命令可能不一样,ubuntu15下有点不一样 apt-get –purge remove mysql-server #ubuntu15系统: apt-get purge mysql-server 直到list中的所有都给卸载调,然后执行: #就是删除可能残留的mysql相关的文件 rm -rf /var/lib/mysql rm -rf /etc/mysql 最后执行: apt-get autoremove apt-get autoclean 至此彻底卸载掉了mysql,剩下的就是重新安装了 apt-get install mysql-server
在Ubuntu 14.04中安装了SublimeText 3之后发现既然不支持输入中文,于是在网上搜罗一下,发现很多人遇到了同样的问题,但是解决办法大该就只有一个。下面根据自身的安装及解决办法总结如下: SublimeText 3的安装 安装方式有多种,本文所描述的是从官方网站上下载64位的.deb文件 ,具体为http://c758482.r82.cf2.rackcdn.com/sublime-text_build-3059_amd64.deb文件,下载后双击即会自动使用默认的安装软件安装。 相关依赖软件的安装 sudo apt-get install build-essential libgtk2.0-dev 拷贝如下代码到文件sublime_imfix.c文件中,该文件需要自己创建,随便放到那里都行。 /* * sublime-imfix.c * Use LD_PRELOAD to interpose some function to fix sublime input method support for linux. * By Cjacker Huang <jianzhong.huang at i-soft.com.cn> * * * gcc -shared -o libsublime-imfix.so sublime_imfix.c `pkg-config --libs --cflags gtk+-2.0` -fPIC * LD_PRELOAD=./libsublime-imfix.so sublime_text */ #include <gtk/gtk.h> #include <gdk/gdkx.h> typedef GdkSegment GdkRegionBox; struct _GdkRegion { long size; long numRects; GdkRegionBox *rects; GdkRegionBox extents; }; GtkIMContext *local_context; void gdk_region_get_clipbox (const GdkRegion *region, GdkRectangle *rectangle) { g_return_if_fail (region != NULL); g_return_if_fail (rectangle != NULL); rectangle->x = region->extents.x1; rectangle->y = region->extents.y1; rectangle->width = region->extents.x2 - region->extents.x1; rectangle->height = region->extents.y2 - region->extents.y1; GdkRectangle rect; rect.x = rectangle->x; rect.y = rectangle->y; rect.width = 0; rect.height = rectangle->height; //The caret width is 2; //Maybe sometimes we will make a mistake, but for most of the time, it should be the caret. if (rectangle->width == 2 && GTK_IS_IM_CONTEXT(local_context)) { gtk_im_context_set_cursor_location(local_context, rectangle); } } //this is needed, for example, if you input something in file dialog and return back the edit area //context will lost, so here we set it again. static GdkFilterReturn event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer im_context) { XEvent *xev = (XEvent *)xevent; if (xev->type == KeyRelease && GTK_IS_IM_CONTEXT(im_context)) { GdkWindow *win = g_object_get_data(G_OBJECT(im_context), "window"); if (GDK_IS_WINDOW(win)) { gtk_im_context_set_client_window(im_context, win); } } return GDK_FILTER_CONTINUE; } void gtk_im_context_set_client_window (GtkIMContext *context, GdkWindow *window) { GtkIMContextClass *klass; g_return_if_fail (GTK_IS_IM_CONTEXT (context)); klass = GTK_IM_CONTEXT_GET_CLASS (context); if (klass->set_client_window) { klass->set_client_window (context, window); } if (!GDK_IS_WINDOW (window)) { return; } g_object_set_data(G_OBJECT(context), "window", window); int width = gdk_window_get_width(window); int height = gdk_window_get_height(window); if (width != 0 && height != 0) { gtk_im_context_focus_in(context); local_context = context; } gdk_window_add_filter (window, event_filter, context); } 按照文件头上注释所说的编译该文件,在终端里进入到存放该文件的目录中,输入如下命令: gcc -shared -o libsublime-imfix.so sublime_imfix.c `pkg-config --libs --cflags gtk+-2.0` -fPIC 最后在当前目录下得到libsublime-imfix.so这个共享库。 中文输入 到这里默认已经装好了中文输入法(搜狗输入法linux版)。得到第3步中的库libsublime-imfix.so之后,先试试看是否能正常使用中文输入法,在终端中输入如下命令: LD_PRELOAD=./libsublime-imfix.so subl #subl是安装好SublimeText 3后的程序启动命令 如果一切正常,在启动之后,搜狗输入法就能可以输入了。 为了方便 在第4步中如果每次都输入LD_PRELOAD这样显得太不方便了,在这里提供简单修改图标连接的方式,快速打开SublimeText。将libsublime-imfix.so拷贝到系统库的默认路径中: sudo cp libsublime-imfix.so /usr/lib/ 修改/usr/share/applications/sublime_text.desktop文件 sudo vim /usr/share/applications/sublime_text.desktop 打开后将Exec=/opt/sublime_text/sublime_text %F修改为 Exec=bash -c ‘LD_PRELOAD=/usr/lib/libsublime-imfix.so /opt/sublime_text/sublime_text %F’ 将Exec=/opt/sublime_text/sublime_text -n修改为 Exec=bash -c ‘LD_PRELOAD=/usr/lib/libsublime-imfix.so /opt/sublime_text/sublime_text -n’ 这样就通过快捷方式打开SublimeText 3就可以支持中文输入了。 原文链接:http://blog.csdn.net/cywosp/article/details/32350899 对原文小小做了下修改(修改desktop文件那部分): 原文: 将Exec=/opt/sublime_text/sublime_text %F修改为 Exec=bash -c 'LD_PRELOAD=/usr/lib/libsublime-imfix.so /opt/sublime_text/sublime_text' %F 将Exec=/opt/sublime_text/sublime_text -n修改为 Exec=bash -c 'LD_PRELOAD=/usr/lib/libsublime-imfix.so /opt/sublime_text/sublime_text' -n 修改后: 将Exec=/opt/sublime_text/sublime_text %F修改为 Exec=bash -c 'LD_PRELOAD=/usr/lib/libsublime-imfix.so /opt/sublime_text/sublime_text %F' 将Exec=/opt/sublime_text/sublime_text -n修改为 Exec=bash -c 'LD_PRELOAD=/usr/lib/libsublime-imfix.so /opt/sublime_text/sublime_text -n' 很小的一点改动。如果不把 ’ 放在参数后面,我在文件上双击打开文件,sublime并不能显示文件内容,而是新建了一个tab。
在centos中执行:yum install bitmap-fonts bitmap-fonts-cjk 在ubuntu中执行:sudo apt-get install xfonts-wqy
客户端代码 public class RMIClient { public static void main(String... args) throws RemoteException, NotBoundException, MalformedURLException { if (args == null || args.length <= 1) { System.out.println("usage : java -jar RMIClient.jar rmi_server_ip content"); System.exit(0); } IRMIService service = (IRMIService) Naming.lookup("rmi://"+args[0]+":1099/RMIServer"); System.out.println(service.speakToYourself(args[1])); //speakToYourself 这个方法仅仅就是在输入参数前面加上了另外一个字符串而已,然后返回。 } } 抓包结果 上述代码执行过程中,使用wireshark进行抓包,得到如下结果 抓包结果分析: 48-49 :tcp的三次握手 54-69 :通信数据 70-72 :断开连接 54-69中协议类型凡是tcp的,都是确认其他数据包的确认包,比如55号: 其他为rmi协议的才包含通信数据。 矛盾 oracle文档中介绍rmi的背景时说: 截图 socket要求client和server参与到应用层协议,以便对交换的信息进行编码和解码。这种协议的制定就是笨重的和容易出错的。 官方连接:https://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-intro2.html 但是跟踪rmi源码可以知道,rmi同样时通过socket来通信的。所以这让人感觉有点矛盾。。 源码截图 Connection封装 通过socket拿到Connection后,rmi对conn进行了tcpconnection的封装 写入rmi协议头部信息 对照抓取到的包信息: 两者是符合的。 原理分析 rmi底层采用了Stub 和 Skeletons机制。。 Skeletons:运行在server端,负责分发请求。接到client端的请求后,会做三件事: unmarshals 客户端发送来的数据 根据收到的数据,执行相关的方法,拿到执行结果 marshals执行结果,发送给client Stub:运行在client端,需要调用远程方法时,会做下面几件事(基本和skeleton反着来) marshals待发送数据并发送 等待结果 unmarshals收到的数据 个人认为那个skeletons就有点类似于spring的dispatcherservlet,当收到客户端请求后,负责把请求分发到相应的controller进行处理。 针对上面给出的客户端代码,客户端发送的数据就是 : "rmi://"+args[0]+":1099/RMIServer" 接收到的结果就是: IRMIService service //这样一个实例 注意 拿到 IRMIService service 这样一个实例后,当调用service的 service.speakToYourself(args[1])方法时,并没有与服务器通信。 也就是说这个方法的执行是在本地执行,而非在远程服务器上执行后再回传结果 注意,是 本地执行 那么rmi说的远程调用,怎么体现远程呢? 这个远程调用指的是,客户端发送”rmi://”+args[0]+”:1099/RMIServer”到获取service实例的过程,这个是在远程服务器上执行的。 本质 跟踪代码执行过程可以知道,从客户端发送数据,到拿到service实例的过程,其实就是 对象的序列号–>网络传输–>反序列化 的过程 上面提到的marshal 和 unmarshal的意思,其实就是 serialize和 unserialize的意思。这个可以查证wiki:https://en.wikipedia.org/wiki/Marshalling_(computer_science) ,在代码上也的确是这么回事。 底层采用的IO模型 查看源码可以知道,现在的rmi实现采用的io是bio,并没有采用jdk1.4提供的nio功能。看截图: PS:我使用的jdk是1.8版本的 JDK8版本及以前版本,RMI采用的IO模型是 BIO 其他 补充阅读:https://www.ietf.org/rfc/rfc2713.txt rmi协议rfc文档 查看tcp报文段首部的格式,发现并没有一个标示来标示tcp上层采用的是什么协议,所以凡是需要用到在tcp协议之上的协议时,都需要自己来处理获取到的数据,自己根据需要按照某种协议格式进行处理数据。 (PS:IP数据报首部是有这样的8bit的空间来标示上一层协议是什么协议) 也就是说当我们拿到通过tcp层传上来的数据时,需要我们自己按照某种协议的格式去处理数据 好比上面rmi协议,可以发现发送数据的时候,是程序自己完成写rmi首部字段工作的。 所以使用http协议时,也需要自己完成 写首部,收到数据后分析处理首部等工作。既然如此,可以验证下tomcat在处理请求的时候,是不是这样子处理收到的数据和发送数据的?
安装wireshark: sudo apt-get install wireshark 安装后打开wireshark 提示权限不足消息: Couldn’t run /usr/bin/dumpcap in child process: Permission denied 解决方案 分别执行: sudo apt-get install libcap2-bin #添加一个组,名字为 wireshark ..我执行时,提示已经存在相同名字的组了 sudo groupadd wireshark #把自己当前的用户名添加到 wireshark组 sudo usermod -a -G wireshark YOUR-USER-NAME newgrp wireshark #修改组别 sudo chgrp wireshark /usr/bin/dumpcap #添加执行权限 sudo chmod 754 /usr/bin/dumpcap 最后一步:Grant Capabilities #下面两句执行其中一句就可以了,我执行的是第一句 sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/dumpcap sudo setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' /usr/bin/dumpcap 重启系统,完成 参考文章:http://ihacklog.com/post/run-whireshark-as-non-root.html
When designing a high performance networking application with non-blocking socket I/O, the architect needs to decide which polling method to use to monitor the events generated by those sockets. There are several such methods, and the use cases for each of them are different. Choosing the correct method may be critical to satisfy the application needs. This article highlights the difference among the polling methods and provides suggestions what to use. Old, trusted workforce from the times the sockets were still called Berkeley sockets. It didn’t make it into the first specification though since there were no concept of non-blocking I/O at that moment, but it did make it around eighties, and nothing changed since that in its interface. To use select, the developer needs to initialize and fill up several fd_set structures with the descriptors and the events to monitor, and then call select(). A typical workflow looks like that: Select fd_set fd_in, fd_out; struct timeval tv; // Reset the sets FD_ZERO( &fd_in ); FD_ZERO( &fd_out ); // Monitor sock1 for input events FD_SET( sock1, &fd_in ); // Monitor sock2 for output events FD_SET( sock1, &fd_out ); // Find out which socket has the largest numeric value as select requires it int largest_sock = sock1 > sock2 ? sock1 : sock2; // Wait up to 10 seconds tv.tv_sec = 10; tv.tv_usec = 0; // Call the select int ret = select( largest_sock + 1, &fd_in, &fd_out, NULL, &tv ); // Check if select actually succeed if ( ret == -1 ) // report error and abort else if ( ret == 0 ) // timeout; no event detected else { if ( FD_ISSET( sock1, &fd_in ) ) // input event on sock1 if ( FD_ISSET( sock2, &fd_out ) ) // output event on sock2 } When the select interface was designed and developed, nobody probably expected there would be multi-threaded applications serving many thousands connections. Hence select carries quite a few design flaws which make it undesirable as a polling mechanism in the modern networking application. The major disadvantages include: select modifies the passed fd_sets so none of them can be reused. Even if you don’t need to change anything – such as if one of descriptors received data and needs to receive more data – a whole set has to be either recreated again (argh!) or restored from a backup copy via FD_COPY. And this has to be done each time the select is called. To find out which descriptors raised the events you have to manually iterate through all the descriptors in the set and call FD_ISSET on each one of them. When you have 2,000 of those descriptors and only one of them is active – and, likely, the last one – you’re wasting CPU cycles each time you wait. Did I just mention 2,000 descriptors? Well, select cannot support that much. At least on Linux. The maximum number of the supported descriptors is defined by the FD_SETSIZE constant, which Linux happily defines as 1024. And while some operating systems allow you to hack this restriction by redefining the FD_SETSIZE before including the sys/select.h, this is not portable. Indeed, Linux would just ignore this hack and the limit will stay the same. You cannot modify the descriptor set from a different thread while waiting. Suppose a thread is executing the code above. Now suppose you have a housekeeping thread which decided that sock1 has been waiting too long for the input data, and it is time to cut the cord. Since this socket could be reused to serve another paying working client, the housekeeping thread wants to close the socket. However the socket is in the fd_set which select is waiting for. Now what happens when this socket is closed? man select has the answer, and you won’t like it. The answer is, “If a file descriptor being monitored by select() is closed in another thread, the result is unspecified”. Same problem arises if another thread suddenly decides to send something via sock1. It is not possible to start monitoring the socket for the output event until select returns. The choice of the events to wait for is limited; for example, to detect whether the remote socket is closed you have to a) monitor it for input and b) actually attempt to read the data from socket to detect the closure (read will return 0). Which is fine if you want to read from this socket, but what if you’re sending a file and do not care about any input right now? select puts extra burden on you when filling up the descriptor list to calculate the largest descriptor number and provide it as a function parameter. Of course the operating system developers recognized those drawbacks and addressed most of them when designing the poll method. Therefore you may ask, is there is any reason to use select at all? Why don’t just store it in the shelf of the Computer Science Museum? Then you may be pleased to know that yes, there are two reasons, which may be either very important to you or not important at all. The first reason is portability. select has been around for ages, and you can be sure that every single platform around which has network support and nonblocking sockets will have a working select implementation while it might not have poll at all. And unfortunately I’m not talking about the tubes and ENIAC here; poll is only available on Windows Vista and above which includes Windows XP – still used by the whooping 34% of users as of Sep 2013 despite the Microsoft pressure. Another option would be to still use poll on those platforms and emulate it with select on those which do not have it; it is up to you whether you consider it reasonable investment. The second reason is more exotic, and is related to the fact that select can – theoretically – handle the timeouts withing the one nanosecond precision, while both poll and epoll can only handle the one millisecond precision. This is not likely to be a concern on a desktop or server system, which clocks doesn’t even run with such precision, but it may be necessary on a realtime embedded platform while interacting with some hardware components. Such as lowering control rods to shut down a nuclear reactor – in this case, please, use select to make sure we’re all stay safe! The case above would probably be the only case where you would have to use select and could not use anything else. However if you are writing an application which would never have to handle more than a handful of sockets (like, 200), the difference between using poll and select would not be based on performance, but more on personal preference or other factors. Polling with poll() poll is a newer polling method which probably was created immediately after someone actually tried to write the high performance networking server. It is much better designed and doesn’t suffer from most of the problems which select has. In the vast majority of cases you would be choosing between poll and epoll/libevent. To use poll, the developer needs to initialize the members of struct pollfd structure with the descriptors and events to monitor, and call the poll(). A typical workflow looks like that: Poll // The structure for two events struct pollfd fds[2]; // Monitor sock1 for input fds[0].fd = sock1; fds[0].events = POLLIN; // Monitor sock2 for output fds[1].fd = sock2; fds[1].events = POLLOUT; // Wait 10 seconds int ret = poll( &fds, 2, 10000 ); // Check if poll actually succeed if ( ret == -1 ) // report error and abort else if ( ret == 0 ) // timeout; no event detected else { // If we detect the event, zero it out so we can reuse the structure if ( pfd[0].revents & POLLIN ) pfd[0].revents = 0; // input event on sock1 if ( pfd[1].revents & POLLOUT ) pfd[1].revents = 0; // output event on sock2 } poll was mainly created to fix the pending problems select had, so it has the following advantages over it: There is no hard limit on the number of descriptors poll can monitor, so the limit of 1024 does not apply here. It does not modify the data passed in the struct pollfd data. Therefore it could be reused between the poll() calls as long as set to zero the revents member for those descriptors which generated the events. The IEEE specification states that “In each pollfd structure, poll() shall clear the revents member, except that where the application requested a report on a condition by setting one of the bits of events listed above, poll() shall set the corresponding bit in revents if the requested condition is true“. However in my experience at least one platform did not follow this recommendation, and man 2 poll on Linux does not make such guarantee either (man 3p poll does though). It allows more fine-grained control of events comparing to select. For example, it can detect remote peer shutdown without monitoring for read events. There are a few disadvantages as well, which were mentioned above at the end of the select section. Notably, poll is not present on Microsoft Windows older than Vista; on Vista and above it is called WSAPoll although the prototype is the same, and it could be defined as simply as: if defined (WIN32) static inline int poll( struct pollfd *pfd, int nfds, int timeout) { return WSAPoll ( pfd, nfds, timeout ); } endif And, as mentioned above, poll timeout has the 1ms precision, which again is very unlikely to be a concern in most scenarios. Nevertheless poll still has a few issues which need to be kept in mind: Like select, it is still not possible to find out which descriptors have the events triggered without iterating through the whole list and checking the revents. Worse, the same happens in the kernel space as well, as the kernel has to iterate through the list of file descriptors to find out which sockets are monitored, and iterate through the whole list again to set up the events. Like select, it is not possible to dynamically modify the set or close the socket which is being polled (see above). Please keep in mind, however, that those issues might be considered unimportant for most client networking applications – the only exception would be client software such as P2P which may require handling of thousands of open connections. Those issues might not be important even for some server applications. Therefore poll should be your default choice over select unless you have specific reasons mentioned above. More, poll should be your preferred method even over epoll if the following is true: You need to support more than just Linux, and do not want to use epoll wrappers such as libevent (epoll is Linux only); Your application needs to monitor less than 1000 sockets at a time (you are not likely to see any benefits from using epoll); Your application needs to monitor more than 1000 sockets at a time, but the connections are very short-lived (this is a close case, but most likely in this scenario you are not likely to see any benefits from using epoll because the speedup in event waiting would be wasted on adding those new descriptors into the set – see below) Your application is not designed the way that it changes the events while another thread is waiting for them (i.e. you’re not porting an app using kqueue or IO Completion Ports). Polling with epoll() epoll is the latest, greatest, newest polling method in Linux (and only Linux). Well, it was actually added to kernel in 2002, so it is not so new. It differs both from poll and select in such a way that it keeps the information about the currently monitored descriptors and associated events inside the kernel, and exports the API to add/remove/modify those. To use epoll, much more preparation is needed. A developer needs to: Create the epoll descriptor by calling epoll_create; Initialize the struct epoll structure with the wanted events and the context data pointer. Context could be anything, epoll passes this value directly to the returned events structure. We store there a pointer to our Connection class. Call epoll_ctl( … EPOLL_CTL_ADD ) to add the descriptor into the monitoring set Call epoll_wait() to wait for 20 events for which we reserve the storage space. Unlike previous methods, this call receives an empty structure, and fills it up only with the triggered events. For example, if there are 200 descriptors and 5 of them have events pending, the epoll_wait will return 5, and only the first five members of the pevents structure will be initialized. If 50 descriptors have events pending, the first 20 would be copied and 30 would be left in queue, they won’t get lost. Iterate through the returned items. This will be a short iteration since the only events returned are those which are triggered. A typical workflow looks like that: // Create the epoll descriptor. Only one is needed per app, and is used to monitor all sockets. // The function argument is ignored (it was not before, but now it is), so put your favorite number here int pollingfd = epoll_create( 0xCAFE ); if ( pollingfd < 0 ) // report error // Initialize the epoll structure in case more members are added in future struct epoll_event ev = { 0 }; // Associate the connection class instance with the event. You can associate anything // you want, epoll does not use this information. We store a connection class pointer, pConnection1 ev.data.ptr = pConnection1; // Monitor for input, and do not automatically rearm the descriptor after the event ev.events = EPOLLIN | EPOLLONESHOT; // Add the descriptor into the monitoring list. We can do it even if another thread is // waiting in epoll_wait - the descriptor will be properly added if ( epoll_ctl( epollfd, EPOLL_CTL_ADD, pConnection1->getSocket(), &ev ) != 0 ) // report error // Wait for up to 20 events (assuming we have added maybe 200 sockets before that it may happen) struct epoll_event pevents[ 20 ]; // Wait for 10 seconds int ready = epoll_wait( pollingfd, pevents, 20, 10000 ); // Check if epoll actually succeed if ( ret == -1 ) // report error and abort else if ( ret == 0 ) // timeout; no event detected else { // Check if any events detected for ( int i = 0; i < ret; i++ ) { if ( pevents[i].events & EPOLLIN ) { // Get back our connection pointer Connection * c = (Connection*) pevents[i].data.ptr; c->handleReadEvent(); } } } Just looking at the implementation alone should give you the hint of what are the disadvantages of epoll, which we will mention firs. It is more complex to use, and requires you to write more code, and it requires more library calls comparing to other polling methods. However epoll has some significant advantages over select/poll both in terms of performance and functionality: epoll returns only the list of descriptors which triggered the events. No need to iterate through 10,000 descriptors anymore to find that one which triggered the event! You can attach meaningful context to the monitored event instead of socket file descriptors. In our example we attached the class pointers which could be called directly, saving you another lookup. You can add sockets or remove them from monitoring anytime, even if another thread is in the epoll_wait function. You can even modify the descriptor events. Everything will work properly, and this behavior is supported and documented. This gives you much more flexibility in implementation. Since the kernel knows all the monitoring descriptors, it can register the events happening on them even when nobody is calling epoll_wait. This allows implementing interesting features such as edge triggering, which will be described in a separate article. It is possible to have the multiple threads waiting on the same epoll queue with epoll_wait(), something you cannot do with select/poll. In fact it is not only possible with epoll, but the recommended method in the edge triggering mode. However you need to keep in mind that epoll is not a “better poll”, and it also has disadvantages when comparing to poll: Changing the event flags (i.e. from READ to WRITE) requires the epoll_ctl syscall, while when using poll this is a simple bitmask operation done entirely in userspace. Switching 5,000 sockets from reading to writing with epoll would require 5,000 syscalls and hence context switches (as of 2014 calls to epoll_ctl still could not be batched, and each descriptor must be changed separately), while in poll it would require a single loop over the pollfd structure. Each accept()ed socket needs to be added to the set, and same as above, with epoll it has to be done by calling epoll_ctl – which means there are two required syscalls per new connection socket instead of one for poll. If your server has many short-lived connections which send or receive little traffic, epoll will likely take longer than poll to serve them. epoll is exclusively Linux domain, and while other platforms have similar mechanisms, they are not exactly the same – edge triggering, for example, is pretty unique (FreeBSD’s kqueue supports it too though). High performance processing logic is more complex and hence more difficult to debug, especially for edge triggering which is prone to deadlocks if you miss extra read/write. Therefore you should only use epoll if all following is true: Your application runs a thread poll which handles many network connections by a handful of threads. You would lose most of epoll benefits in a single-threaded application, and most likely it won’t outperform poll. You expect to have a reasonably large number of sockets to monitor (at least 1,000); with a smaller number epoll is not likely to have any performance benefits over poll and may actually worse the performance; Your connections are relatively long-lived; as stated above epoll will be slower than poll in a situation when a new connection sends a few bytes of data and immediately disconnects because of extra system call required to add the descriptor into epoll set; Your app depends on other Linux-specific features (so in case portability question would suddenly pop up, epoll wouldn’t be the only roadblock), or you can provide wrappers for other supported systems. In the last case you should strongly consider libevent. If all the items above aren’t true, you should be better served by using poll instead. Polling with libevent libebent is a library which wraps the polling methods listed in this article (and some others) in an uniform API.Its main advantage is that it allows you to write the code once and compile and run it on many operating systems without the need to change the code. It is important to understand that libevent it is just a wrapper built on top of the existing polling methods, and therefore it inherits the issues those polling methods have. It will not make select supporting more than 1024 sockets on Linux or allow epoll to modify the polling events without a syscall/context switch. Therefore it is still important to understand each method’s pros and cons. Having to provide access to the functionality from the dramatically different methods, libevent has a rather complex API which is much more difficult to use than poll or even epoll. It is however easier to use libevent than to write two separate backends if you need to support FreeBSD (epoll and kqueue). Hence it is a viable alternative which should be considered if: Your application requirements indicate that you must use epoll, and using just poll would not be enough (if poll would satisfy your needs, it is extremely unlikely libevent would offer you any benefits) You need to support other OS than Linux, or may expect such need to arise in future. Again, this depends on other features of your application – if it is tied up to many other Linux-specific things you’re not going to achieve anything by using libevent instead of epoll 原文链接:http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/
在Linux Socket服务器短编程时,为了处理大量客户的连接请求,需要使用非阻塞I/O和复用,select、poll和epoll是Linux API提供的I/O复用方式,自从Linux 2.6中加入了epoll之后,在高性能服务器领域得到广泛的应用,现在比较出名的nginx就是使用epoll来实现I/O复用支持高并发,目前在高并 发的场景下,nginx越来越收到欢迎。这里有个文章参考。Nginx成为全球Top1000网站最受欢迎的Web服务器。 据 w3techs 7月 3 日的统计数据表明,在全球 Top 1000 的网站中,有 34.9% 的网站在使用 Nginx,这使得 Nginx 超越了 Apache,成为了高流量网站最信任的 Web 服务器。下图是统计数据。 select: 下面是select的函数接口: int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述副就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以 通过遍历fdset,来找到就绪的描述符。 select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一 个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但 是这样也会造成效率的降低。 poll: int poll (struct pollfd *fds, unsigned int nfds, int timeout); 不同与select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现。 struct pollfd { int fd; /* file descriptor */ short events; /* requested events to watch */ short revents; /* returned events witnessed */ }; pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式。同时,pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。 从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。 epoll: epoll的接口如下: int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); typedef union epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t; struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); 主要是epoll_create,epoll_ctl和epoll_wait三个函数。epoll_create函数创建epoll文件描述符,参数size并不是限制了epoll所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议。返回是epoll描述符。-1表示创建失败。epoll_ctl 控制对指定描述符fd执行op操作,event是与fd关联的监听事件。op操作有三种:添加EPOLL_CTL_ADD,删除EPOLL_CTL_DEL,修改EPOLL_CTL_MOD。分别添加、删除和修改对fd的监听事件。epoll_wait 等待epfd上的io事件,最多返回maxevents个事件。 在 select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一 个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait() 时便得到通知。 epoll的优点主要是一下几个方面: 监视的描述符数量不受限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左 右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。select的最大缺点就是进程打开的fd是有数量限制的。这对 于连接数量比较大的服务器来说根本不能满足。虽然也可以选择多进程的解决方案( Apache就是这样实现的),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也 不是一种完美的方案。 IO的效率不会随着监视fd的数量的增长而下降。epoll不同于select和poll轮询的方式,而是通过每个fd定义的回调函数来实现的。只有就绪的fd才会执行回调函数。 3.支持电平触发和边沿触发(只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发)两种方式,理论上边缘触发的性能要更高一些,但是代码实现相当复杂。 4.mmap加速内核与用户空间的信息传递。epoll是通过内核于用户空间mmap同一块内存,避免了无畏的内存拷贝。 原文链接:http://www.cnblogs.com/bigwangdi/p/3182958.html 其他相关文章: http://blog.csdn.net/sunboy_2050/article/details/5971779 http://www.cnblogs.com/huacw/p/3518461.html http://my.oschina.net/bluesky0leon/blog/132361
感受 这个类的代码除去注释差不多有千多行,要想把所有代码都读完,然后按照作者的思路给理解完,是不容易的.这里我仔仔细细读了差不多一半的代码,说难倒不是很难. 虽然没有完全看完,但是基本上理解了作者代码的意图..说得简单些,就是操作一个双向链表.而链表中的每个节点有多种状态.AQS就是要保证整个双向链表和节点的状态的正确性. 连续看了好几天的JUC相关的源码,现在脑袋真有点晕乎乎的.加上这个类的代码确实有点多,这个就不在整理这个类的分析结果,就直接把代码copy了,并附上一张图.(这里只贴出分析过的那部分代码) 如果有朋友需要一起探讨的,留言就好了. public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable { private static final long serialVersionUID = 7373984972572414691L; protected AbstractQueuedSynchronizer() { } //链接节点类 static final class Node { static final Node SHARED = new Node(); static final Node EXCLUSIVE = null; //表示当前节点的线程已经被取消 static final int CANCELLED = 1; //表示当前节点的后继节点应该被唤醒 static final int SIGNAL = -1; //表示当前节点的线程正在等待某一个条件 static final int CONDITION = -2; static final int PROPAGATE = -3; //上面四个状态之一或者0 volatile int waitStatus; volatile Node prev; volatile Node next; volatile Thread thread; Node nextWaiter; /** * Returns true if node is waiting in shared mode. */ final boolean isShared() { return nextWaiter == SHARED; } //返回前继节点 final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } Node() { // Used to establish initial head or SHARED marker } Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } } /** * Inserts node into queue, initializing if necessary. See picture above. * * @param node the node to insert * @return node's predecessor 返回插入节点的前继节点 */ //返回插入节点的前继节点 //是线程安全的方法,试想多个线程同时执行这个方法,分析代码可知是不会出现数据不一致的情况的 //假如多个线程同时执行这个方法,且此时tail为null,那么可能出现他们同时执行a行代码的情况, //但是a行代码是一个CAS操作,所以只会有一个线程执行成功,也就是说只会有一个线程才会执行b行代码. //然后所有的线程又会进入循环.然会它们可能又同时执行了c行代码 // (也就是所有的线程都把各自要插入的节点的前继节点设置为了队列的尾节点,初看上去不应该这样,但是莫慌) //然后所有的线程可能同时执行d代码,但是d行代码事CAS操作,所以也只会有个线程执行成功,也就是说只会有一个线程执行e行并返回. //而剩余的线程又会进入循环.此时尾节点已经更新,所以不会造成数据的不一致 private Node enq(final Node node) { for (; ; ) { // Node t = tail; // if (t == null) { // Must initialize // if (compareAndSetHead(new Node())) //a tail = head; //b } else { node.prev = t; //c if (compareAndSetTail(t, node)) { //d t.next = node; //e return t; } } } } /** * Creates and enqueues node for current thread and given mode. * * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared * @return the new node */ //参数的值那么是 Node.EXCLUSIVE 要么是 Node.SHARED private Node addWaiter(Node mode) { //这行代码不存在竞争资源,所以不存在线程是否安全的问题 Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure // 下面的代码会先尝试以最快的方式入队列,同时如果失败就会以常规方式入队列作为后备计划 Node pred = tail; if (pred != null) { node.prev = pred; //如果多个线程同时执行下面的代码,那么只会有个线程会返回,所以其他线程则执行a代码 if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } //以常规方式入队列 enq(node); //a return node; } /** * Sets head of queue to be node, thus dequeuing. Called only by * acquire methods. Also nulls out unused fields for sake of GC * and to suppress unnecessary signals and traversals. * * @param node the node */ private void setHead(Node node) { head = node; node.thread = null; node.prev = null; } /** * Wakes up node's successor, if one exists. * * @param node the node */ //如果输入节点存在后继节点,则唤醒之.如果不存在,则唤醒队列中第一个非CANCELLED状态的节点(如果有的话). private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ int ws = node.waitStatus; //如果当且节点对应的线程正处于 Node.SIGNAL OR Node.CONDITION OR Node.PROPAGATE 三个状态之一 //则通过CAS操作把状态更改为0 如果更改失败也没有关系 if (ws < 0) compareAndSetWaitStatus(node, ws, 0); /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ //得到后继节点 Node s = node.next; //如果后继节点不存在或者后继节点对应的线程已经被取消掉,则从尾节点向头节点遍历(排除输入节点), //只要遍历的节点的状态不是CANCELLED,则记录之.最后的效果就相当于从前往后找到整个队列中第一个非CANCELLED状态的节点(排除输入节点) if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) //如果s!=null,则s可能是输入节点的后继节点,也可能是队列中第一个非CANCELLED状态的节点(排除输入节点) //取消阻塞s节点 //需要注意的是:如果n个线程同时执行unparkSuccessor(Node)方法,且最后的s不为null,那么就会在s对应的线程上执行n次unpart操作. //这样的话,后面的n-1次unpark操作可能都是在非阻塞情况下执行.需要注意对非阻塞线程进行unpark操作的影响. LockSupport.unpark(s.thread); } /** * Release action for shared mode -- signals successor and ensures * propagation. (Note: For exclusive mode, release just amounts * to calling unparkSuccessor of head if it needs signal.) */ //建议先不要看这段总结,直接看方法内部的说明.内部看完后再来看这段总结 // //整个方法总结下来就是:要求整个方法执行过程中没有其他线程更改头节点的值,否则线程一直在循环体中不出来. //假设整个方法的执行过程中,头节点的值没有发生变化. // //那么如果有n个线程同时执行这个方法,那么最后的效果就是 //有一个线程把头节点的值更改为了0,同时执行了unpartSuccessor(h)操作. //而其余的线程中又会有一个线程会在头几点的状态更改为0之后又把头节点的状态更改为PROPAGATE //然后剩余的n-2个线程则不会更改任何东西就直接结束了方法的执行 // //上面的分析是在多个线程,头节点开始状态是SIGNAL的情况下,当然还有其他情况下的执行结果. //这里就不在一一说明.比如 // 多线程,开始状态为0的情况 :有一个线程把状态更改为了PROPAGATE,然后退出方法.其他线程不做任何改变然后退出方法 // 多线程,开始状态既不是SIGNAL也不是0的情况 :所有线程不做什么改变然后退出方法 // // 单线程,开始状态为SIGNAL的情况 :头节点状态被更改为0,且执行了unparkSuccessor操作 // 单线程,开始状态为0的情况; :头节点状态被更改为PROPAGATE // 单线程,开始状态既不是SIGNAL也不是0的情况 :线程不做什么改变然后退出方法 // //再次总结:(下面是单线程情况下.多线程可以看做是下面的流程执行了多次) //在头节点不改变的前提下 // 如果头节点的状态是SIGNAL,则把状态更改为0,同时执行unparkSuccessor操作 // 如果头节点状态是0,则把状态更改为PROPAGATE // 如果是其他状态,什么也不改变 private void doReleaseShared() { /* * Ensure that a release propagates, even if there are other * in-progress acquires/releases. This proceeds in the usual * way of trying to unparkSuccessor of head if it needs * signal. But if it does not, status is set to PROPAGATE to * ensure that upon release, propagation continues. * Additionally, we must loop in case a new node is added * while we are doing this. Also, unlike other uses of * unparkSuccessor, we need to know if CAS to reset status * fails, if so rechecking. */ for (; ; ) { //获取此时的头节点 Node h = head; //如果h节点(不能说头节点,因为这个过程中,可能有其他线程更改了头节点的值)不为null,且h节点不是尾节点(即队列长度大于等于2) if (h != null && h != tail) { //获取h节点的状态 int ws = h.waitStatus; //如果h节点的状态是SIGNAL if (ws == Node.SIGNAL) { //把h节点的状态更改为0 如果有多个线程同时执行到这里,且获取到的头节点都是同一个节点,那么只会有一个线程执行成功. //成功更改状态的那个线程就会执行 取消阻塞h节点的后继节点 的操作,然后执行a行代码.失败的线程则又会进入循环 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; // loop to recheck cases unparkSuccessor(h); } //如果h节点的状态是0,则把状态更改为PROPAGATE else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } //如果线程执行到这里,如果没有其他线程更改头节点,则跳出循环结束方法. //而如果有其他线程更改了头节点的值,则又进入循环 if (h == head) // loop if head changed //a break; } } /** * Sets head of queue, and checks if successor may be waiting * in shared mode, if so propagating if either propagate > 0 or * PROPAGATE status was set. * * @param node the node * @param propagate the return value from a tryAcquireShared */ private void setHeadAndPropagate(Node node, int propagate) { Node h = head; // Record old head for check below setHead(node); //这个条件没有看明白,如果输入node为null,那后面的代码岂不报错? if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0 ) { //得到输入节点的后继节点 Node s = node.next; //如果不存在后继节点或者后继节点是分享模式,则执行doReleaseShared(); if (s == null || s.isShared()) doReleaseShared(); } } // Utilities for various versions of acquire /** * Cancels an ongoing attempt to acquire. * * @param node the node */ //看这个方法的时候配合后面的图也许好理解些. //这个图只描述了一种情况,其他情况的图就没有画了. private void cancelAcquire(Node node) { // Ignore if node doesn't exist //要求输入node不为null if (node == null) return; node.thread = null; // Skip cancelled predecessors Node pred = node.prev; // 图A //循环条件是 状态为CANCELLED. //跳出循环后的结果: // pred 指向的是 从输入节点的前继节点开始向头节点遍历,遇到的第一个非CANCELLED状态的节点 // 输入节点的前继节点指向 pred 节点 // // 如果多线程同时执行这个循环,会导致node.prev的值不一致, // 所以AQS框架应该会在外部进行限制,使得不会有多个线程同时执行这个方法.呆会会看到这个限制 while (pred.waitStatus > 0) node.prev = pred = pred.prev; //图B // predNext is the apparent node to unsplice. CASes below will // fail if not, in which case, we lost race vs another cancel // or signal, so no further action is necessary. Node predNext = pred.next; // Can use unconditional write instead of CAS here. // After this atomic step, other Nodes can skip past us. // Before, we are free of interference from other threads. node.waitStatus = Node.CANCELLED; //图C // If we are the tail, remove ourselves. // //如果输入节点是尾节点,就把尾节点设置为pred节点,同时把pred节点的后继节点设置为null // 上面的分析得出不会有多个线程同时执行这个方法,但是有可能多个线程同时在执行compareAndSetTail操作. // 所以如果执行该方法的线程在执行compareAndSetTail时有可能返回false.而如果返回false,则进入到else if (node == tail && compareAndSetTail(node, pred)) { //a compareAndSetNext(pred, predNext, null); } else { // If successor needs signal, try to set pred's next-link // so it will get one. Otherwise wake it up to propagate. //图C //使得线程执行这部分代码,有两种情况: // 输入节点不是尾节点 // 输入节点是尾节点,但更新尾节点的值时失败.(这种情况的话,node节点又变为了不是尾节点) // 所以无论是上述哪一种情况,执行到这里时,node节点肯定不是尾节点了(而此时的尾节点到底是在node节点前还是后呢?) int ws; //我勒个天,这个条件好难理 if (pred != head && ( (ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null ) { //执行这段代码的条件: // pred不是头节点 且 pred节点的状态是SIGNAL(如果不是SIGNAL,则要求成功把状态更改为SIGNAL) 且 pred节点的线程不是null Node next = node.next; if (next != null && next.waitStatus <= 0) //执行条件是 此时的node节点有后继节点 且 后继节点的状态是非CANCELLED状态 compareAndSetNext(pred, predNext, next); } else { unparkSuccessor(node); } node.next = node; // help GC } } } 附图 欢迎关注订阅号:
LockSupport类简介 LockSupport类是其他类实现锁和同步的基础 * Basic thread blocking primitives for creating locks and other * synchronization classes. 读了源码就会知道,这个类主要利用了Unsafe类中提供的part和unpart两个方法.而LockSupport类暴露出来的两个核心接口也是part和unpart两个. 如果需要阅读Unsafe类源码,参考我的另一篇博文:读Unsafe类源码 读源码 //构造方法私有化 private LockSupport() {} // Cannot be instantiated. // 引用Unsafe类 private static final sun.misc.Unsafe UNSAFE; //Thread类中 parkBlocker 字段的偏移量 private static final long parkBlockerOffset; //Thread 类中 threadLocalRandomSeed 字段的偏移量 private static final long SEED; //Thread 类中 threadLocalRandomProbe 字段的偏移量 private static final long PROBE; //Thread 类中 threadLocalRandomSecondarySeed 字段的偏移量 private static final long SECONDARY; //初始化上面4个字段的值 static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class<?> tk = Thread.class; parkBlockerOffset = UNSAFE.objectFieldOffset (tk.getDeclaredField("parkBlocker")); SEED = UNSAFE.objectFieldOffset (tk.getDeclaredField("threadLocalRandomSeed")); PROBE = UNSAFE.objectFieldOffset (tk.getDeclaredField("threadLocalRandomProbe")); SECONDARY = UNSAFE.objectFieldOffset (tk.getDeclaredField("threadLocalRandomSecondarySeed")); } catch (Exception ex) { throw new Error(ex); } } //把 Thread 实例 t 的 parkBlocker 字段的值设置为 arg private static void setBlocker(Thread t, Object arg) { // Even though volatile, hotspot doesn't need a write barrier here. UNSAFE.putObject(t, parkBlockerOffset, arg); } //获取对象 t 中 parkBlocker 字段的值 public static Object getBlocker(Thread t) { if (t == null) throw new NullPointerException(); return UNSAFE.getObjectVolatile(t, parkBlockerOffset); } // 取消阻塞线程,如果线程已经处于非阻塞状态,那么下次调用park时不会阻塞线程 public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); } // 使当前调用线程在给定对象上阻塞(不能保证一定阻塞, // 因为如果之前在非阻塞状态调用了unpar方法的话,此次调用park方法就不会阻塞线程) public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, 0L); setBlocker(t, null); } // 阻塞线程 public static void park() { UNSAFE.park(false, 0L); } // 使当前线程在blocker对象上阻塞给定的纳秒时间 public static void parkNanos(Object blocker, long nanos) { if (nanos > 0) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, nanos); setBlocker(t, null); } } // 使当前线程在blocker对象上阻塞到给定的时间点 // 这个时间点是从Epoch time(1970-01-01 00:00:00 UTC)开始算起的某个具体的时间点。 public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(true, deadline); setBlocker(t, null); } 欢迎关注订阅号:
Unsafe类简介 JUC中很多的实现都是调用了Unsafe类来实现的,所以这里阅读下该类的内容. Unsafe类包装了很多低级别的非安全性操作.虽然该类及其所有的方法都是public的,但是它只能被受信任的代码使用(也就是jdk中的代码) 读源码过程中,这里只会对部分方法进行说明,其余的方法要么类似要么不那么重要. 读源码 //构造方法私有化 private Unsafe() {} //采用饿汉式单例 private static final Unsafe theUnsafe = new Unsafe(); //获取实例对象 public static Unsafe getUnsafe() { //获取调用该方法的调用者信息 Class<?> caller = Reflection.getCallerClass(); //判断该调用这的类加载器是否是系统类加载器(系统类加载器为null) if (!VM.isSystemDomainLoader(caller.getClassLoader())) throw new SecurityException("Unsafe"); return theUnsafe; } //返回一个静态字段的偏移量 public native long staticFieldOffset(Field f); //返回一个非静态字段字段的偏移量 public native long objectFieldOffset(Field f); //获取给定对象指定偏移量的int值 public native int getInt(Object o, long offset); //把给定对象指定偏移量上的值设置为整型变量x public native void putInt(Object o, long offset, int x); //获取给定内存地址上的byte值 public native byte getByte(long address); //把给定内存地址上的值设置为byte值x public native void putByte(long address, byte x); //获取给定内存地址上的值,该值是表示一个内存地址 public native long getAddress(long address); //分配一块本地内存,这块内存的大小便是给定的大小.这块内存的值是没有被初始化的 public native long allocateMemory(long bytes); //定义一个类 public native Class<?> defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain); //定义一个匿名类 public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches); //获取给定对象上的锁(jvm中有monitorenter 和 monitorexit两个指令) public native void monitorEnter(Object o); //释放给定对象上的锁(jvm中有monitorenter 和 monitorexit public native void monitorExit(Object o); //CAS操作,修改对象指定偏移量上的(CAS简介见末尾) public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x); //CAS操作 public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x); //CAS操作 public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x); //获取被volatile关键字修饰的字段的值 public native Object getObjectVolatile(Object o, long offset); //取消阻塞线程 thread,如果 thread 已经是非阻塞状态, //那么下次对该 thread 进行park操作时就不会阻塞 public native void unpark(Object thread); //阻塞当前线程,isAbsolute的作用不是很清楚.(如果该方法调用前在线程非阻塞情况下调用了unpart方法,那么此次调用该方法不会令当前线程阻塞) public native void park(boolean isAbsolute, long time); //CAS操作 public final int getAndAddInt(Object o, long offset, int delta) { int v; do { v = getIntVolatile(o, offset); } while (!compareAndSwapInt(o, offset, v, v + delta)); return v; } CAS简介 CAS(Compare And Swap)比较并交换,是CPU级提供的功能. 比如IA64,X86指令集中就有cmpxchg指令来完成CAS功能 CAS指令需要3个操作数,分别是内存位置V,旧的预期值A,新值B. CAS指令执行时,当且仅当V符合旧的预期值A,处理器才用新值B更新V的值,否则部执行更新.最后无论是否更新V的值,都会返回V的旧值.CAS操作是一个原子操作. —参考<<深入理解Java虚拟机:JVM高级特性与最佳实践(第二版) 周志明 著>> 第13章 线程安全与锁优化 欢迎关注订阅号:
查看建表语句 show create table tableName; eg: mysql> show create table demo; +-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | demo | CREATE TABLE `demo` ( `uid` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `uname` varchar(500) DEFAULT NULL, `email` varchar(500) DEFAULT NULL, `createTime` datetime DEFAULT NULL, PRIMARY KEY (`uid`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 | +-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) 可以看到该表使用的数据引擎事 MyISAM (该引擎不支持事务) 查看可使用的数据引擎 show engines; eg: mysql> show engines; +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ | Engine | Support | Comment | Transactions | XA | Savepoints | +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ | MyISAM | YES | MyISAM storage engine | NO | NO | NO | | CSV | YES | CSV storage engine | NO | NO | NO | | MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO | | BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO | | MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO | | FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL | | ARCHIVE | YES | Archive storage engine | NO | NO | NO | | PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO | | InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES | +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ 9 rows in set (0.00 sec) 可以看到只有InnoDB引擎才支持事务 修改表的数据引擎 alter table tableName engine=innodb; eg: mysql> alter table demo engine=innodb; Query OK, 1 row affected (0.58 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> show create table demo; +-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | demo | CREATE TABLE `demo` ( `uid` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `uname` varchar(500) DEFAULT NULL, `email` varchar(500) DEFAULT NULL, `createTime` datetime DEFAULT NULL, PRIMARY KEY (`uid`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 | +-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) 修改默认的存储引擎 修改my.cnf文件,在[mysqld]下加入: default-storage-engine=INNODB 修改默认的字符集 修改my.cnf文件,在[mysqld]下加入: # charset default-character-set=utf8 default-collation=utf8_general_ci character-set-server=utf8 collation-server=utf8_general_ci init-connect='SET NAMES utf8'
表结构 xxxx(id,title,content,url,status) id为主键,自动递增 普通分页 select * from xxxx where status = 1 limit 1000,100; 这样查询在数据量比较小,同时查询前面的数据的时候是很快的.但是如果表的数量持续增大(这里的测试数据有589530条数据),同时需要查询后面分页的数据,如: select * from xxxx where status = 1 limit 235500,500; 执行上面这条语句,就会很慢. 这里测试结果是 8-9s的样子 优化分页 利用id主键,如下: select * from xxxx where id >=(select id from xxxx where status=1 limit 235500,1) and status = 1 limit 500; 上面这条语句的执行结果是 0.24s 效率一下提升40多倍
编辑脚本 本例以添加svnserve为例 #!/bin/sh #echo $1 START='svnserve -d -T -r /home/rocky/programs/svnrepo/' PID="`ps aux | grep svnserve\ -d\ -T\ -r\ /home/rocky/programs/svnrepo/ | grep -v grep|awk '{print $2}'`" case $1 in 'start') id=$PID if [ "$id" = "" ];then $START fi echo 'START SVNSERVE [OK]' ;; 'stop') id=$PID if [ "$id" != "" ];then kill $id fi echo 'STOP SVNSERVE [OK]' ;; 'restart') id=$PID if [ "$id" != "" ];then kill $id fi echo 'STOP SVNSERVE [OK]' $START echo 'START SVNSERVE [OK]' ;; '') $START ;; esac 执行update-rc.d命令 把启动脚本保存为svnserve,存放在/etc/init.d/目录下 cd /etc/init.d/ update-rc.d svnserve defaults 20 输出如下: update-rc.d: warning: /etc/init.d/svnserve missing LSB information update-rc.d: see <http://wiki.debian.org/LSBInitScripts> Adding system startup for /etc/init.d/svnserve ... /etc/rc0.d/K20svnserve -> ../init.d/svnserve /etc/rc1.d/K20svnserve -> ../init.d/svnserve /etc/rc6.d/K20svnserve -> ../init.d/svnserve /etc/rc2.d/S20svnserve -> ../init.d/svnserve /etc/rc3.d/S20svnserve -> ../init.d/svnserve /etc/rc4.d/S20svnserve -> ../init.d/svnserve /etc/rc5.d/S20svnserve -> ../init.d/svnserve 20代表启动顺序,这个数值越大,则越后启动 至此,下次重启电脑就会自动执行该脚本命令 立即启动服务 service svnserve start 其他命令 service svnserve stop service svnserve restart
基础篇 执行: ssh-keygen -t rsa rocky@tiger:~$ ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/home/rocky/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/rocky/.ssh/id_rsa. Your public key has been saved in /home/rocky/.ssh/id_rsa.pub. The key fingerprint is: 02:fc:ca:a5:8b:28:d1:bf:0a:d5:40:7a:80:8d:43:b1 rocky@tiger The key's randomart image is: +--[ RSA 2048]----+ |+=o | |++o. | |.Eo o | | . o o | | .. . + S | |.... + . | |.. .+ | |.o ... | |o o.o. | +-----------------+ 注:提示输入passphrase的时候,直接输入回车,这样登录时就无需再输入密码 执行后会在/home/rocky/.ssh/目录下生成两个文件: id_rsa(私钥) id_rsa.pub(公钥) 上传id_rsa.pub文件到remote server的 ~/.ssh/目录下 (没有该目录时,自行创建即可) scp /home/rocky/.ssh/id_rsa.pub root@ipaddr:/root/.ssh/authorized_keys (此时要输入密码) 注:1.上面的命令不但会把id_rsa.pub文件上传到remote server的/root/.ssh目录下,还会把文件名替换为authorized_keys 即authorized_keys文件就是id_rsa.pub文件 2.如果之前remote server上已经存在了authorized_keys文件,上面的命令会清除文件内容在写入.因此这个时候最好先保存为其他某个文件,再把文件内容追加到authorized_keys文件中. cat xxx.pub >> authorized_keys 重新登录测试 ssh root@ipaddress 如果仍然需要密码,需登录到remote server检查.ssh目录的权限是否为700,以及authorized_keys文件的权限是否为644 再次测试,成功 异常处理: 1.Agent admitted failure to sign using the key. 在本机执行ssh-add命令即可 rocky@tiger:.ssh$ ssh-add Identity added: /home/rocky/.ssh/id_rsa (/home/rocky/.ssh/id_rsa) 进阶篇 基础篇中采用scp把公钥文件拷贝到remote server的authorized_keys文件中,其中要注意各种文件,比如权限,内容追加这些地方. 下面有个简便方法搞定上诉步骤: ssh-copy-id -i .ssh/id_rsa.pub user@IP 该命令会自动把id_rsa.pub命令追加到user用户下的.ssh/authorized_keys文件中.
Listener Filter Servlet的执行顺序 Listener -> Filter -> Servlet Filter之间的初始化顺序 与web.xml文件中filter 标签的声明顺序一致 Filter之间doFilter方法的执行顺序 与web.xml文件中filter-mapping 标签的声明顺序一致
删除所有key redis-cli keys “*” | xargs redis-cli del 需要密码的 redis-cli -a password keys “*” | xargs redis-cli -a password del 删除指定数据库的key redis-cli -n 0 keys “*” | xargs redis-cli -n 0 del 删除当前数据的 flushdb 删除所有的key flushall
修改/etc/gnome/defaults.list文件即可修改文件的默认打开软件. 如需要把.html文件与sublime text 软件关联: text/plain=SublimeText2.desktop
对url建立唯一性索引: 新增一列,该列存储 url的 crc32计算结果,对该列建立唯一性索引. 如下: create table url_list ( id int auto_increment, url varchar(255) not null, url_crc int unsigned not null default 0, primary key (id) ) 然后建立触发器 delimiter // create trigger url_crc before insert on url_visit_times for each row begin set new.url_crc = crc32(new.url); end // create trigger url_crc_update before update on url_visit_times for each row begin set new.url_crc = crc32(new.url); end // delimiter ;
CentOS Mysql安装 yum list | grep mysql yum install mysql-server Mysql 配置 目的:把data文件和log文件移动到另外一张磁盘上.如下: 注意:最好先不要执行 service mysqld start命令 1. 移动 /var/lib/mysql 目录到指定目录 , 这里为 /data/mysql目录 mv /var/lib/mysql /data/ 2. 修改 /data/mysql目录权限 chown -R mysql:mysql /data/mysql 3. 修改 /etc/my.cnf文件内容 [mysqld] datadir=/data/mysql socket=/data/mysql/mysql.sock user=mysql # Disabling symbolic-links is recommended to prevent assorted security risks symbolic-links=0 # charset default-character-set=utf8 default-collation=utf8_general_ci character-set-server=utf8 collation-server=utf8_general_ci init-connect='SET NAMES utf8' [mysqld_safe] log-error=/data/mysql/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid [client] default-character-set=utf8 socket=/data/mysql/mysql.sock 注意:mysqld 中socket的配置必须和 client 中socket的配置一致. 4. 创建用户并赋予权限 create user ‘username’ identified by ‘password’; grant all on *.* to username; 5. 启动 service mysqld start
Alpha Alpha最开始发布的版本,也许存在许多Bug.一般作为内部开始使用,一般用户不建议使用该版本。 Beta Beta版本事Alpha版本后的一个版本,与Alpha版本相比,Beta版本修复了Alpha版本的一些Bug。 RC (Release Candidate) RC版本称为“发布候选版”,相比Beta版本,该版本加入了一些新的功能。该版本后一般不会再加入新的功能,后续工作主要在于出错。 GA (General Availability) GA版本是普遍可用版本,从该版本开始,官方开始推广使用。 R (Release) 最终版,或者正式版。
深入理解Java虚拟机:JVM高级特性与最佳实践 周志明著 大型网站系统与Java中间件实践 Java EE设计模式:Spring企业级开发最佳实践 IBATIS IN ACTION Java 消息服务 Head First设计模式 企业应用设计模式 Thing in UML http://blog.csdn.net/winnerbao/article/details/1510827
修改原因: thrfit (0.9.2版本)在生成的c#代码,如果thrift server 返回了null,生成的c#代码会抛出错误,提示 "unkown result.". 由于这个异常并不能明确说明sever端返回了null,同时个人觉得如果server端返回了null,客户端自然返回null就是了. 也就是server端返回了什么,客户端就拿到什么,而不应该报错.所以产出了修改源码的想法. (仅是个人想法) 说改就改,在github上clone了thrift项目,然后用vs2010修改了其中的 t_csharp_generator.cc 文件. (本地编译时报错,解决办法见:http://blog.csdn.net/wilsonpeng3/article/details/41984787) 修改前: (这里贴出来了修改前后的整个文件,可以使用文件比对器查看两个文件的差别 Beyond Compare 不错) /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * * Contains some contributions under the Thrift Software License. * Please see doc/old-thrift-license.txt in the Thrift distribution for * details. */ #include <cassert> #include <string> #include <fstream> #include <iostream> #include <vector> #include <cctype> #include <stdlib.h> #include <sys/stat.h> #include <sstream> #include "platform.h" #include "t_oop_generator.h" using std::map; using std::ofstream; using std::ostringstream; using std::string; using std::stringstream; using std::vector; static const string endl = "\n"; // avoid ostream << std::endl flushes class t_csharp_generator : public t_oop_generator { public: t_csharp_generator(t_program* program, const std::map<std::string, std::string>& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void)option_string; std::map<std::string, std::string>::const_iterator iter; iter = parsed_options.find("async"); async_ = (iter != parsed_options.end()); iter = parsed_options.find("asyncctp"); async_ctp_ = (iter != parsed_options.end()); if (async_ && async_ctp_) { throw "argument error: Cannot specify both async and asyncctp; they are incompatible."; } iter = parsed_options.find("nullable"); nullable_ = (iter != parsed_options.end()); iter = parsed_options.find("hashcode"); hashcode_ = (iter != parsed_options.end()); iter = parsed_options.find("union"); union_ = (iter != parsed_options.end()); iter = parsed_options.find("serial"); serialize_ = (iter != parsed_options.end()); if (serialize_) { wcf_namespace_ = iter->second; // since there can be only one namespace } iter = parsed_options.find("wcf"); wcf_ = (iter != parsed_options.end()); if (wcf_) { wcf_namespace_ = iter->second; } out_dir_base_ = "gen-csharp"; } void init_generator(); void close_generator(); void generate_consts(std::vector<t_const*> consts); void generate_typedef(t_typedef* ttypedef); void generate_enum(t_enum* tenum); void generate_struct(t_struct* tstruct); void generate_union(t_struct* tunion); void generate_xception(t_struct* txception); void generate_service(t_service* tservice); void generate_property(ofstream& out, t_field* tfield, bool isPublic, bool generateIsset); void generate_csharp_property(ofstream& out, t_field* tfield, bool isPublic, bool includeIsset = true, std::string fieldPrefix = ""); bool print_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value, bool in_static, bool defval = false, bool needtype = false); std::string render_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value); void print_const_constructor(std::ofstream& out, std::vector<t_const*> consts); void print_const_def_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value); void generate_csharp_struct(t_struct* tstruct, bool is_exception); void generate_csharp_union(t_struct* tunion); void generate_csharp_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception = false, bool in_class = false, bool is_result = false); void generate_csharp_union_definition(std::ofstream& out, t_struct* tunion); void generate_csharp_union_class(std::ofstream& out, t_struct* tunion, t_field* tfield); void generate_csharp_wcffault(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_result_writer(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_tostring(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_equals(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_hashcode(std::ofstream& out, t_struct* tstruct); void generate_csharp_union_reader(std::ofstream& out, t_struct* tunion); void generate_function_helpers(t_function* tfunction); void generate_service_interface(t_service* tservice); void generate_service_helpers(t_service* tservice); void generate_service_client(t_service* tservice); void generate_service_server(t_service* tservice); void generate_process_function(t_service* tservice, t_function* function); void generate_deserialize_field(std::ofstream& out, t_field* tfield, std::string prefix = "", bool is_propertyless = false); void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); void generate_deserialize_list_element(std::ofstream& out, t_list* list, std::string prefix = ""); void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = "", bool is_element = false, bool is_propertyless = false); void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); void generate_serialize_map_element(std::ofstream& out, t_map* tmap, std::string iter, std::string map); void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); void generate_csharp_doc(std::ofstream& out, t_field* field); void generate_csharp_doc(std::ofstream& out, t_doc* tdoc); void generate_csharp_doc(std::ofstream& out, t_function* tdoc); void generate_csharp_docstring_comment(std::ofstream& out, string contents); void start_csharp_namespace(std::ofstream& out); void end_csharp_namespace(std::ofstream& out); std::string csharp_type_usings(); std::string csharp_thrift_usings(); std::string type_name(t_type* ttype, bool in_countainer = false, bool in_init = false, bool in_param = false, bool is_required = false); std::string base_type_name(t_base_type* tbase, bool in_container = false, bool in_param = false, bool is_required = false); std::string declare_field(t_field* tfield, bool init = false, std::string prefix = ""); std::string function_signature_async_begin(t_function* tfunction, std::string prefix = ""); std::string function_signature_async_end(t_function* tfunction, std::string prefix = ""); std::string function_signature_async(t_function* tfunction, std::string prefix = ""); std::string function_signature(t_function* tfunction, std::string prefix = ""); std::string argument_list(t_struct* tstruct); std::string type_to_enum(t_type* ttype); std::string prop_name(t_field* tfield, bool suppress_mapping = false); std::string get_enum_class_name(t_type* type); bool field_has_default(t_field* tfield) { return tfield->get_value() != NULL; } bool field_is_required(t_field* tfield) { return tfield->get_req() == t_field::T_REQUIRED; } bool type_can_be_null(t_type* ttype) { while (ttype->is_typedef()) { ttype = ((t_typedef*)ttype)->get_type(); } return ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string(); } private: std::string namespace_name_; std::ofstream f_service_; std::string namespace_dir_; bool async_; bool async_ctp_; bool nullable_; bool union_; bool hashcode_; bool serialize_; bool wcf_; std::string wcf_namespace_; std::map<std::string, int> csharp_keywords; void* member_mapping_scope; std::map<std::string, std::string> member_name_mapping; void init_keywords(); std::string normalize_name(std::string name); std::string make_valid_csharp_identifier(std::string const& fromName); void prepare_member_name_mapping(t_struct* tstruct); void prepare_member_name_mapping(void* scope, const vector<t_field*>& members, const string& structname); void cleanup_member_name_mapping(void* scope); string get_mapped_member_name(string oldname); }; void t_csharp_generator::init_generator() { MKDIR(get_out_dir().c_str()); namespace_name_ = program_->get_namespace("csharp"); string dir = namespace_name_; string subdir = get_out_dir().c_str(); string::size_type loc; while ((loc = dir.find(".")) != string::npos) { subdir = subdir + "/" + dir.substr(0, loc); MKDIR(subdir.c_str()); dir = dir.substr(loc + 1); } if (dir.size() > 0) { subdir = subdir + "/" + dir; MKDIR(subdir.c_str()); } namespace_dir_ = subdir; init_keywords(); member_mapping_scope = NULL; pverbose("C# options:\n"); pverbose("- async ...... %s\n", (async_ ? "ON" : "off")); pverbose("- async_ctp .. %s\n", (async_ctp_ ? "ON" : "off")); pverbose("- nullable ... %s\n", (nullable_ ? "ON" : "off")); pverbose("- union ...... %s\n", (union_ ? "ON" : "off")); pverbose("- hashcode ... %s\n", (hashcode_ ? "ON" : "off")); pverbose("- serialize .. %s\n", (serialize_ ? "ON" : "off")); pverbose("- wcf ........ %s\n", (wcf_ ? "ON" : "off")); } std::string t_csharp_generator::normalize_name(std::string name) { string tmp(name); std::transform(tmp.begin(), tmp.end(), tmp.begin(), static_cast<int (*)(int)>(std::tolower)); // un-conflict keywords by prefixing with "@" if (csharp_keywords.find(tmp) != csharp_keywords.end()) { return "@" + name; } // no changes necessary return name; } void t_csharp_generator::init_keywords() { csharp_keywords.clear(); // C# keywords csharp_keywords["abstract"] = 1; csharp_keywords["as"] = 1; csharp_keywords["base"] = 1; csharp_keywords["bool"] = 1; csharp_keywords["break"] = 1; csharp_keywords["byte"] = 1; csharp_keywords["case"] = 1; csharp_keywords["catch"] = 1; csharp_keywords["char"] = 1; csharp_keywords["checked"] = 1; csharp_keywords["class"] = 1; csharp_keywords["const"] = 1; csharp_keywords["continue"] = 1; csharp_keywords["decimal"] = 1; csharp_keywords["default"] = 1; csharp_keywords["delegate"] = 1; csharp_keywords["do"] = 1; csharp_keywords["double"] = 1; csharp_keywords["else"] = 1; csharp_keywords["enum"] = 1; csharp_keywords["event"] = 1; csharp_keywords["explicit"] = 1; csharp_keywords["extern"] = 1; csharp_keywords["false"] = 1; csharp_keywords["finally"] = 1; csharp_keywords["fixed"] = 1; csharp_keywords["float"] = 1; csharp_keywords["for"] = 1; csharp_keywords["foreach"] = 1; csharp_keywords["goto"] = 1; csharp_keywords["if"] = 1; csharp_keywords["implicit"] = 1; csharp_keywords["in"] = 1; csharp_keywords["int"] = 1; csharp_keywords["interface"] = 1; csharp_keywords["internal"] = 1; csharp_keywords["is"] = 1; csharp_keywords["lock"] = 1; csharp_keywords["long"] = 1; csharp_keywords["namespace"] = 1; csharp_keywords["new"] = 1; csharp_keywords["null"] = 1; csharp_keywords["object"] = 1; csharp_keywords["operator"] = 1; csharp_keywords["out"] = 1; csharp_keywords["override"] = 1; csharp_keywords["params"] = 1; csharp_keywords["private"] = 1; csharp_keywords["protected"] = 1; csharp_keywords["public"] = 1; csharp_keywords["readonly"] = 1; csharp_keywords["ref"] = 1; csharp_keywords["return"] = 1; csharp_keywords["sbyte"] = 1; csharp_keywords["sealed"] = 1; csharp_keywords["short"] = 1; csharp_keywords["sizeof"] = 1; csharp_keywords["stackalloc"] = 1; csharp_keywords["static"] = 1; csharp_keywords["string"] = 1; csharp_keywords["struct"] = 1; csharp_keywords["switch"] = 1; csharp_keywords["this"] = 1; csharp_keywords["throw"] = 1; csharp_keywords["true"] = 1; csharp_keywords["try"] = 1; csharp_keywords["typeof"] = 1; csharp_keywords["uint"] = 1; csharp_keywords["ulong"] = 1; csharp_keywords["unchecked"] = 1; csharp_keywords["unsafe"] = 1; csharp_keywords["ushort"] = 1; csharp_keywords["using"] = 1; csharp_keywords["virtual"] = 1; csharp_keywords["void"] = 1; csharp_keywords["volatile"] = 1; csharp_keywords["while"] = 1; // C# contextual keywords csharp_keywords["add"] = 1; csharp_keywords["alias"] = 1; csharp_keywords["ascending"] = 1; csharp_keywords["async"] = 1; csharp_keywords["await"] = 1; csharp_keywords["descending"] = 1; csharp_keywords["dynamic"] = 1; csharp_keywords["from"] = 1; csharp_keywords["get"] = 1; csharp_keywords["global"] = 1; csharp_keywords["group"] = 1; csharp_keywords["into"] = 1; csharp_keywords["join"] = 1; csharp_keywords["let"] = 1; csharp_keywords["orderby"] = 1; csharp_keywords["partial"] = 1; csharp_keywords["remove"] = 1; csharp_keywords["select"] = 1; csharp_keywords["set"] = 1; csharp_keywords["value"] = 1; csharp_keywords["var"] = 1; csharp_keywords["where"] = 1; csharp_keywords["yield"] = 1; } void t_csharp_generator::start_csharp_namespace(ofstream& out) { if (!namespace_name_.empty()) { out << "namespace " << namespace_name_ << "\n"; scope_up(out); } } void t_csharp_generator::end_csharp_namespace(ofstream& out) { if (!namespace_name_.empty()) { scope_down(out); } } string t_csharp_generator::csharp_type_usings() { return string() + "using System;\n" + "using System.Collections;\n" + "using System.Collections.Generic;\n" + "using System.Text;\n" + "using System.IO;\n" + ((async_ || async_ctp_) ? "using System.Threading.Tasks;\n" : "") + "using Thrift;\n" + "using Thrift.Collections;\n" + ((serialize_ || wcf_) ? "#if !SILVERLIGHT\n" : "") + ((serialize_ || wcf_) ? "using System.Xml.Serialization;\n" : "") + ((serialize_ || wcf_) ? "#endif\n" : "") + (wcf_ ? "//using System.ServiceModel;\n" : "") + "using System.Runtime.Serialization;\n"; } string t_csharp_generator::csharp_thrift_usings() { return string() + "using Thrift.Protocol;\n" + "using Thrift.Transport;\n"; } void t_csharp_generator::close_generator() { } void t_csharp_generator::generate_typedef(t_typedef* ttypedef) { (void)ttypedef; } void t_csharp_generator::generate_enum(t_enum* tenum) { string f_enum_name = namespace_dir_ + "/" + (tenum->get_name()) + ".cs"; ofstream f_enum; f_enum.open(f_enum_name.c_str()); f_enum << autogen_comment() << endl; start_csharp_namespace(f_enum); generate_csharp_doc(f_enum, tenum); indent(f_enum) << "public enum " << tenum->get_name() << "\n"; scope_up(f_enum); vector<t_enum_value*> constants = tenum->get_constants(); vector<t_enum_value*>::iterator c_iter; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { generate_csharp_doc(f_enum, *c_iter); int value = (*c_iter)->get_value(); indent(f_enum) << (*c_iter)->get_name() << " = " << value << "," << endl; } scope_down(f_enum); end_csharp_namespace(f_enum); f_enum.close(); } void t_csharp_generator::generate_consts(std::vector<t_const*> consts) { if (consts.empty()) { return; } string f_consts_name = namespace_dir_ + '/' + program_name_ + ".Constants.cs"; ofstream f_consts; f_consts.open(f_consts_name.c_str()); f_consts << autogen_comment() << csharp_type_usings() << endl; start_csharp_namespace(f_consts); indent(f_consts) << "public static class " << make_valid_csharp_identifier(program_name_) << "Constants" << endl; scope_up(f_consts); vector<t_const*>::iterator c_iter; bool need_static_constructor = false; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { generate_csharp_doc(f_consts, (*c_iter)); if (print_const_value(f_consts, (*c_iter)->get_name(), (*c_iter)->get_type(), (*c_iter)->get_value(), false)) { need_static_constructor = true; } } if (need_static_constructor) { print_const_constructor(f_consts, consts); } scope_down(f_consts); end_csharp_namespace(f_consts); f_consts.close(); } void t_csharp_generator::print_const_def_value(std::ofstream& out, string name, t_type* type, t_const_value* value) { if (type->is_struct() || type->is_xception()) { const vector<t_field*>& fields = ((t_struct*)type)->get_members(); vector<t_field*>::const_iterator f_iter; const map<t_const_value*, t_const_value*>& val = value->get_map(); map<t_const_value*, t_const_value*>::const_iterator v_iter; prepare_member_name_mapping((t_struct*)type); for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { t_field* field = NULL; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field = (*f_iter); } } if (field == NULL) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } t_type* field_type = field->get_type(); string val = render_const_value(out, name, field_type, v_iter->second); indent(out) << name << "." << prop_name(field) << " = " << val << ";" << endl; } cleanup_member_name_mapping((t_struct*)type); } else if (type->is_map()) { t_type* ktype = ((t_map*)type)->get_key_type(); t_type* vtype = ((t_map*)type)->get_val_type(); const map<t_const_value*, t_const_value*>& val = value->get_map(); map<t_const_value*, t_const_value*>::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { string key = render_const_value(out, name, ktype, v_iter->first); string val = render_const_value(out, name, vtype, v_iter->second); indent(out) << name << "[" << key << "]" << " = " << val << ";" << endl; } } else if (type->is_list() || type->is_set()) { t_type* etype; if (type->is_list()) { etype = ((t_list*)type)->get_elem_type(); } else { etype = ((t_set*)type)->get_elem_type(); } const vector<t_const_value*>& val = value->get_list(); vector<t_const_value*>::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { string val = render_const_value(out, name, etype, *v_iter); indent(out) << name << ".Add(" << val << ");" << endl; } } } void t_csharp_generator::print_const_constructor(std::ofstream& out, std::vector<t_const*> consts) { indent(out) << "static " << make_valid_csharp_identifier(program_name_).c_str() << "Constants()" << endl; scope_up(out); vector<t_const*>::iterator c_iter; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { string name = (*c_iter)->get_name(); t_type* type = (*c_iter)->get_type(); t_const_value* value = (*c_iter)->get_value(); print_const_def_value(out, name, type, value); } scope_down(out); } // it seems like all that methods that call this are using in_static to be the opposite of what it // would imply bool t_csharp_generator::print_const_value(std::ofstream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval, bool needtype) { indent(out); bool need_static_construction = !in_static; while (type->is_typedef()) { type = ((t_typedef*)type)->get_type(); } if (!defval || needtype) { out << (in_static ? "" : type->is_base_type() ? "public const " : "public static ") << type_name(type) << " "; } if (type->is_base_type()) { string v2 = render_const_value(out, name, type, value); out << name << " = " << v2 << ";" << endl; need_static_construction = false; } else if (type->is_enum()) { out << name << " = " << type_name(type, false, true) << "." << value->get_identifier_name() << ";" << endl; need_static_construction = false; } else if (type->is_struct() || type->is_xception()) { out << name << " = new " << type_name(type) << "();" << endl; } else if (type->is_map()) { out << name << " = new " << type_name(type, true, true) << "();" << endl; } else if (type->is_list() || type->is_set()) { out << name << " = new " << type_name(type) << "();" << endl; } if (defval && !type->is_base_type() && !type->is_enum()) { print_const_def_value(out, name, type, value); } return need_static_construction; } std::string t_csharp_generator::render_const_value(ofstream& out, string name, t_type* type, t_const_value* value) { (void)name; std::ostringstream render; if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_STRING: render << '"' << get_escaped_string(value) << '"'; break; case t_base_type::TYPE_BOOL: render << ((value->get_integer() > 0) ? "true" : "false"); break; case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: case t_base_type::TYPE_I64: render << value->get_integer(); break; case t_base_type::TYPE_DOUBLE: if (value->get_type() == t_const_value::CV_INTEGER) { render << value->get_integer(); } else { render << value->get_double(); } break; default: throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); } } else if (type->is_enum()) { render << type->get_name() << "." << value->get_identifier_name(); } else { string t = tmp("tmp"); print_const_value(out, t, type, value, true, true, true); render << t; } return render.str(); } void t_csharp_generator::generate_struct(t_struct* tstruct) { if (union_ && tstruct->is_union()) { generate_csharp_union(tstruct); } else { generate_csharp_struct(tstruct, false); } } void t_csharp_generator::generate_xception(t_struct* txception) { generate_csharp_struct(txception, true); } void t_csharp_generator::generate_csharp_struct(t_struct* tstruct, bool is_exception) { string f_struct_name = namespace_dir_ + "/" + (tstruct->get_name()) + ".cs"; ofstream f_struct; f_struct.open(f_struct_name.c_str()); f_struct << autogen_comment() << csharp_type_usings() << csharp_thrift_usings() << endl; generate_csharp_struct_definition(f_struct, tstruct, is_exception); f_struct.close(); } void t_csharp_generator::generate_csharp_struct_definition(ofstream& out, t_struct* tstruct, bool is_exception, bool in_class, bool is_result) { if (!in_class) { start_csharp_namespace(out); } out << endl; generate_csharp_doc(out, tstruct); prepare_member_name_mapping(tstruct); indent(out) << "#if !SILVERLIGHT" << endl; indent(out) << "[Serializable]" << endl; indent(out) << "#endif" << endl; if ((serialize_ || wcf_) && !is_exception) { indent(out) << "[DataContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl; // do not make exception classes directly WCF serializable, we provide a // separate "fault" for that } bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); indent(out) << "public " << (is_final ? "sealed " : "") << "partial class " << normalize_name(tstruct->get_name()) << " : "; if (is_exception) { out << "TException, "; } out << "TBase"; out << endl; scope_up(out); const vector<t_field*>& members = tstruct->get_members(); vector<t_field*>::const_iterator m_iter; // make private members with public Properties for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { // if the field is requied, then we use auto-properties if (!field_is_required((*m_iter)) && (!nullable_ || field_has_default((*m_iter)))) { indent(out) << "private " << declare_field(*m_iter, false, "_") << endl; } } out << endl; bool has_non_required_fields = false; bool has_non_required_default_value_fields = false; bool has_required_fields = false; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { generate_csharp_doc(out, *m_iter); generate_property(out, *m_iter, true, true); bool is_required = field_is_required((*m_iter)); bool has_default = field_has_default((*m_iter)); if (is_required) { has_required_fields = true; } else { if (has_default) { has_non_required_default_value_fields = true; } has_non_required_fields = true; } } bool generate_isset = (nullable_ && has_non_required_default_value_fields) || (!nullable_ && has_non_required_fields); if (generate_isset) { out << endl; if (serialize_ || wcf_) { out << indent() << "[XmlIgnore] // XmlSerializer" << endl << indent() << "[DataMember(Order = 1)] // XmlObjectSerializer, DataContractJsonSerializer, etc." << endl; } out << indent() << "public Isset __isset;" << endl << indent() << "#if !SILVERLIGHT" << endl << indent() << "[Serializable]" << endl << indent() << "#endif" << endl; if (serialize_ || wcf_) { indent(out) << "[DataContract]" << endl; } indent(out) << "public struct Isset {" << endl; indent_up(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { bool is_required = field_is_required((*m_iter)); bool has_default = field_has_default((*m_iter)); // if it is required, don't need Isset for that variable // if it is not required, if it has a default value, we need to generate Isset // if we are not nullable, then we generate Isset if (!is_required && (!nullable_ || has_default)) { if (serialize_ || wcf_) { indent(out) << "[DataMember]" << endl; } indent(out) << "public bool " << normalize_name((*m_iter)->get_name()) << ";" << endl; } } indent_down(); indent(out) << "}" << endl << endl; if (generate_isset && (serialize_ || wcf_)) { indent(out) << "#region XmlSerializer support" << endl << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { bool is_required = field_is_required((*m_iter)); bool has_default = field_has_default((*m_iter)); // if it is required, don't need Isset for that variable // if it is not required, if it has a default value, we need to generate Isset // if we are not nullable, then we generate Isset if (!is_required && (!nullable_ || has_default)) { indent(out) << "public bool ShouldSerialize" << prop_name((*m_iter)) << "()" << endl; indent(out) << "{" << endl; indent_up(); indent(out) << "return __isset." << normalize_name((*m_iter)->get_name()) << ";" << endl; indent_down(); indent(out) << "}" << endl << endl; } } indent(out) << "#endregion XmlSerializer support" << endl << endl; } } // We always want a default, no argument constructor for Reading indent(out) << "public " << normalize_name(tstruct->get_name()) << "() {" << endl; indent_up(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = (*m_iter)->get_type(); while (t->is_typedef()) { t = ((t_typedef*)t)->get_type(); } if ((*m_iter)->get_value() != NULL) { if (field_is_required((*m_iter))) { print_const_value(out, "this." + prop_name(*m_iter), t, (*m_iter)->get_value(), true, true); } else { print_const_value(out, "this._" + (*m_iter)->get_name(), t, (*m_iter)->get_value(), true, true); // Optionals with defaults are marked set indent(out) << "this.__isset." << normalize_name((*m_iter)->get_name()) << " = true;" << endl; } } } indent_down(); indent(out) << "}" << endl << endl; if (has_required_fields) { indent(out) << "public " << tstruct->get_name() << "("; bool first = true; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { if (field_is_required((*m_iter))) { if (first) { first = false; } else { out << ", "; } out << type_name((*m_iter)->get_type()) << " " << (*m_iter)->get_name(); } } out << ") : this() {" << endl; indent_up(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { if (field_is_required((*m_iter))) { indent(out) << "this." << prop_name((*m_iter)) << " = " << (*m_iter)->get_name() << ";" << endl; } } indent_down(); indent(out) << "}" << endl << endl; } generate_csharp_struct_reader(out, tstruct); if (is_result) { generate_csharp_struct_result_writer(out, tstruct); } else { generate_csharp_struct_writer(out, tstruct); } if (hashcode_) { generate_csharp_struct_equals(out, tstruct); generate_csharp_struct_hashcode(out, tstruct); } generate_csharp_struct_tostring(out, tstruct); scope_down(out); out << endl; // generate a corresponding WCF fault to wrap the exception if ((serialize_ || wcf_) && is_exception) { generate_csharp_wcffault(out, tstruct); } cleanup_member_name_mapping(tstruct); if (!in_class) { end_csharp_namespace(out); } } void t_csharp_generator::generate_csharp_wcffault(ofstream& out, t_struct* tstruct) { out << endl; indent(out) << "#if !SILVERLIGHT" << endl; indent(out) << "[Serializable]" << endl; indent(out) << "#endif" << endl; indent(out) << "[DataContract]" << endl; bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); indent(out) << "public " << (is_final ? "sealed " : "") << "partial class " << tstruct->get_name() << "Fault" << endl; scope_up(out); const vector<t_field*>& members = tstruct->get_members(); vector<t_field*>::const_iterator m_iter; // make private members with public Properties for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { indent(out) << "private " << declare_field(*m_iter, false, "_") << endl; } out << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { generate_property(out, *m_iter, true, false); } scope_down(out); out << endl; } void t_csharp_generator::generate_csharp_struct_reader(ofstream& out, t_struct* tstruct) { indent(out) << "public void Read (TProtocol iprot)" << endl; scope_up(out); const vector<t_field*>& fields = tstruct->get_members(); vector<t_field*>::const_iterator f_iter; // Required variables aren't in __isset, so we need tmp vars to check them for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (field_is_required((*f_iter))) { indent(out) << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl; } } indent(out) << "TField field;" << endl << indent() << "iprot.ReadStructBegin();" << endl; indent(out) << "while (true)" << endl; scope_up(out); indent(out) << "field = iprot.ReadFieldBegin();" << endl; indent(out) << "if (field.Type == TType.Stop) { " << endl; indent_up(); indent(out) << "break;" << endl; indent_down(); indent(out) << "}" << endl; indent(out) << "switch (field.ID)" << endl; scope_up(out); for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { bool is_required = field_is_required((*f_iter)); indent(out) << "case " << (*f_iter)->get_key() << ":" << endl; indent_up(); indent(out) << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; indent_up(); generate_deserialize_field(out, *f_iter); if (is_required) { indent(out) << "isset_" << (*f_iter)->get_name() << " = true;" << endl; } indent_down(); out << indent() << "} else { " << endl << indent() << " TProtocolUtil.Skip(iprot, field.Type);" << endl << indent() << "}" << endl << indent() << "break;" << endl; indent_down(); } indent(out) << "default: " << endl; indent_up(); indent(out) << "TProtocolUtil.Skip(iprot, field.Type);" << endl; indent(out) << "break;" << endl; indent_down(); scope_down(out); indent(out) << "iprot.ReadFieldEnd();" << endl; scope_down(out); indent(out) << "iprot.ReadStructEnd();" << endl; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (field_is_required((*f_iter))) { indent(out) << "if (!isset_" << (*f_iter)->get_name() << ")" << endl; indent_up(); indent(out) << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl; indent_down(); } } indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_writer(ofstream& out, t_struct* tstruct) { out << indent() << "public void Write(TProtocol oprot) {" << endl; indent_up(); string name = tstruct->get_name(); const vector<t_field*>& fields = tstruct->get_sorted_members(); vector<t_field*>::const_iterator f_iter; indent(out) << "TStruct struc = new TStruct(\"" << name << "\");" << endl; indent(out) << "oprot.WriteStructBegin(struc);" << endl; if (fields.size() > 0) { indent(out) << "TField field = new TField();" << endl; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { bool is_required = field_is_required((*f_iter)); bool has_default = field_has_default((*f_iter)); if (nullable_ && !has_default && !is_required) { indent(out) << "if (" << prop_name((*f_iter)) << " != null) {" << endl; indent_up(); } else if (!is_required) { bool null_allowed = type_can_be_null((*f_iter)->get_type()); if (null_allowed) { indent(out) << "if (" << prop_name((*f_iter)) << " != null && __isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; indent_up(); } else { indent(out) << "if (__isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; indent_up(); } } indent(out) << "field.Name = \"" << (*f_iter)->get_name() << "\";" << endl; indent(out) << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl; indent(out) << "field.ID = " << (*f_iter)->get_key() << ";" << endl; indent(out) << "oprot.WriteFieldBegin(field);" << endl; generate_serialize_field(out, *f_iter); indent(out) << "oprot.WriteFieldEnd();" << endl; if (!is_required) { indent_down(); indent(out) << "}" << endl; } } } indent(out) << "oprot.WriteFieldStop();" << endl; indent(out) << "oprot.WriteStructEnd();" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_result_writer(ofstream& out, t_struct* tstruct) { indent(out) << "public void Write(TProtocol oprot) {" << endl; indent_up(); string name = tstruct->get_name(); const vector<t_field*>& fields = tstruct->get_sorted_members(); vector<t_field*>::const_iterator f_iter; indent(out) << "TStruct struc = new TStruct(\"" << name << "\");" << endl; indent(out) << "oprot.WriteStructBegin(struc);" << endl; if (fields.size() > 0) { indent(out) << "TField field = new TField();" << endl; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; out << endl << indent() << "if "; } else { out << " else if "; } if (nullable_) { out << "(this." << prop_name((*f_iter)) << " != null) {" << endl; } else { out << "(this.__isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; } indent_up(); bool null_allowed = !nullable_ && type_can_be_null((*f_iter)->get_type()); if (null_allowed) { indent(out) << "if (" << prop_name(*f_iter) << " != null) {" << endl; indent_up(); } indent(out) << "field.Name = \"" << prop_name(*f_iter) << "\";" << endl; indent(out) << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl; indent(out) << "field.ID = " << (*f_iter)->get_key() << ";" << endl; indent(out) << "oprot.WriteFieldBegin(field);" << endl; generate_serialize_field(out, *f_iter); indent(out) << "oprot.WriteFieldEnd();" << endl; if (null_allowed) { indent_down(); indent(out) << "}" << endl; } indent_down(); indent(out) << "}"; } } out << endl << indent() << "oprot.WriteFieldStop();" << endl << indent() << "oprot.WriteStructEnd();" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_tostring(ofstream& out, t_struct* tstruct) { indent(out) << "public override string ToString() {" << endl; indent_up(); indent(out) << "StringBuilder __sb = new StringBuilder(\"" << tstruct->get_name() << "(\");" << endl; const vector<t_field*>& fields = tstruct->get_members(); vector<t_field*>::const_iterator f_iter; bool useFirstFlag = false; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (!field_is_required((*f_iter))) { indent(out) << "bool __first = true;" << endl; useFirstFlag = true; } break; } bool had_required = false; // set to true after first required field has been processed for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { bool is_required = field_is_required((*f_iter)); bool has_default = field_has_default((*f_iter)); if (nullable_ && !has_default && !is_required) { indent(out) << "if (" << prop_name((*f_iter)) << " != null) {" << endl; indent_up(); } else if (!is_required) { bool null_allowed = type_can_be_null((*f_iter)->get_type()); if (null_allowed) { indent(out) << "if (" << prop_name((*f_iter)) << " != null && __isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; indent_up(); } else { indent(out) << "if (__isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; indent_up(); } } if (useFirstFlag && (!had_required)) { indent(out) << "if(!__first) { __sb.Append(\", \"); }" << endl; if (!is_required) { indent(out) << "__first = false;" << endl; } indent(out) << "__sb.Append(\"" << prop_name((*f_iter)) << ": \");" << endl; } else { indent(out) << "__sb.Append(\", " << prop_name((*f_iter)) << ": \");" << endl; } t_type* ttype = (*f_iter)->get_type(); if (ttype->is_xception() || ttype->is_struct()) { indent(out) << "__sb.Append(" << prop_name((*f_iter)) << "== null ? \"<null>\" : " << prop_name((*f_iter)) << ".ToString());" << endl; } else { indent(out) << "__sb.Append(" << prop_name((*f_iter)) << ");" << endl; } if (!is_required) { indent_down(); indent(out) << "}" << endl; } else { had_required = true; // now __first must be false, so we don't need to check it anymore } } indent(out) << "__sb.Append(\")\");" << endl; indent(out) << "return __sb.ToString();" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_union(t_struct* tunion) { string f_union_name = namespace_dir_ + "/" + (tunion->get_name()) + ".cs"; ofstream f_union; f_union.open(f_union_name.c_str()); f_union << autogen_comment() << csharp_type_usings() << csharp_thrift_usings() << endl; generate_csharp_union_definition(f_union, tunion); f_union.close(); } void t_csharp_generator::generate_csharp_union_definition(std::ofstream& out, t_struct* tunion) { // Let's define the class first start_csharp_namespace(out); indent(out) << "public abstract partial class " << tunion->get_name() << " : TAbstractBase {" << endl; indent_up(); indent(out) << "public abstract void Write(TProtocol protocol);" << endl; indent(out) << "public readonly bool Isset;" << endl; indent(out) << "public abstract object Data { get; }" << endl; indent(out) << "protected " << tunion->get_name() << "(bool isset) {" << endl; indent_up(); indent(out) << "Isset = isset;" << endl; indent_down(); indent(out) << "}" << endl << endl; indent(out) << "public class ___undefined : " << tunion->get_name() << " {" << endl; indent_up(); indent(out) << "public override object Data { get { return null; } }" << endl; indent(out) << "public ___undefined() : base(false) {}" << endl << endl; indent(out) << "public override void Write(TProtocol protocol) {" << endl; indent_up(); indent(out) << "throw new TProtocolException( TProtocolException.INVALID_DATA, \"Cannot persist " "an union type which is not set.\");" << endl; indent_down(); indent(out) << "}" << endl << endl; indent_down(); indent(out) << "}" << endl << endl; const vector<t_field*>& fields = tunion->get_members(); vector<t_field*>::const_iterator f_iter; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { generate_csharp_union_class(out, tunion, (*f_iter)); } generate_csharp_union_reader(out, tunion); indent_down(); indent(out) << "}" << endl << endl; end_csharp_namespace(out); } void t_csharp_generator::generate_csharp_union_class(std::ofstream& out, t_struct* tunion, t_field* tfield) { indent(out) << "public class " << tfield->get_name() << " : " << tunion->get_name() << " {" << endl; indent_up(); indent(out) << "private " << type_name(tfield->get_type()) << " _data;" << endl; indent(out) << "public override object Data { get { return _data; } }" << endl; indent(out) << "public " << tfield->get_name() << "(" << type_name(tfield->get_type()) << " data) : base(true) {" << endl; indent_up(); indent(out) << "this._data = data;" << endl; indent_down(); indent(out) << "}" << endl; indent(out) << "public override void Write(TProtocol oprot) {" << endl; indent_up(); indent(out) << "TStruct struc = new TStruct(\"" << tunion->get_name() << "\");" << endl; indent(out) << "oprot.WriteStructBegin(struc);" << endl; indent(out) << "TField field = new TField();" << endl; indent(out) << "field.Name = \"" << tfield->get_name() << "\";" << endl; indent(out) << "field.Type = " << type_to_enum(tfield->get_type()) << ";" << endl; indent(out) << "field.ID = " << tfield->get_key() << ";" << endl; indent(out) << "oprot.WriteFieldBegin(field);" << endl; generate_serialize_field(out, tfield, "_data", true, true); indent(out) << "oprot.WriteFieldEnd();" << endl; indent(out) << "oprot.WriteFieldStop();" << endl; indent(out) << "oprot.WriteStructEnd();" << endl; indent_down(); indent(out) << "}" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_equals(ofstream& out, t_struct* tstruct) { indent(out) << "public override bool Equals(object that) {" << endl; indent_up(); indent(out) << "var other = that as " << type_name(tstruct) << ";" << endl; indent(out) << "if (other == null) return false;" << endl; indent(out) << "if (ReferenceEquals(this, other)) return true;" << endl; const vector<t_field*>& fields = tstruct->get_members(); vector<t_field*>::const_iterator f_iter; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; indent(out) << "return "; indent_up(); } else { out << endl; indent(out) << "&& "; } if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter)))) { out << "((__isset." << normalize_name((*f_iter)->get_name()) << " == other.__isset." << normalize_name((*f_iter)->get_name()) << ") && ((!__isset." << normalize_name((*f_iter)->get_name()) << ") || ("; } t_type* ttype = (*f_iter)->get_type(); if (ttype->is_container()) { out << "TCollections.Equals("; } else { out << "System.Object.Equals("; } out << prop_name((*f_iter)) << ", other." << prop_name((*f_iter)) << ")"; if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter)))) { out << ")))"; } } if (first) { indent(out) << "return true;" << endl; } else { out << ";" << endl; indent_down(); } indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_hashcode(ofstream& out, t_struct* tstruct) { indent(out) << "public override int GetHashCode() {" << endl; indent_up(); indent(out) << "int hashcode = 0;" << endl; indent(out) << "unchecked {" << endl; indent_up(); const vector<t_field*>& fields = tstruct->get_members(); vector<t_field*>::const_iterator f_iter; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { t_type* ttype = (*f_iter)->get_type(); indent(out) << "hashcode = (hashcode * 397) ^ "; if (field_is_required((*f_iter))) { out << "("; } else if (nullable_) { out << "(" << prop_name((*f_iter)) << " == null ? 0 : "; } else { out << "(!__isset." << normalize_name((*f_iter)->get_name()) << " ? 0 : "; } if (ttype->is_container()) { out << "(TCollections.GetHashCode(" << prop_name((*f_iter)) << "))"; } else { out << "(" << prop_name((*f_iter)) << ".GetHashCode())"; } out << ");" << endl; } indent_down(); indent(out) << "}" << endl; indent(out) << "return hashcode;" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_service(t_service* tservice) { string f_service_name = namespace_dir_ + "/" + service_name_ + ".cs"; f_service_.open(f_service_name.c_str()); f_service_ << autogen_comment() << csharp_type_usings() << csharp_thrift_usings() << endl; start_csharp_namespace(f_service_); indent(f_service_) << "public partial class " << normalize_name(service_name_) << " {" << endl; indent_up(); generate_service_interface(tservice); generate_service_client(tservice); generate_service_server(tservice); generate_service_helpers(tservice); indent_down(); indent(f_service_) << "}" << endl; end_csharp_namespace(f_service_); f_service_.close(); } void t_csharp_generator::generate_service_interface(t_service* tservice) { string extends = ""; string extends_iface = ""; if (tservice->get_extends() != NULL) { extends = type_name(tservice->get_extends()); extends_iface = " : " + extends + ".Iface"; } generate_csharp_doc(f_service_, tservice); if (wcf_) { indent(f_service_) << "[ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl; } indent(f_service_) << "public interface Iface" << extends_iface << " {" << endl; indent_up(); vector<t_function*> functions = tservice->get_functions(); vector<t_function*>::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { generate_csharp_doc(f_service_, *f_iter); // if we're using WCF, add the corresponding attributes if (wcf_) { indent(f_service_) << "[OperationContract]" << endl; const std::vector<t_field*>& xceptions = (*f_iter)->get_xceptions()->get_members(); vector<t_field*>::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { indent(f_service_) << "[FaultContract(typeof(" + type_name((*x_iter)->get_type(), false, false) + "Fault))]" << endl; } } indent(f_service_) << function_signature(*f_iter) << ";" << endl; if (!async_) { indent(f_service_) << "#if SILVERLIGHT" << endl; } indent(f_service_) << function_signature_async_begin(*f_iter, "Begin_") << ";" << endl; indent(f_service_) << function_signature_async_end(*f_iter, "End_") << ";" << endl; if (async_ || async_ctp_) { indent(f_service_) << function_signature_async(*f_iter) << ";" << endl; } if (!async_) { indent(f_service_) << "#endif" << endl; } } indent_down(); f_service_ << indent() << "}" << endl << endl; } void t_csharp_generator::generate_service_helpers(t_service* tservice) { vector<t_function*> functions = tservice->get_functions(); vector<t_function*>::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_struct* ts = (*f_iter)->get_arglist(); generate_csharp_struct_definition(f_service_, ts, false, true); generate_function_helpers(*f_iter); } } void t_csharp_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; if (tservice->get_extends() != NULL) { extends = type_name(tservice->get_extends()); extends_client = extends + ".Client, "; } else { extends_client = "IDisposable, "; } generate_csharp_doc(f_service_, tservice); indent(f_service_) << "public class Client : " << extends_client << "Iface {" << endl; indent_up(); indent(f_service_) << "public Client(TProtocol prot) : this(prot, prot)" << endl; scope_up(f_service_); scope_down(f_service_); f_service_ << endl; indent(f_service_) << "public Client(TProtocol iprot, TProtocol oprot)"; if (!extends.empty()) { f_service_ << " : base(iprot, oprot)"; } f_service_ << endl; scope_up(f_service_); if (extends.empty()) { f_service_ << indent() << "iprot_ = iprot;" << endl << indent() << "oprot_ = oprot;" << endl; } scope_down(f_service_); f_service_ << endl; if (extends.empty()) { f_service_ << indent() << "protected TProtocol iprot_;" << endl << indent() << "protected TProtocol oprot_;" << endl << indent() << "protected int seqid_;" << endl << endl; f_service_ << indent() << "public TProtocol InputProtocol" << endl; scope_up(f_service_); indent(f_service_) << "get { return iprot_; }" << endl; scope_down(f_service_); f_service_ << indent() << "public TProtocol OutputProtocol" << endl; scope_up(f_service_); indent(f_service_) << "get { return oprot_; }" << endl; scope_down(f_service_); f_service_ << endl << endl; indent(f_service_) << "#region \" IDisposable Support \"" << endl; indent(f_service_) << "private bool _IsDisposed;" << endl << endl; indent(f_service_) << "// IDisposable" << endl; indent(f_service_) << "public void Dispose()" << endl; scope_up(f_service_); indent(f_service_) << "Dispose(true);" << endl; scope_down(f_service_); indent(f_service_) << endl << endl; indent(f_service_) << "protected virtual void Dispose(bool disposing)" << endl; scope_up(f_service_); indent(f_service_) << "if (!_IsDisposed)" << endl; scope_up(f_service_); indent(f_service_) << "if (disposing)" << endl; scope_up(f_service_); indent(f_service_) << "if (iprot_ != null)" << endl; scope_up(f_service_); indent(f_service_) << "((IDisposable)iprot_).Dispose();" << endl; scope_down(f_service_); indent(f_service_) << "if (oprot_ != null)" << endl; scope_up(f_service_); indent(f_service_) << "((IDisposable)oprot_).Dispose();" << endl; scope_down(f_service_); scope_down(f_service_); scope_down(f_service_); indent(f_service_) << "_IsDisposed = true;" << endl; scope_down(f_service_); indent(f_service_) << "#endregion" << endl; f_service_ << endl << endl; } vector<t_function*> functions = tservice->get_functions(); vector<t_function*>::const_iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { string funname = (*f_iter)->get_name(); indent(f_service_) << endl; if (!async_) { indent(f_service_) << "#if SILVERLIGHT" << endl; } // Begin_ indent(f_service_) << "public " << function_signature_async_begin(*f_iter, "Begin_") << endl; scope_up(f_service_); indent(f_service_) << "return " << "send_" << funname << "(callback, state"; t_struct* arg_struct = (*f_iter)->get_arglist(); prepare_member_name_mapping(arg_struct); const vector<t_field*>& fields = arg_struct->get_members(); vector<t_field*>::const_iterator fld_iter; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { f_service_ << ", "; f_service_ << normalize_name((*fld_iter)->get_name()); } f_service_ << ");" << endl; scope_down(f_service_); f_service_ << endl; // End indent(f_service_) << "public " << function_signature_async_end(*f_iter, "End_") << endl; scope_up(f_service_); indent(f_service_) << "oprot_.Transport.EndFlush(asyncResult);" << endl; if (!(*f_iter)->is_oneway()) { f_service_ << indent(); if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << "return "; } f_service_ << "recv_" << funname << "();" << endl; } scope_down(f_service_); f_service_ << endl; // async bool first; if (async_ || async_ctp_) { indent(f_service_) << "public async " << function_signature_async(*f_iter, "") << endl; scope_up(f_service_); if (!(*f_iter)->get_returntype()->is_void()) { indent(f_service_) << type_name((*f_iter)->get_returntype()) << " retval;" << endl; indent(f_service_) << "retval = "; } else { indent(f_service_); } if (async_) { f_service_ << "await Task.Run(() =>" << endl; } else { f_service_ << "await TaskEx.Run(() =>" << endl; } scope_up(f_service_); indent(f_service_); if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << "return "; } f_service_ << funname << "("; first = true; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { if (first) { first = false; } else { f_service_ << ", "; } f_service_ << (*fld_iter)->get_name(); } f_service_ << ");" << endl; indent_down(); indent(f_service_) << "});" << endl; if (!(*f_iter)->get_returntype()->is_void()) { indent(f_service_) << "return retval;" << endl; } scope_down(f_service_); f_service_ << endl; } if (!async_) { indent(f_service_) << "#endif" << endl << endl; } // "Normal" Synchronous invoke generate_csharp_doc(f_service_, *f_iter); indent(f_service_) << "public " << function_signature(*f_iter) << endl; scope_up(f_service_); if (!async_) { indent(f_service_) << "#if !SILVERLIGHT" << endl; indent(f_service_) << "send_" << funname << "("; first = true; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { if (first) { first = false; } else { f_service_ << ", "; } f_service_ << normalize_name((*fld_iter)->get_name()); } f_service_ << ");" << endl; if (!(*f_iter)->is_oneway()) { f_service_ << indent(); if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << "return "; } f_service_ << "recv_" << funname << "();" << endl; } f_service_ << endl; indent(f_service_) << "#else" << endl; } // Silverlight synchronous invoke indent(f_service_) << "var asyncResult = Begin_" << funname << "(null, null"; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { f_service_ << ", " << normalize_name((*fld_iter)->get_name()); } f_service_ << ");" << endl; if (!(*f_iter)->is_oneway()) { f_service_ << indent(); if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << "return "; } f_service_ << "End_" << funname << "(asyncResult);" << endl; } f_service_ << endl; if (!async_) { indent(f_service_) << "#endif" << endl; } scope_down(f_service_); // Send t_function send_function(g_type_void, string("send_") + (*f_iter)->get_name(), (*f_iter)->get_arglist()); string argsname = (*f_iter)->get_name() + "_args"; if (!async_) { indent(f_service_) << "#if SILVERLIGHT" << endl; } indent(f_service_) << "public " << function_signature_async_begin(&send_function) << endl; if (!async_) { indent(f_service_) << "#else" << endl; indent(f_service_) << "public " << function_signature(&send_function) << endl; indent(f_service_) << "#endif" << endl; } scope_up(f_service_); f_service_ << indent() << "oprot_.WriteMessageBegin(new TMessage(\"" << funname << "\", " << ((*f_iter)->is_oneway() ? "TMessageType.Oneway" : "TMessageType.Call") << ", seqid_));" << endl << indent() << argsname << " args = new " << argsname << "();" << endl; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { f_service_ << indent() << "args." << prop_name(*fld_iter) << " = " << normalize_name((*fld_iter)->get_name()) << ";" << endl; } f_service_ << indent() << "args.Write(oprot_);" << endl << indent() << "oprot_.WriteMessageEnd();" << endl; ; if (!async_) { indent(f_service_) << "#if SILVERLIGHT" << endl; } indent(f_service_) << "return oprot_.Transport.BeginFlush(callback, state);" << endl; if (!async_) { indent(f_service_) << "#else" << endl; indent(f_service_) << "oprot_.Transport.Flush();" << endl; indent(f_service_) << "#endif" << endl; } cleanup_member_name_mapping(arg_struct); scope_down(f_service_); f_service_ << endl; if (!(*f_iter)->is_oneway()) { string resultname = (*f_iter)->get_name() + "_result"; t_struct noargs(program_); t_function recv_function((*f_iter)->get_returntype(), string("recv_") + (*f_iter)->get_name(), &noargs, (*f_iter)->get_xceptions()); indent(f_service_) << "public " << function_signature(&recv_function) << endl; scope_up(f_service_); prepare_member_name_mapping((*f_iter)->get_xceptions()); f_service_ << indent() << "TMessage msg = iprot_.ReadMessageBegin();" << endl << indent() << "if (msg.Type == TMessageType.Exception) {" << endl; indent_up(); f_service_ << indent() << "TApplicationException x = TApplicationException.Read(iprot_);" << endl << indent() << "iprot_.ReadMessageEnd();" << endl << indent() << "throw x;" << endl; indent_down(); f_service_ << indent() << "}" << endl << indent() << resultname << " result = new " << resultname << "();" << endl << indent() << "result.Read(iprot_);" << endl << indent() << "iprot_.ReadMessageEnd();" << endl; if (!(*f_iter)->get_returntype()->is_void()) { if (nullable_) { if (type_can_be_null((*f_iter)->get_returntype())) { f_service_ << indent() << "if (result.Success != null) {" << endl << indent() << " return result.Success;" << endl << indent() << "}" << endl; } else { f_service_ << indent() << "if (result.Success.HasValue) {" << endl << indent() << " return result.Success.Value;" << endl << indent() << "}" << endl; } } else { f_service_ << indent() << "if (result.__isset.success) {" << endl << indent() << " return result.Success;" << endl << indent() << "}" << endl; } } t_struct* xs = (*f_iter)->get_xceptions(); const std::vector<t_field*>& xceptions = xs->get_members(); vector<t_field*>::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { if (nullable_) { f_service_ << indent() << "if (result." << prop_name(*x_iter) << " != null) {" << endl << indent() << " throw result." << prop_name(*x_iter) << ";" << endl << indent() << "}" << endl; } else { f_service_ << indent() << "if (result.__isset." << normalize_name((*x_iter)->get_name()) << ") {" << endl << indent() << " throw result." << prop_name(*x_iter) << ";" << endl << indent() << "}" << endl; } } if ((*f_iter)->get_returntype()->is_void()) { indent(f_service_) << "return;" << endl; } else { f_service_ << indent() << "throw new " "TApplicationException(TApplicationException.ExceptionType.MissingResult, \"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl; } cleanup_member_name_mapping((*f_iter)->get_xceptions()); scope_down(f_service_); f_service_ << endl; } } indent_down(); indent(f_service_) << "}" << endl; } void t_csharp_generator::generate_service_server(t_service* tservice) { vector<t_function*> functions = tservice->get_functions(); vector<t_function*>::iterator f_iter; string extends = ""; string extends_processor = ""; if (tservice->get_extends() != NULL) { extends = type_name(tservice->get_extends()); extends_processor = extends + ".Processor, "; } indent(f_service_) << "public class Processor : " << extends_processor << "TProcessor {" << endl; indent_up(); indent(f_service_) << "public Processor(Iface iface)"; if (!extends.empty()) { f_service_ << " : base(iface)"; } f_service_ << endl; scope_up(f_service_); f_service_ << indent() << "iface_ = iface;" << endl; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_service_ << indent() << "processMap_[\"" << (*f_iter)->get_name() << "\"] = " << (*f_iter)->get_name() << "_Process;" << endl; } scope_down(f_service_); f_service_ << endl; if (extends.empty()) { f_service_ << indent() << "protected delegate void ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot);" << endl; } f_service_ << indent() << "private Iface iface_;" << endl; if (extends.empty()) { f_service_ << indent() << "protected Dictionary<string, ProcessFunction> processMap_ = new " "Dictionary<string, ProcessFunction>();" << endl; } f_service_ << endl; if (extends.empty()) { indent(f_service_) << "public bool Process(TProtocol iprot, TProtocol oprot)" << endl; } else { indent(f_service_) << "public new bool Process(TProtocol iprot, TProtocol oprot)" << endl; } scope_up(f_service_); f_service_ << indent() << "try" << endl; scope_up(f_service_); f_service_ << indent() << "TMessage msg = iprot.ReadMessageBegin();" << endl; f_service_ << indent() << "ProcessFunction fn;" << endl << indent() << "processMap_.TryGetValue(msg.Name, out fn);" << endl << indent() << "if (fn == null) {" << endl << indent() << " TProtocolUtil.Skip(iprot, TType.Struct);" << endl << indent() << " iprot.ReadMessageEnd();" << endl << indent() << " TApplicationException x = new TApplicationException " "(TApplicationException.ExceptionType.UnknownMethod, \"Invalid method name: '\" + " "msg.Name + \"'\");" << endl << indent() << " oprot.WriteMessageBegin(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID));" << endl << indent() << " x.Write(oprot);" << endl << indent() << " oprot.WriteMessageEnd();" << endl << indent() << " oprot.Transport.Flush();" << endl << indent() << " return true;" << endl << indent() << "}" << endl << indent() << "fn(msg.SeqID, iprot, oprot);" << endl; scope_down(f_service_); f_service_ << indent() << "catch (IOException)" << endl; scope_up(f_service_); f_service_ << indent() << "return false;" << endl; scope_down(f_service_); f_service_ << indent() << "return true;" << endl; scope_down(f_service_); f_service_ << endl; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { generate_process_function(tservice, *f_iter); } indent_down(); indent(f_service_) << "}" << endl << endl; } void t_csharp_generator::generate_function_helpers(t_function* tfunction) { if (tfunction->is_oneway()) { return; } t_struct result(program_, tfunction->get_name() + "_result"); t_field success(tfunction->get_returntype(), "success", 0); if (!tfunction->get_returntype()->is_void()) { result.append(&success); } t_struct* xs = tfunction->get_xceptions(); const vector<t_field*>& fields = xs->get_members(); vector<t_field*>::const_iterator f_iter; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { result.append(*f_iter); } generate_csharp_struct_definition(f_service_, &result, false, true, true); } void t_csharp_generator::generate_process_function(t_service* tservice, t_function* tfunction) { (void)tservice; indent(f_service_) << "public void " << tfunction->get_name() << "_Process(int seqid, TProtocol iprot, TProtocol oprot)" << endl; scope_up(f_service_); string argsname = tfunction->get_name() + "_args"; string resultname = tfunction->get_name() + "_result"; f_service_ << indent() << argsname << " args = new " << argsname << "();" << endl << indent() << "args.Read(iprot);" << endl << indent() << "iprot.ReadMessageEnd();" << endl; t_struct* xs = tfunction->get_xceptions(); const std::vector<t_field*>& xceptions = xs->get_members(); vector<t_field*>::const_iterator x_iter; if (!tfunction->is_oneway()) { f_service_ << indent() << resultname << " result = new " << resultname << "();" << endl; } if (xceptions.size() > 0) { f_service_ << indent() << "try {" << endl; indent_up(); } t_struct* arg_struct = tfunction->get_arglist(); const std::vector<t_field*>& fields = arg_struct->get_members(); vector<t_field*>::const_iterator f_iter; f_service_ << indent(); if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { f_service_ << "result.Success = "; } f_service_ << "iface_." << normalize_name(tfunction->get_name()) << "("; bool first = true; prepare_member_name_mapping(arg_struct); for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; } else { f_service_ << ", "; } f_service_ << "args." << prop_name(*f_iter); if (nullable_ && !type_can_be_null((*f_iter)->get_type())) { f_service_ << ".Value"; } } cleanup_member_name_mapping(arg_struct); f_service_ << ");" << endl; if (!tfunction->is_oneway() && xceptions.size() > 0) { indent_down(); f_service_ << indent() << "}"; prepare_member_name_mapping(xs); for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { f_service_ << " catch (" << type_name((*x_iter)->get_type(), false, false) << " " << (*x_iter)->get_name() << ") {" << endl; if (!tfunction->is_oneway()) { indent_up(); f_service_ << indent() << "result." << prop_name(*x_iter) << " = " << (*x_iter)->get_name() << ";" << endl; indent_down(); f_service_ << indent() << "}"; } else { f_service_ << "}"; } } cleanup_member_name_mapping(xs); f_service_ << endl; } if (tfunction->is_oneway()) { f_service_ << indent() << "return;" << endl; scope_down(f_service_); return; } f_service_ << indent() << "oprot.WriteMessageBegin(new TMessage(\"" << tfunction->get_name() << "\", TMessageType.Reply, seqid)); " << endl << indent() << "result.Write(oprot);" << endl << indent() << "oprot.WriteMessageEnd();" << endl << indent() << "oprot.Transport.Flush();" << endl; scope_down(f_service_); f_service_ << endl; } void t_csharp_generator::generate_csharp_union_reader(std::ofstream& out, t_struct* tunion) { // Thanks to THRIFT-1768, we don't need to check for required fields in the union const vector<t_field*>& fields = tunion->get_members(); vector<t_field*>::const_iterator f_iter; indent(out) << "public static " << tunion->get_name() << " Read(TProtocol iprot)" << endl; scope_up(out); indent(out) << tunion->get_name() << " retval;" << endl; indent(out) << "iprot.ReadStructBegin();" << endl; indent(out) << "TField field = iprot.ReadFieldBegin();" << endl; // we cannot have the first field be a stop -- we must have a single field defined indent(out) << "if (field.Type == TType.Stop)" << endl; scope_up(out); indent(out) << "iprot.ReadFieldEnd();" << endl; indent(out) << "retval = new ___undefined();" << endl; scope_down(out); indent(out) << "else" << endl; scope_up(out); indent(out) << "switch (field.ID)" << endl; scope_up(out); for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { indent(out) << "case " << (*f_iter)->get_key() << ":" << endl; indent_up(); indent(out) << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; indent_up(); indent(out) << type_name((*f_iter)->get_type()) << " temp;" << endl; generate_deserialize_field(out, (*f_iter), "temp", true); indent(out) << "retval = new " << (*f_iter)->get_name() << "(temp);" << endl; indent_down(); out << indent() << "} else { " << endl << indent() << " TProtocolUtil.Skip(iprot, field.Type);" << endl << indent() << " retval = new ___undefined();" << endl << indent() << "}" << endl << indent() << "break;" << endl; indent_down(); } indent(out) << "default: " << endl; indent_up(); indent(out) << "TProtocolUtil.Skip(iprot, field.Type);" << endl << indent() << "retval = new ___undefined();" << endl; indent(out) << "break;" << endl; indent_down(); scope_down(out); indent(out) << "iprot.ReadFieldEnd();" << endl; indent(out) << "if (iprot.ReadFieldBegin().Type != TType.Stop)" << endl; scope_up(out); indent(out) << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl; scope_down(out); // end of else for TStop scope_down(out); indent(out) << "iprot.ReadStructEnd();" << endl; indent(out) << "return retval;" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_deserialize_field(ofstream& out, t_field* tfield, string prefix, bool is_propertyless) { t_type* type = tfield->get_type(); while (type->is_typedef()) { type = ((t_typedef*)type)->get_type(); } if (type->is_void()) { throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name(); } string name = prefix + (is_propertyless ? "" : prop_name(tfield)); if (type->is_struct() || type->is_xception()) { generate_deserialize_struct(out, (t_struct*)type, name); } else if (type->is_container()) { generate_deserialize_container(out, type, name); } else if (type->is_base_type() || type->is_enum()) { indent(out) << name << " = "; if (type->is_enum()) { out << "(" << type_name(type, false, true) << ")"; } out << "iprot."; if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "compiler error: cannot serialize void field in a struct: " + name; break; case t_base_type::TYPE_STRING: if (((t_base_type*)type)->is_binary()) { out << "ReadBinary();"; } else { out << "ReadString();"; } break; case t_base_type::TYPE_BOOL: out << "ReadBool();"; break; case t_base_type::TYPE_BYTE: out << "ReadByte();"; break; case t_base_type::TYPE_I16: out << "ReadI16();"; break; case t_base_type::TYPE_I32: out << "ReadI32();"; break; case t_base_type::TYPE_I64: out << "ReadI64();"; break; case t_base_type::TYPE_DOUBLE: out << "ReadDouble();"; break; default: throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase); } } else if (type->is_enum()) { out << "ReadI32();"; } out << endl; } else { printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", tfield->get_name().c_str(), type_name(type).c_str()); } } void t_csharp_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string prefix) { if (union_ && tstruct->is_union()) { out << indent() << prefix << " = " << type_name(tstruct) << ".Read(iprot);" << endl; } else { out << indent() << prefix << " = new " << type_name(tstruct) << "();" << endl << indent() << prefix << ".Read(iprot);" << endl; } } void t_csharp_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string prefix) { scope_up(out); string obj; if (ttype->is_map()) { obj = tmp("_map"); } else if (ttype->is_set()) { obj = tmp("_set"); } else if (ttype->is_list()) { obj = tmp("_list"); } indent(out) << prefix << " = new " << type_name(ttype, false, true) << "();" << endl; if (ttype->is_map()) { out << indent() << "TMap " << obj << " = iprot.ReadMapBegin();" << endl; } else if (ttype->is_set()) { out << indent() << "TSet " << obj << " = iprot.ReadSetBegin();" << endl; } else if (ttype->is_list()) { out << indent() << "TList " << obj << " = iprot.ReadListBegin();" << endl; } string i = tmp("_i"); indent(out) << "for( int " << i << " = 0; " << i << " < " << obj << ".Count" << "; " << "++" << i << ")" << endl; scope_up(out); if (ttype->is_map()) { generate_deserialize_map_element(out, (t_map*)ttype, prefix); } else if (ttype->is_set()) { generate_deserialize_set_element(out, (t_set*)ttype, prefix); } else if (ttype->is_list()) { generate_deserialize_list_element(out, (t_list*)ttype, prefix); } scope_down(out); if (ttype->is_map()) { indent(out) << "iprot.ReadMapEnd();" << endl; } else if (ttype->is_set()) { indent(out) << "iprot.ReadSetEnd();" << endl; } else if (ttype->is_list()) { indent(out) << "iprot.ReadListEnd();" << endl; } scope_down(out); } void t_csharp_generator::generate_deserialize_map_element(ofstream& out, t_map* tmap, string prefix) { string key = tmp("_key"); string val = tmp("_val"); t_field fkey(tmap->get_key_type(), key); t_field fval(tmap->get_val_type(), val); indent(out) << declare_field(&fkey) << endl; indent(out) << declare_field(&fval) << endl; generate_deserialize_field(out, &fkey); generate_deserialize_field(out, &fval); indent(out) << prefix << "[" << key << "] = " << val << ";" << endl; } void t_csharp_generator::generate_deserialize_set_element(ofstream& out, t_set* tset, string prefix) { string elem = tmp("_elem"); t_field felem(tset->get_elem_type(), elem); indent(out) << declare_field(&felem) << endl; generate_deserialize_field(out, &felem); indent(out) << prefix << ".Add(" << elem << ");" << endl; } void t_csharp_generator::generate_deserialize_list_element(ofstream& out, t_list* tlist, string prefix) { string elem = tmp("_elem"); t_field felem(tlist->get_elem_type(), elem); indent(out) << declare_field(&felem) << endl; generate_deserialize_field(out, &felem); indent(out) << prefix << ".Add(" << elem << ");" << endl; } void t_csharp_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix, bool is_element, bool is_propertyless) { t_type* type = tfield->get_type(); while (type->is_typedef()) { type = ((t_typedef*)type)->get_type(); } string name = prefix + (is_propertyless ? "" : prop_name(tfield)); if (type->is_void()) { throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name; } if (type->is_struct() || type->is_xception()) { generate_serialize_struct(out, (t_struct*)type, name); } else if (type->is_container()) { generate_serialize_container(out, type, name); } else if (type->is_base_type() || type->is_enum()) { indent(out) << "oprot."; string nullable_name = nullable_ && !is_element && !field_is_required(tfield) ? name + ".Value" : name; if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "compiler error: cannot serialize void field in a struct: " + name; break; case t_base_type::TYPE_STRING: if (((t_base_type*)type)->is_binary()) { out << "WriteBinary("; } else { out << "WriteString("; } out << name << ");"; break; case t_base_type::TYPE_BOOL: out << "WriteBool(" << nullable_name << ");"; break; case t_base_type::TYPE_BYTE: out << "WriteByte(" << nullable_name << ");"; break; case t_base_type::TYPE_I16: out << "WriteI16(" << nullable_name << ");"; break; case t_base_type::TYPE_I32: out << "WriteI32(" << nullable_name << ");"; break; case t_base_type::TYPE_I64: out << "WriteI64(" << nullable_name << ");"; break; case t_base_type::TYPE_DOUBLE: out << "WriteDouble(" << nullable_name << ");"; break; default: throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase); } } else if (type->is_enum()) { out << "WriteI32((int)" << nullable_name << ");"; } out << endl; } else { printf("DO NOT KNOW HOW TO SERIALIZE '%s%s' TYPE '%s'\n", prefix.c_str(), tfield->get_name().c_str(), type_name(type).c_str()); } } void t_csharp_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { (void)tstruct; out << indent() << prefix << ".Write(oprot);" << endl; } void t_csharp_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { scope_up(out); if (ttype->is_map()) { indent(out) << "oprot.WriteMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix << ".Count));" << endl; } else if (ttype->is_set()) { indent(out) << "oprot.WriteSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << prefix << ".Count));" << endl; } else if (ttype->is_list()) { indent(out) << "oprot.WriteListBegin(new TList(" << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".Count));" << endl; } string iter = tmp("_iter"); if (ttype->is_map()) { indent(out) << "foreach (" << type_name(((t_map*)ttype)->get_key_type()) << " " << iter << " in " << prefix << ".Keys)"; } else if (ttype->is_set()) { indent(out) << "foreach (" << type_name(((t_set*)ttype)->get_elem_type()) << " " << iter << " in " << prefix << ")"; } else if (ttype->is_list()) { indent(out) << "foreach (" << type_name(((t_list*)ttype)->get_elem_type()) << " " << iter << " in " << prefix << ")"; } out << endl; scope_up(out); if (ttype->is_map()) { generate_serialize_map_element(out, (t_map*)ttype, iter, prefix); } else if (ttype->is_set()) { generate_serialize_set_element(out, (t_set*)ttype, iter); } else if (ttype->is_list()) { generate_serialize_list_element(out, (t_list*)ttype, iter); } scope_down(out); if (ttype->is_map()) { indent(out) << "oprot.WriteMapEnd();" << endl; } else if (ttype->is_set()) { indent(out) << "oprot.WriteSetEnd();" << endl; } else if (ttype->is_list()) { indent(out) << "oprot.WriteListEnd();" << endl; } scope_down(out); } void t_csharp_generator::generate_serialize_map_element(ofstream& out, t_map* tmap, string iter, string map) { t_field kfield(tmap->get_key_type(), iter); generate_serialize_field(out, &kfield, "", true); t_field vfield(tmap->get_val_type(), map + "[" + iter + "]"); generate_serialize_field(out, &vfield, "", true); } void t_csharp_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { t_field efield(tset->get_elem_type(), iter); generate_serialize_field(out, &efield, "", true); } void t_csharp_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { t_field efield(tlist->get_elem_type(), iter); generate_serialize_field(out, &efield, "", true); } void t_csharp_generator::generate_property(ofstream& out, t_field* tfield, bool isPublic, bool generateIsset) { generate_csharp_property(out, tfield, isPublic, generateIsset, "_"); } void t_csharp_generator::generate_csharp_property(ofstream& out, t_field* tfield, bool isPublic, bool generateIsset, std::string fieldPrefix) { if ((serialize_ || wcf_) && isPublic) { indent(out) << "[DataMember(Order = 0)]" << endl; } bool has_default = field_has_default(tfield); bool is_required = field_is_required(tfield); if ((nullable_ && !has_default) || (is_required)) { indent(out) << (isPublic ? "public " : "private ") << type_name(tfield->get_type(), false, false, true, is_required) << " " << prop_name(tfield) << " { get; set; }" << endl; } else { indent(out) << (isPublic ? "public " : "private ") << type_name(tfield->get_type(), false, false, true) << " " << prop_name(tfield) << endl; scope_up(out); indent(out) << "get" << endl; scope_up(out); bool use_nullable = false; if (nullable_) { t_type* ttype = tfield->get_type(); while (ttype->is_typedef()) { ttype = ((t_typedef*)ttype)->get_type(); } if (ttype->is_base_type()) { use_nullable = ((t_base_type*)ttype)->get_base() != t_base_type::TYPE_STRING; } } indent(out) << "return " << fieldPrefix + tfield->get_name() << ";" << endl; scope_down(out); indent(out) << "set" << endl; scope_up(out); if (use_nullable) { if (generateIsset) { indent(out) << "__isset." << normalize_name(tfield->get_name()) << " = value.HasValue;" << endl; } indent(out) << "if (value.HasValue) this." << fieldPrefix + tfield->get_name() << " = value.Value;" << endl; } else { if (generateIsset) { indent(out) << "__isset." << normalize_name(tfield->get_name()) << " = true;" << endl; } indent(out) << "this." << fieldPrefix + tfield->get_name() << " = value;" << endl; } scope_down(out); scope_down(out); } out << endl; } std::string t_csharp_generator::make_valid_csharp_identifier(std::string const& fromName) { std::string str = fromName; if (str.empty()) { return str; } // tests rely on this assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9')); // if the first letter is a number, we add an additional underscore in front of it char c = str.at(0); if (('0' <= c) && (c <= '9')) { str = "_" + str; } // following chars: letter, number or underscore for (size_t i = 0; i < str.size(); ++i) { c = str.at(i); if ((('A' > c) || (c > 'Z')) && (('a' > c) || (c > 'z')) && (('0' > c) || (c > '9')) && ('_' != c)) { str.replace(i, 1, "_"); } } return str; } void t_csharp_generator::cleanup_member_name_mapping(void* scope) { if (member_mapping_scope != scope) { if (member_mapping_scope == NULL) { throw "internal error: cleanup_member_name_mapping() not active"; } else { throw "internal error: cleanup_member_name_mapping() called for wrong struct"; } } member_mapping_scope = NULL; member_name_mapping.clear(); } string t_csharp_generator::get_mapped_member_name(string name) { map<string, string>::iterator iter = member_name_mapping.find(name); if (member_name_mapping.end() != iter) { return iter->second; } pverbose("no mapping for member %s\n", name.c_str()); return name; } void t_csharp_generator::prepare_member_name_mapping(t_struct* tstruct) { prepare_member_name_mapping(tstruct, tstruct->get_members(), tstruct->get_name()); } void t_csharp_generator::prepare_member_name_mapping(void* scope, const vector<t_field*>& members, const string& structname) { if (member_mapping_scope != NULL) { if (member_mapping_scope != scope) { throw "internal error: prepare_member_name_mapping() already active for different struct"; } else { throw "internal error: prepare_member_name_mapping() already active for this struct"; } } member_mapping_scope = scope; member_name_mapping.clear(); std::set<std::string> used_member_names; vector<t_field*>::const_iterator iter; // current C# generator policy: // - prop names are always rendered with an Uppercase first letter // - struct names are used as given for (iter = members.begin(); iter != members.end(); ++iter) { string oldname = (*iter)->get_name(); string newname = prop_name(*iter, true); while (true) { // name conflicts with struct (CS0542 error) if (structname.compare(newname) == 0) { pverbose("struct %s: member %s conflicts with struct (preventing CS0542)\n", structname.c_str(), newname.c_str()); newname += '_'; } // new name conflicts with another member if (used_member_names.find(newname) != used_member_names.end()) { pverbose("struct %s: member %s conflicts with another member\n", structname.c_str(), newname.c_str()); newname += '_'; continue; } // add always, this helps us to detect edge cases like // different spellings ("foo" and "Foo") within the same struct pverbose("struct %s: member mapping %s => %s\n", structname.c_str(), oldname.c_str(), newname.c_str()); member_name_mapping[oldname] = newname; used_member_names.insert(newname); break; } } } std::string t_csharp_generator::prop_name(t_field* tfield, bool suppress_mapping) { string name(tfield->get_name()); if (suppress_mapping) { name[0] = toupper(name[0]); } else { name = get_mapped_member_name(name); } return name; } string t_csharp_generator::type_name(t_type* ttype, bool in_container, bool in_init, bool in_param, bool is_required) { (void)in_init; while (ttype->is_typedef()) { ttype = ((t_typedef*)ttype)->get_type(); } if (ttype->is_base_type()) { return base_type_name((t_base_type*)ttype, in_container, in_param, is_required); } else if (ttype->is_map()) { t_map* tmap = (t_map*)ttype; return "Dictionary<" + type_name(tmap->get_key_type(), true) + ", " + type_name(tmap->get_val_type(), true) + ">"; } else if (ttype->is_set()) { t_set* tset = (t_set*)ttype; return "THashSet<" + type_name(tset->get_elem_type(), true) + ">"; } else if (ttype->is_list()) { t_list* tlist = (t_list*)ttype; return "List<" + type_name(tlist->get_elem_type(), true) + ">"; } t_program* program = ttype->get_program(); string postfix = (!is_required && nullable_ && in_param && ttype->is_enum()) ? "?" : ""; if (program != NULL && program != program_) { string ns = program->get_namespace("csharp"); if (!ns.empty()) { return ns + "." + normalize_name(ttype->get_name()) + postfix; } } return normalize_name(ttype->get_name()) + postfix; } string t_csharp_generator::base_type_name(t_base_type* tbase, bool in_container, bool in_param, bool is_required) { (void)in_container; string postfix = (!is_required && nullable_ && in_param) ? "?" : ""; switch (tbase->get_base()) { case t_base_type::TYPE_VOID: return "void"; case t_base_type::TYPE_STRING: if (tbase->is_binary()) { return "byte[]"; } else { return "string"; } case t_base_type::TYPE_BOOL: return "bool" + postfix; case t_base_type::TYPE_BYTE: return "sbyte" + postfix; case t_base_type::TYPE_I16: return "short" + postfix; case t_base_type::TYPE_I32: return "int" + postfix; case t_base_type::TYPE_I64: return "long" + postfix; case t_base_type::TYPE_DOUBLE: return "double" + postfix; default: throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase->get_base()); } } string t_csharp_generator::declare_field(t_field* tfield, bool init, std::string prefix) { string result = type_name(tfield->get_type()) + " " + prefix + tfield->get_name(); if (init) { t_type* ttype = tfield->get_type(); while (ttype->is_typedef()) { ttype = ((t_typedef*)ttype)->get_type(); } if (ttype->is_base_type() && field_has_default(tfield)) { ofstream dummy; result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value()); } else if (ttype->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "NO T_VOID CONSTRUCT"; case t_base_type::TYPE_STRING: result += " = null"; break; case t_base_type::TYPE_BOOL: result += " = false"; break; case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: case t_base_type::TYPE_I64: result += " = 0"; break; case t_base_type::TYPE_DOUBLE: result += " = (double)0"; break; } } else if (ttype->is_enum()) { result += " = (" + type_name(ttype, false, true) + ")0"; } else if (ttype->is_container()) { result += " = new " + type_name(ttype, false, true) + "()"; } else { result += " = new " + type_name(ttype, false, true) + "()"; } } return result + ";"; } string t_csharp_generator::function_signature(t_function* tfunction, string prefix) { t_type* ttype = tfunction->get_returntype(); return type_name(ttype) + " " + normalize_name(prefix + tfunction->get_name()) + "(" + argument_list(tfunction->get_arglist()) + ")"; } string t_csharp_generator::function_signature_async_begin(t_function* tfunction, string prefix) { string comma = (tfunction->get_arglist()->get_members().size() > 0 ? ", " : ""); return "IAsyncResult " + normalize_name(prefix + tfunction->get_name()) + "(AsyncCallback callback, object state" + comma + argument_list(tfunction->get_arglist()) + ")"; } string t_csharp_generator::function_signature_async_end(t_function* tfunction, string prefix) { t_type* ttype = tfunction->get_returntype(); return type_name(ttype) + " " + normalize_name(prefix + tfunction->get_name()) + "(IAsyncResult asyncResult)"; } string t_csharp_generator::function_signature_async(t_function* tfunction, string prefix) { t_type* ttype = tfunction->get_returntype(); string task = "Task"; if (!ttype->is_void()) task += "<" + type_name(ttype) + ">"; return task + " " + normalize_name(prefix + tfunction->get_name()) + "Async(" + argument_list(tfunction->get_arglist()) + ")"; } string t_csharp_generator::argument_list(t_struct* tstruct) { string result = ""; const vector<t_field*>& fields = tstruct->get_members(); vector<t_field*>::const_iterator f_iter; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; } else { result += ", "; } result += type_name((*f_iter)->get_type()) + " " + normalize_name((*f_iter)->get_name()); } return result; } string t_csharp_generator::type_to_enum(t_type* type) { while (type->is_typedef()) { type = ((t_typedef*)type)->get_type(); } if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "NO T_VOID CONSTRUCT"; case t_base_type::TYPE_STRING: return "TType.String"; case t_base_type::TYPE_BOOL: return "TType.Bool"; case t_base_type::TYPE_BYTE: return "TType.Byte"; case t_base_type::TYPE_I16: return "TType.I16"; case t_base_type::TYPE_I32: return "TType.I32"; case t_base_type::TYPE_I64: return "TType.I64"; case t_base_type::TYPE_DOUBLE: return "TType.Double"; } } else if (type->is_enum()) { return "TType.I32"; } else if (type->is_struct() || type->is_xception()) { return "TType.Struct"; } else if (type->is_map()) { return "TType.Map"; } else if (type->is_set()) { return "TType.Set"; } else if (type->is_list()) { return "TType.List"; } throw "INVALID TYPE IN type_to_enum: " + type->get_name(); } void t_csharp_generator::generate_csharp_docstring_comment(ofstream& out, string contents) { generate_docstring_comment(out, "/// <summary>\n", "/// ", contents, "/// </summary>\n"); } void t_csharp_generator::generate_csharp_doc(ofstream& out, t_field* field) { if (field->get_type()->is_enum()) { string combined_message = field->get_doc() + "\n<seealso cref=\"" + get_enum_class_name(field->get_type()) + "\"/>"; generate_csharp_docstring_comment(out, combined_message); } else { generate_csharp_doc(out, (t_doc*)field); } } void t_csharp_generator::generate_csharp_doc(ofstream& out, t_doc* tdoc) { if (tdoc->has_doc()) { generate_csharp_docstring_comment(out, tdoc->get_doc()); } } void t_csharp_generator::generate_csharp_doc(ofstream& out, t_function* tfunction) { if (tfunction->has_doc()) { stringstream ps; const vector<t_field*>& fields = tfunction->get_arglist()->get_members(); vector<t_field*>::const_iterator p_iter; for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) { t_field* p = *p_iter; ps << "\n<param name=\"" << p->get_name() << "\">"; if (p->has_doc()) { std::string str = p->get_doc(); str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); // remove the newlines that appear from the parser ps << str; } ps << "</param>"; } generate_docstring_comment(out, "", "/// ", "<summary>\n" + tfunction->get_doc() + "</summary>" + ps.str(), ""); } } std::string t_csharp_generator::get_enum_class_name(t_type* type) { string package = ""; t_program* program = type->get_program(); if (program != NULL && program != program_) { package = program->get_namespace("csharp") + "."; } return package + type->get_name(); } THRIFT_REGISTER_GENERATOR( csharp, "C#", " async: Adds Async support using Task.Run.\n" " asyncctp: Adds Async CTP support using TaskEx.Run.\n" " wcf: Adds bindings for WCF to generated classes.\n" " serial: Add serialization support to generated classes.\n" " nullable: Use nullable types for properties.\n" " hashcode: Generate a hashcode and equals implementation for classes.\n" " union: Use new union typing, which includes a static read function for union " "types.\n") 修改后: /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * * Contains some contributions under the Thrift Software License. * Please see doc/old-thrift-license.txt in the Thrift distribution for * details. */ #include <cassert> #include <string> #include <fstream> #include <iostream> #include <vector> #include <cctype> #include <stdlib.h> #include <sys/stat.h> #include <sstream> #include "platform.h" #include "t_oop_generator.h" using std::map; using std::ofstream; using std::ostringstream; using std::string; using std::stringstream; using std::vector; static const string endl = "\n"; // avoid ostream << std::endl flushes class t_csharp_generator : public t_oop_generator { public: t_csharp_generator(t_program* program, const std::map<std::string, std::string>& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void)option_string; std::map<std::string, std::string>::const_iterator iter; iter = parsed_options.find("async"); async_ = (iter != parsed_options.end()); iter = parsed_options.find("asyncctp"); async_ctp_ = (iter != parsed_options.end()); if (async_ && async_ctp_) { throw "argument error: Cannot specify both async and asyncctp; they are incompatible."; } iter = parsed_options.find("nullable"); nullable_ = (iter != parsed_options.end()); iter = parsed_options.find("hashcode"); hashcode_ = (iter != parsed_options.end()); iter = parsed_options.find("union"); union_ = (iter != parsed_options.end()); iter = parsed_options.find("serial"); serialize_ = (iter != parsed_options.end()); if (serialize_) { wcf_namespace_ = iter->second; // since there can be only one namespace } iter = parsed_options.find("wcf"); wcf_ = (iter != parsed_options.end()); if (wcf_) { wcf_namespace_ = iter->second; } out_dir_base_ = "gen-csharp"; } void init_generator(); void close_generator(); void generate_consts(std::vector<t_const*> consts); void generate_typedef(t_typedef* ttypedef); void generate_enum(t_enum* tenum); void generate_struct(t_struct* tstruct); void generate_union(t_struct* tunion); void generate_xception(t_struct* txception); void generate_service(t_service* tservice); void generate_property(ofstream& out, t_field* tfield, bool isPublic, bool generateIsset); void generate_csharp_property(ofstream& out, t_field* tfield, bool isPublic, bool includeIsset = true, std::string fieldPrefix = ""); bool print_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value, bool in_static, bool defval = false, bool needtype = false); std::string render_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value); void print_const_constructor(std::ofstream& out, std::vector<t_const*> consts); void print_const_def_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value); void generate_csharp_struct(t_struct* tstruct, bool is_exception); void generate_csharp_union(t_struct* tunion); void generate_csharp_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception = false, bool in_class = false, bool is_result = false); void generate_csharp_union_definition(std::ofstream& out, t_struct* tunion); void generate_csharp_union_class(std::ofstream& out, t_struct* tunion, t_field* tfield); void generate_csharp_wcffault(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_result_writer(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_tostring(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_equals(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_hashcode(std::ofstream& out, t_struct* tstruct); void generate_csharp_union_reader(std::ofstream& out, t_struct* tunion); void generate_function_helpers(t_function* tfunction); void generate_service_interface(t_service* tservice); void generate_service_helpers(t_service* tservice); void generate_service_client(t_service* tservice); void generate_service_server(t_service* tservice); void generate_process_function(t_service* tservice, t_function* function); void generate_deserialize_field(std::ofstream& out, t_field* tfield, std::string prefix = "", bool is_propertyless = false); void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); void generate_deserialize_list_element(std::ofstream& out, t_list* list, std::string prefix = ""); void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = "", bool is_element = false, bool is_propertyless = false); void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); void generate_serialize_map_element(std::ofstream& out, t_map* tmap, std::string iter, std::string map); void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); void generate_csharp_doc(std::ofstream& out, t_field* field); void generate_csharp_doc(std::ofstream& out, t_doc* tdoc); void generate_csharp_doc(std::ofstream& out, t_function* tdoc); void generate_csharp_docstring_comment(std::ofstream& out, string contents); void start_csharp_namespace(std::ofstream& out); void end_csharp_namespace(std::ofstream& out); std::string csharp_type_usings(); std::string csharp_thrift_usings(); std::string type_name(t_type* ttype, bool in_countainer = false, bool in_init = false, bool in_param = false, bool is_required = false); std::string base_type_name(t_base_type* tbase, bool in_container = false, bool in_param = false, bool is_required = false); std::string declare_field(t_field* tfield, bool init = false, std::string prefix = ""); std::string function_signature_async_begin(t_function* tfunction, std::string prefix = ""); std::string function_signature_async_end(t_function* tfunction, std::string prefix = ""); std::string function_signature_async(t_function* tfunction, std::string prefix = ""); std::string function_signature(t_function* tfunction, std::string prefix = ""); std::string argument_list(t_struct* tstruct); std::string type_to_enum(t_type* ttype); std::string prop_name(t_field* tfield, bool suppress_mapping = false); std::string get_enum_class_name(t_type* type); bool field_has_default(t_field* tfield) { return tfield->get_value() != NULL; } bool field_is_required(t_field* tfield) { return tfield->get_req() == t_field::T_REQUIRED; } bool type_can_be_null(t_type* ttype) { while (ttype->is_typedef()) { ttype = ((t_typedef*)ttype)->get_type(); } return ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string(); } private: std::string namespace_name_; std::ofstream f_service_; std::string namespace_dir_; bool async_; bool async_ctp_; bool nullable_; bool union_; bool hashcode_; bool serialize_; bool wcf_; std::string wcf_namespace_; std::map<std::string, int> csharp_keywords; void* member_mapping_scope; std::map<std::string, std::string> member_name_mapping; void init_keywords(); std::string normalize_name(std::string name); std::string make_valid_csharp_identifier(std::string const& fromName); void prepare_member_name_mapping(t_struct* tstruct); void prepare_member_name_mapping(void* scope, const vector<t_field*>& members, const string& structname); void cleanup_member_name_mapping(void* scope); string get_mapped_member_name(string oldname); }; void t_csharp_generator::init_generator() { MKDIR(get_out_dir().c_str()); namespace_name_ = program_->get_namespace("csharp"); string dir = namespace_name_; string subdir = get_out_dir().c_str(); string::size_type loc; while ((loc = dir.find(".")) != string::npos) { subdir = subdir + "/" + dir.substr(0, loc); MKDIR(subdir.c_str()); dir = dir.substr(loc + 1); } if (dir.size() > 0) { subdir = subdir + "/" + dir; MKDIR(subdir.c_str()); } namespace_dir_ = subdir; init_keywords(); member_mapping_scope = NULL; pverbose("C# options:\n"); pverbose("- async ...... %s\n", (async_ ? "ON" : "off")); pverbose("- async_ctp .. %s\n", (async_ctp_ ? "ON" : "off")); pverbose("- nullable ... %s\n", (nullable_ ? "ON" : "off")); pverbose("- union ...... %s\n", (union_ ? "ON" : "off")); pverbose("- hashcode ... %s\n", (hashcode_ ? "ON" : "off")); pverbose("- serialize .. %s\n", (serialize_ ? "ON" : "off")); pverbose("- wcf ........ %s\n", (wcf_ ? "ON" : "off")); } std::string t_csharp_generator::normalize_name(std::string name) { string tmp(name); std::transform(tmp.begin(), tmp.end(), tmp.begin(), static_cast<int (*)(int)>(std::tolower)); // un-conflict keywords by prefixing with "@" if (csharp_keywords.find(tmp) != csharp_keywords.end()) { return "@" + name; } // no changes necessary return name; } void t_csharp_generator::init_keywords() { csharp_keywords.clear(); // C# keywords csharp_keywords["abstract"] = 1; csharp_keywords["as"] = 1; csharp_keywords["base"] = 1; csharp_keywords["bool"] = 1; csharp_keywords["break"] = 1; csharp_keywords["byte"] = 1; csharp_keywords["case"] = 1; csharp_keywords["catch"] = 1; csharp_keywords["char"] = 1; csharp_keywords["checked"] = 1; csharp_keywords["class"] = 1; csharp_keywords["const"] = 1; csharp_keywords["continue"] = 1; csharp_keywords["decimal"] = 1; csharp_keywords["default"] = 1; csharp_keywords["delegate"] = 1; csharp_keywords["do"] = 1; csharp_keywords["double"] = 1; csharp_keywords["else"] = 1; csharp_keywords["enum"] = 1; csharp_keywords["event"] = 1; csharp_keywords["explicit"] = 1; csharp_keywords["extern"] = 1; csharp_keywords["false"] = 1; csharp_keywords["finally"] = 1; csharp_keywords["fixed"] = 1; csharp_keywords["float"] = 1; csharp_keywords["for"] = 1; csharp_keywords["foreach"] = 1; csharp_keywords["goto"] = 1; csharp_keywords["if"] = 1; csharp_keywords["implicit"] = 1; csharp_keywords["in"] = 1; csharp_keywords["int"] = 1; csharp_keywords["interface"] = 1; csharp_keywords["internal"] = 1; csharp_keywords["is"] = 1; csharp_keywords["lock"] = 1; csharp_keywords["long"] = 1; csharp_keywords["namespace"] = 1; csharp_keywords["new"] = 1; csharp_keywords["null"] = 1; csharp_keywords["object"] = 1; csharp_keywords["operator"] = 1; csharp_keywords["out"] = 1; csharp_keywords["override"] = 1; csharp_keywords["params"] = 1; csharp_keywords["private"] = 1; csharp_keywords["protected"] = 1; csharp_keywords["public"] = 1; csharp_keywords["readonly"] = 1; csharp_keywords["ref"] = 1; csharp_keywords["return"] = 1; csharp_keywords["sbyte"] = 1; csharp_keywords["sealed"] = 1; csharp_keywords["short"] = 1; csharp_keywords["sizeof"] = 1; csharp_keywords["stackalloc"] = 1; csharp_keywords["static"] = 1; csharp_keywords["string"] = 1; csharp_keywords["struct"] = 1; csharp_keywords["switch"] = 1; csharp_keywords["this"] = 1; csharp_keywords["throw"] = 1; csharp_keywords["true"] = 1; csharp_keywords["try"] = 1; csharp_keywords["typeof"] = 1; csharp_keywords["uint"] = 1; csharp_keywords["ulong"] = 1; csharp_keywords["unchecked"] = 1; csharp_keywords["unsafe"] = 1; csharp_keywords["ushort"] = 1; csharp_keywords["using"] = 1; csharp_keywords["virtual"] = 1; csharp_keywords["void"] = 1; csharp_keywords["volatile"] = 1; csharp_keywords["while"] = 1; // C# contextual keywords csharp_keywords["add"] = 1; csharp_keywords["alias"] = 1; csharp_keywords["ascending"] = 1; csharp_keywords["async"] = 1; csharp_keywords["await"] = 1; csharp_keywords["descending"] = 1; csharp_keywords["dynamic"] = 1; csharp_keywords["from"] = 1; csharp_keywords["get"] = 1; csharp_keywords["global"] = 1; csharp_keywords["group"] = 1; csharp_keywords["into"] = 1; csharp_keywords["join"] = 1; csharp_keywords["let"] = 1; csharp_keywords["orderby"] = 1; csharp_keywords["partial"] = 1; csharp_keywords["remove"] = 1; csharp_keywords["select"] = 1; csharp_keywords["set"] = 1; csharp_keywords["value"] = 1; csharp_keywords["var"] = 1; csharp_keywords["where"] = 1; csharp_keywords["yield"] = 1; } void t_csharp_generator::start_csharp_namespace(ofstream& out) { if (!namespace_name_.empty()) { out << "namespace " << namespace_name_ << "\n"; scope_up(out); } } void t_csharp_generator::end_csharp_namespace(ofstream& out) { if (!namespace_name_.empty()) { scope_down(out); } } string t_csharp_generator::csharp_type_usings() { return string() + "using System;\n" + "using System.Collections;\n" + "using System.Collections.Generic;\n" + "using System.Text;\n" + "using System.IO;\n" + ((async_ || async_ctp_) ? "using System.Threading.Tasks;\n" : "") + "using Thrift;\n" + "using Thrift.Collections;\n" + ((serialize_ || wcf_) ? "#if !SILVERLIGHT\n" : "") + ((serialize_ || wcf_) ? "using System.Xml.Serialization;\n" : "") + ((serialize_ || wcf_) ? "#endif\n" : "") + (wcf_ ? "//using System.ServiceModel;\n" : "") + "using System.Runtime.Serialization;\n"; } string t_csharp_generator::csharp_thrift_usings() { return string() + "using Thrift.Protocol;\n" + "using Thrift.Transport;\n"; } void t_csharp_generator::close_generator() { } void t_csharp_generator::generate_typedef(t_typedef* ttypedef) { (void)ttypedef; } void t_csharp_generator::generate_enum(t_enum* tenum) { string f_enum_name = namespace_dir_ + "/" + (tenum->get_name()) + ".cs"; ofstream f_enum; f_enum.open(f_enum_name.c_str()); f_enum << autogen_comment() << endl; start_csharp_namespace(f_enum); generate_csharp_doc(f_enum, tenum); indent(f_enum) << "public enum " << tenum->get_name() << "\n"; scope_up(f_enum); vector<t_enum_value*> constants = tenum->get_constants(); vector<t_enum_value*>::iterator c_iter; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { generate_csharp_doc(f_enum, *c_iter); int value = (*c_iter)->get_value(); indent(f_enum) << (*c_iter)->get_name() << " = " << value << "," << endl; } scope_down(f_enum); end_csharp_namespace(f_enum); f_enum.close(); } void t_csharp_generator::generate_consts(std::vector<t_const*> consts) { if (consts.empty()) { return; } string f_consts_name = namespace_dir_ + '/' + program_name_ + ".Constants.cs"; ofstream f_consts; f_consts.open(f_consts_name.c_str()); f_consts << autogen_comment() << csharp_type_usings() << endl; start_csharp_namespace(f_consts); indent(f_consts) << "public static class " << make_valid_csharp_identifier(program_name_) << "Constants" << endl; scope_up(f_consts); vector<t_const*>::iterator c_iter; bool need_static_constructor = false; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { generate_csharp_doc(f_consts, (*c_iter)); if (print_const_value(f_consts, (*c_iter)->get_name(), (*c_iter)->get_type(), (*c_iter)->get_value(), false)) { need_static_constructor = true; } } if (need_static_constructor) { print_const_constructor(f_consts, consts); } scope_down(f_consts); end_csharp_namespace(f_consts); f_consts.close(); } void t_csharp_generator::print_const_def_value(std::ofstream& out, string name, t_type* type, t_const_value* value) { if (type->is_struct() || type->is_xception()) { const vector<t_field*>& fields = ((t_struct*)type)->get_members(); vector<t_field*>::const_iterator f_iter; const map<t_const_value*, t_const_value*>& val = value->get_map(); map<t_const_value*, t_const_value*>::const_iterator v_iter; prepare_member_name_mapping((t_struct*)type); for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { t_field* field = NULL; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field = (*f_iter); } } if (field == NULL) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } t_type* field_type = field->get_type(); string val = render_const_value(out, name, field_type, v_iter->second); indent(out) << name << "." << prop_name(field) << " = " << val << ";" << endl; } cleanup_member_name_mapping((t_struct*)type); } else if (type->is_map()) { t_type* ktype = ((t_map*)type)->get_key_type(); t_type* vtype = ((t_map*)type)->get_val_type(); const map<t_const_value*, t_const_value*>& val = value->get_map(); map<t_const_value*, t_const_value*>::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { string key = render_const_value(out, name, ktype, v_iter->first); string val = render_const_value(out, name, vtype, v_iter->second); indent(out) << name << "[" << key << "]" << " = " << val << ";" << endl; } } else if (type->is_list() || type->is_set()) { t_type* etype; if (type->is_list()) { etype = ((t_list*)type)->get_elem_type(); } else { etype = ((t_set*)type)->get_elem_type(); } const vector<t_const_value*>& val = value->get_list(); vector<t_const_value*>::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { string val = render_const_value(out, name, etype, *v_iter); indent(out) << name << ".Add(" << val << ");" << endl; } } } void t_csharp_generator::print_const_constructor(std::ofstream& out, std::vector<t_const*> consts) { indent(out) << "static " << make_valid_csharp_identifier(program_name_).c_str() << "Constants()" << endl; scope_up(out); vector<t_const*>::iterator c_iter; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { string name = (*c_iter)->get_name(); t_type* type = (*c_iter)->get_type(); t_const_value* value = (*c_iter)->get_value(); print_const_def_value(out, name, type, value); } scope_down(out); } // it seems like all that methods that call this are using in_static to be the opposite of what it // would imply bool t_csharp_generator::print_const_value(std::ofstream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval, bool needtype) { indent(out); bool need_static_construction = !in_static; while (type->is_typedef()) { type = ((t_typedef*)type)->get_type(); } if (!defval || needtype) { out << (in_static ? "" : type->is_base_type() ? "public const " : "public static ") << type_name(type) << " "; } if (type->is_base_type()) { string v2 = render_const_value(out, name, type, value); out << name << " = " << v2 << ";" << endl; need_static_construction = false; } else if (type->is_enum()) { out << name << " = " << type_name(type, false, true) << "." << value->get_identifier_name() << ";" << endl; need_static_construction = false; } else if (type->is_struct() || type->is_xception()) { out << name << " = new " << type_name(type) << "();" << endl; } else if (type->is_map()) { out << name << " = new " << type_name(type, true, true) << "();" << endl; } else if (type->is_list() || type->is_set()) { out << name << " = new " << type_name(type) << "();" << endl; } if (defval && !type->is_base_type() && !type->is_enum()) { print_const_def_value(out, name, type, value); } return need_static_construction; } std::string t_csharp_generator::render_const_value(ofstream& out, string name, t_type* type, t_const_value* value) { (void)name; std::ostringstream render; if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_STRING: render << '"' << get_escaped_string(value) << '"'; break; case t_base_type::TYPE_BOOL: render << ((value->get_integer() > 0) ? "true" : "false"); break; case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: case t_base_type::TYPE_I64: render << value->get_integer(); break; case t_base_type::TYPE_DOUBLE: if (value->get_type() == t_const_value::CV_INTEGER) { render << value->get_integer(); } else { render << value->get_double(); } break; default: throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); } } else if (type->is_enum()) { render << type->get_name() << "." << value->get_identifier_name(); } else { string t = tmp("tmp"); print_const_value(out, t, type, value, true, true, true); render << t; } return render.str(); } void t_csharp_generator::generate_struct(t_struct* tstruct) { if (union_ && tstruct->is_union()) { generate_csharp_union(tstruct); } else { generate_csharp_struct(tstruct, false); } } void t_csharp_generator::generate_xception(t_struct* txception) { generate_csharp_struct(txception, true); } void t_csharp_generator::generate_csharp_struct(t_struct* tstruct, bool is_exception) { string f_struct_name = namespace_dir_ + "/" + (tstruct->get_name()) + ".cs"; ofstream f_struct; f_struct.open(f_struct_name.c_str()); f_struct << autogen_comment() << csharp_type_usings() << csharp_thrift_usings() << endl; generate_csharp_struct_definition(f_struct, tstruct, is_exception); f_struct.close(); } void t_csharp_generator::generate_csharp_struct_definition(ofstream& out, t_struct* tstruct, bool is_exception, bool in_class, bool is_result) { if (!in_class) { start_csharp_namespace(out); } out << endl; generate_csharp_doc(out, tstruct); prepare_member_name_mapping(tstruct); indent(out) << "#if !SILVERLIGHT" << endl; indent(out) << "[Serializable]" << endl; indent(out) << "#endif" << endl; if ((serialize_ || wcf_) && !is_exception) { indent(out) << "[DataContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl; // do not make exception classes directly WCF serializable, we provide a // separate "fault" for that } bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); indent(out) << "public " << (is_final ? "sealed " : "") << "partial class " << normalize_name(tstruct->get_name()) << " : "; if (is_exception) { out << "TException, "; } out << "TBase"; out << endl; scope_up(out); const vector<t_field*>& members = tstruct->get_members(); vector<t_field*>::const_iterator m_iter; // make private members with public Properties for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { // if the field is requied, then we use auto-properties if (!field_is_required((*m_iter)) && (!nullable_ || field_has_default((*m_iter)))) { indent(out) << "private " << declare_field(*m_iter, false, "_") << endl; } } out << endl; bool has_non_required_fields = false; bool has_non_required_default_value_fields = false; bool has_required_fields = false; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { generate_csharp_doc(out, *m_iter); generate_property(out, *m_iter, true, true); bool is_required = field_is_required((*m_iter)); bool has_default = field_has_default((*m_iter)); if (is_required) { has_required_fields = true; } else { if (has_default) { has_non_required_default_value_fields = true; } has_non_required_fields = true; } } bool generate_isset = (nullable_ && has_non_required_default_value_fields) || (!nullable_ && has_non_required_fields); if (generate_isset) { out << endl; if (serialize_ || wcf_) { out << indent() << "[XmlIgnore] // XmlSerializer" << endl << indent() << "[DataMember(Order = 1)] // XmlObjectSerializer, DataContractJsonSerializer, etc." << endl; } out << indent() << "public Isset __isset;" << endl << indent() << "#if !SILVERLIGHT" << endl << indent() << "[Serializable]" << endl << indent() << "#endif" << endl; if (serialize_ || wcf_) { indent(out) << "[DataContract]" << endl; } indent(out) << "public struct Isset {" << endl; indent_up(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { bool is_required = field_is_required((*m_iter)); bool has_default = field_has_default((*m_iter)); // if it is required, don't need Isset for that variable // if it is not required, if it has a default value, we need to generate Isset // if we are not nullable, then we generate Isset if (!is_required && (!nullable_ || has_default)) { if (serialize_ || wcf_) { indent(out) << "[DataMember]" << endl; } indent(out) << "public bool " << normalize_name((*m_iter)->get_name()) << ";" << endl; } } indent_down(); indent(out) << "}" << endl << endl; if (generate_isset && (serialize_ || wcf_)) { indent(out) << "#region XmlSerializer support" << endl << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { bool is_required = field_is_required((*m_iter)); bool has_default = field_has_default((*m_iter)); // if it is required, don't need Isset for that variable // if it is not required, if it has a default value, we need to generate Isset // if we are not nullable, then we generate Isset if (!is_required && (!nullable_ || has_default)) { indent(out) << "public bool ShouldSerialize" << prop_name((*m_iter)) << "()" << endl; indent(out) << "{" << endl; indent_up(); indent(out) << "return __isset." << normalize_name((*m_iter)->get_name()) << ";" << endl; indent_down(); indent(out) << "}" << endl << endl; } } indent(out) << "#endregion XmlSerializer support" << endl << endl; } } // We always want a default, no argument constructor for Reading indent(out) << "public " << normalize_name(tstruct->get_name()) << "() {" << endl; indent_up(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = (*m_iter)->get_type(); while (t->is_typedef()) { t = ((t_typedef*)t)->get_type(); } if ((*m_iter)->get_value() != NULL) { if (field_is_required((*m_iter))) { print_const_value(out, "this." + prop_name(*m_iter), t, (*m_iter)->get_value(), true, true); } else { print_const_value(out, "this._" + (*m_iter)->get_name(), t, (*m_iter)->get_value(), true, true); // Optionals with defaults are marked set indent(out) << "this.__isset." << normalize_name((*m_iter)->get_name()) << " = true;" << endl; } } } indent_down(); indent(out) << "}" << endl << endl; if (has_required_fields) { indent(out) << "public " << tstruct->get_name() << "("; bool first = true; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { if (field_is_required((*m_iter))) { if (first) { first = false; } else { out << ", "; } out << type_name((*m_iter)->get_type()) << " " << (*m_iter)->get_name(); } } out << ") : this() {" << endl; indent_up(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { if (field_is_required((*m_iter))) { indent(out) << "this." << prop_name((*m_iter)) << " = " << (*m_iter)->get_name() << ";" << endl; } } indent_down(); indent(out) << "}" << endl << endl; } generate_csharp_struct_reader(out, tstruct); if (is_result) { generate_csharp_struct_result_writer(out, tstruct); } else { generate_csharp_struct_writer(out, tstruct); } if (hashcode_) { generate_csharp_struct_equals(out, tstruct); generate_csharp_struct_hashcode(out, tstruct); } generate_csharp_struct_tostring(out, tstruct); scope_down(out); out << endl; // generate a corresponding WCF fault to wrap the exception if ((serialize_ || wcf_) && is_exception) { generate_csharp_wcffault(out, tstruct); } cleanup_member_name_mapping(tstruct); if (!in_class) { end_csharp_namespace(out); } } void t_csharp_generator::generate_csharp_wcffault(ofstream& out, t_struct* tstruct) { out << endl; indent(out) << "#if !SILVERLIGHT" << endl; indent(out) << "[Serializable]" << endl; indent(out) << "#endif" << endl; indent(out) << "[DataContract]" << endl; bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); indent(out) << "public " << (is_final ? "sealed " : "") << "partial class " << tstruct->get_name() << "Fault" << endl; scope_up(out); const vector<t_field*>& members = tstruct->get_members(); vector<t_field*>::const_iterator m_iter; // make private members with public Properties for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { indent(out) << "private " << declare_field(*m_iter, false, "_") << endl; } out << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { generate_property(out, *m_iter, true, false); } scope_down(out); out << endl; } void t_csharp_generator::generate_csharp_struct_reader(ofstream& out, t_struct* tstruct) { indent(out) << "public void Read (TProtocol iprot)" << endl; scope_up(out); const vector<t_field*>& fields = tstruct->get_members(); vector<t_field*>::const_iterator f_iter; // Required variables aren't in __isset, so we need tmp vars to check them for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (field_is_required((*f_iter))) { indent(out) << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl; } } indent(out) << "TField field;" << endl << indent() << "iprot.ReadStructBegin();" << endl; indent(out) << "while (true)" << endl; scope_up(out); indent(out) << "field = iprot.ReadFieldBegin();" << endl; indent(out) << "if (field.Type == TType.Stop) { " << endl; indent_up(); indent(out) << "break;" << endl; indent_down(); indent(out) << "}" << endl; indent(out) << "switch (field.ID)" << endl; scope_up(out); for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { bool is_required = field_is_required((*f_iter)); indent(out) << "case " << (*f_iter)->get_key() << ":" << endl; indent_up(); indent(out) << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; indent_up(); generate_deserialize_field(out, *f_iter); if (is_required) { indent(out) << "isset_" << (*f_iter)->get_name() << " = true;" << endl; } indent_down(); out << indent() << "} else { " << endl << indent() << " TProtocolUtil.Skip(iprot, field.Type);" << endl << indent() << "}" << endl << indent() << "break;" << endl; indent_down(); } indent(out) << "default: " << endl; indent_up(); indent(out) << "TProtocolUtil.Skip(iprot, field.Type);" << endl; indent(out) << "break;" << endl; indent_down(); scope_down(out); indent(out) << "iprot.ReadFieldEnd();" << endl; scope_down(out); indent(out) << "iprot.ReadStructEnd();" << endl; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (field_is_required((*f_iter))) { indent(out) << "if (!isset_" << (*f_iter)->get_name() << ")" << endl; indent_up(); indent(out) << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl; indent_down(); } } indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_writer(ofstream& out, t_struct* tstruct) { out << indent() << "public void Write(TProtocol oprot) {" << endl; indent_up(); string name = tstruct->get_name(); const vector<t_field*>& fields = tstruct->get_sorted_members(); vector<t_field*>::const_iterator f_iter; indent(out) << "TStruct struc = new TStruct(\"" << name << "\");" << endl; indent(out) << "oprot.WriteStructBegin(struc);" << endl; if (fields.size() > 0) { indent(out) << "TField field = new TField();" << endl; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { bool is_required = field_is_required((*f_iter)); bool has_default = field_has_default((*f_iter)); if (nullable_ && !has_default && !is_required) { indent(out) << "if (" << prop_name((*f_iter)) << " != null) {" << endl; indent_up(); } else if (!is_required) { bool null_allowed = type_can_be_null((*f_iter)->get_type()); if (null_allowed) { indent(out) << "if (" << prop_name((*f_iter)) << " != null && __isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; indent_up(); } else { indent(out) << "if (__isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; indent_up(); } } indent(out) << "field.Name = \"" << (*f_iter)->get_name() << "\";" << endl; indent(out) << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl; indent(out) << "field.ID = " << (*f_iter)->get_key() << ";" << endl; indent(out) << "oprot.WriteFieldBegin(field);" << endl; generate_serialize_field(out, *f_iter); indent(out) << "oprot.WriteFieldEnd();" << endl; if (!is_required) { indent_down(); indent(out) << "}" << endl; } } } indent(out) << "oprot.WriteFieldStop();" << endl; indent(out) << "oprot.WriteStructEnd();" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_result_writer(ofstream& out, t_struct* tstruct) { indent(out) << "public void Write(TProtocol oprot) {" << endl; indent_up(); string name = tstruct->get_name(); const vector<t_field*>& fields = tstruct->get_sorted_members(); vector<t_field*>::const_iterator f_iter; indent(out) << "TStruct struc = new TStruct(\"" << name << "\");" << endl; indent(out) << "oprot.WriteStructBegin(struc);" << endl; if (fields.size() > 0) { indent(out) << "TField field = new TField();" << endl; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; out << endl << indent() << "if "; } else { out << " else if "; } if (nullable_) { out << "(this." << prop_name((*f_iter)) << " != null) {" << endl; } else { out << "(this.__isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; } indent_up(); bool null_allowed = !nullable_ && type_can_be_null((*f_iter)->get_type()); if (null_allowed) { indent(out) << "if (" << prop_name(*f_iter) << " != null) {" << endl; indent_up(); } indent(out) << "field.Name = \"" << prop_name(*f_iter) << "\";" << endl; indent(out) << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl; indent(out) << "field.ID = " << (*f_iter)->get_key() << ";" << endl; indent(out) << "oprot.WriteFieldBegin(field);" << endl; generate_serialize_field(out, *f_iter); indent(out) << "oprot.WriteFieldEnd();" << endl; if (null_allowed) { indent_down(); indent(out) << "}" << endl; } indent_down(); indent(out) << "}"; } } out << endl << indent() << "oprot.WriteFieldStop();" << endl << indent() << "oprot.WriteStructEnd();" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_tostring(ofstream& out, t_struct* tstruct) { indent(out) << "public override string ToString() {" << endl; indent_up(); indent(out) << "StringBuilder __sb = new StringBuilder(\"" << tstruct->get_name() << "(\");" << endl; const vector<t_field*>& fields = tstruct->get_members(); vector<t_field*>::const_iterator f_iter; bool useFirstFlag = false; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (!field_is_required((*f_iter))) { indent(out) << "bool __first = true;" << endl; useFirstFlag = true; } break; } bool had_required = false; // set to true after first required field has been processed for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { bool is_required = field_is_required((*f_iter)); bool has_default = field_has_default((*f_iter)); if (nullable_ && !has_default && !is_required) { indent(out) << "if (" << prop_name((*f_iter)) << " != null) {" << endl; indent_up(); } else if (!is_required) { bool null_allowed = type_can_be_null((*f_iter)->get_type()); if (null_allowed) { indent(out) << "if (" << prop_name((*f_iter)) << " != null && __isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; indent_up(); } else { indent(out) << "if (__isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; indent_up(); } } if (useFirstFlag && (!had_required)) { indent(out) << "if(!__first) { __sb.Append(\", \"); }" << endl; if (!is_required) { indent(out) << "__first = false;" << endl; } indent(out) << "__sb.Append(\"" << prop_name((*f_iter)) << ": \");" << endl; } else { indent(out) << "__sb.Append(\", " << prop_name((*f_iter)) << ": \");" << endl; } t_type* ttype = (*f_iter)->get_type(); if (ttype->is_xception() || ttype->is_struct()) { indent(out) << "__sb.Append(" << prop_name((*f_iter)) << "== null ? \"<null>\" : " << prop_name((*f_iter)) << ".ToString());" << endl; } else { indent(out) << "__sb.Append(" << prop_name((*f_iter)) << ");" << endl; } if (!is_required) { indent_down(); indent(out) << "}" << endl; } else { had_required = true; // now __first must be false, so we don't need to check it anymore } } indent(out) << "__sb.Append(\")\");" << endl; indent(out) << "return __sb.ToString();" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_union(t_struct* tunion) { string f_union_name = namespace_dir_ + "/" + (tunion->get_name()) + ".cs"; ofstream f_union; f_union.open(f_union_name.c_str()); f_union << autogen_comment() << csharp_type_usings() << csharp_thrift_usings() << endl; generate_csharp_union_definition(f_union, tunion); f_union.close(); } void t_csharp_generator::generate_csharp_union_definition(std::ofstream& out, t_struct* tunion) { // Let's define the class first start_csharp_namespace(out); indent(out) << "public abstract partial class " << tunion->get_name() << " : TAbstractBase {" << endl; indent_up(); indent(out) << "public abstract void Write(TProtocol protocol);" << endl; indent(out) << "public readonly bool Isset;" << endl; indent(out) << "public abstract object Data { get; }" << endl; indent(out) << "protected " << tunion->get_name() << "(bool isset) {" << endl; indent_up(); indent(out) << "Isset = isset;" << endl; indent_down(); indent(out) << "}" << endl << endl; indent(out) << "public class ___undefined : " << tunion->get_name() << " {" << endl; indent_up(); indent(out) << "public override object Data { get { return null; } }" << endl; indent(out) << "public ___undefined() : base(false) {}" << endl << endl; indent(out) << "public override void Write(TProtocol protocol) {" << endl; indent_up(); indent(out) << "throw new TProtocolException( TProtocolException.INVALID_DATA, \"Cannot persist " "an union type which is not set.\");" << endl; indent_down(); indent(out) << "}" << endl << endl; indent_down(); indent(out) << "}" << endl << endl; const vector<t_field*>& fields = tunion->get_members(); vector<t_field*>::const_iterator f_iter; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { generate_csharp_union_class(out, tunion, (*f_iter)); } generate_csharp_union_reader(out, tunion); indent_down(); indent(out) << "}" << endl << endl; end_csharp_namespace(out); } void t_csharp_generator::generate_csharp_union_class(std::ofstream& out, t_struct* tunion, t_field* tfield) { indent(out) << "public class " << tfield->get_name() << " : " << tunion->get_name() << " {" << endl; indent_up(); indent(out) << "private " << type_name(tfield->get_type()) << " _data;" << endl; indent(out) << "public override object Data { get { return _data; } }" << endl; indent(out) << "public " << tfield->get_name() << "(" << type_name(tfield->get_type()) << " data) : base(true) {" << endl; indent_up(); indent(out) << "this._data = data;" << endl; indent_down(); indent(out) << "}" << endl; indent(out) << "public override void Write(TProtocol oprot) {" << endl; indent_up(); indent(out) << "TStruct struc = new TStruct(\"" << tunion->get_name() << "\");" << endl; indent(out) << "oprot.WriteStructBegin(struc);" << endl; indent(out) << "TField field = new TField();" << endl; indent(out) << "field.Name = \"" << tfield->get_name() << "\";" << endl; indent(out) << "field.Type = " << type_to_enum(tfield->get_type()) << ";" << endl; indent(out) << "field.ID = " << tfield->get_key() << ";" << endl; indent(out) << "oprot.WriteFieldBegin(field);" << endl; generate_serialize_field(out, tfield, "_data", true, true); indent(out) << "oprot.WriteFieldEnd();" << endl; indent(out) << "oprot.WriteFieldStop();" << endl; indent(out) << "oprot.WriteStructEnd();" << endl; indent_down(); indent(out) << "}" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_equals(ofstream& out, t_struct* tstruct) { indent(out) << "public override bool Equals(object that) {" << endl; indent_up(); indent(out) << "var other = that as " << type_name(tstruct) << ";" << endl; indent(out) << "if (other == null) return false;" << endl; indent(out) << "if (ReferenceEquals(this, other)) return true;" << endl; const vector<t_field*>& fields = tstruct->get_members(); vector<t_field*>::const_iterator f_iter; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; indent(out) << "return "; indent_up(); } else { out << endl; indent(out) << "&& "; } if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter)))) { out << "((__isset." << normalize_name((*f_iter)->get_name()) << " == other.__isset." << normalize_name((*f_iter)->get_name()) << ") && ((!__isset." << normalize_name((*f_iter)->get_name()) << ") || ("; } t_type* ttype = (*f_iter)->get_type(); if (ttype->is_container()) { out << "TCollections.Equals("; } else { out << "System.Object.Equals("; } out << prop_name((*f_iter)) << ", other." << prop_name((*f_iter)) << ")"; if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter)))) { out << ")))"; } } if (first) { indent(out) << "return true;" << endl; } else { out << ";" << endl; indent_down(); } indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_hashcode(ofstream& out, t_struct* tstruct) { indent(out) << "public override int GetHashCode() {" << endl; indent_up(); indent(out) << "int hashcode = 0;" << endl; indent(out) << "unchecked {" << endl; indent_up(); const vector<t_field*>& fields = tstruct->get_members(); vector<t_field*>::const_iterator f_iter; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { t_type* ttype = (*f_iter)->get_type(); indent(out) << "hashcode = (hashcode * 397) ^ "; if (field_is_required((*f_iter))) { out << "("; } else if (nullable_) { out << "(" << prop_name((*f_iter)) << " == null ? 0 : "; } else { out << "(!__isset." << normalize_name((*f_iter)->get_name()) << " ? 0 : "; } if (ttype->is_container()) { out << "(TCollections.GetHashCode(" << prop_name((*f_iter)) << "))"; } else { out << "(" << prop_name((*f_iter)) << ".GetHashCode())"; } out << ");" << endl; } indent_down(); indent(out) << "}" << endl; indent(out) << "return hashcode;" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_service(t_service* tservice) { string f_service_name = namespace_dir_ + "/" + service_name_ + ".cs"; f_service_.open(f_service_name.c_str()); f_service_ << autogen_comment() << csharp_type_usings() << csharp_thrift_usings() << endl; start_csharp_namespace(f_service_); indent(f_service_) << "public partial class " << normalize_name(service_name_) << " {" << endl; indent_up(); generate_service_interface(tservice); generate_service_client(tservice); generate_service_server(tservice); generate_service_helpers(tservice); indent_down(); indent(f_service_) << "}" << endl; end_csharp_namespace(f_service_); f_service_.close(); } void t_csharp_generator::generate_service_interface(t_service* tservice) { string extends = ""; string extends_iface = ""; if (tservice->get_extends() != NULL) { extends = type_name(tservice->get_extends()); extends_iface = " : " + extends + ".Iface"; } generate_csharp_doc(f_service_, tservice); if (wcf_) { indent(f_service_) << "[ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl; } indent(f_service_) << "public interface Iface" << extends_iface << " {" << endl; indent_up(); vector<t_function*> functions = tservice->get_functions(); vector<t_function*>::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { generate_csharp_doc(f_service_, *f_iter); // if we're using WCF, add the corresponding attributes if (wcf_) { indent(f_service_) << "[OperationContract]" << endl; const std::vector<t_field*>& xceptions = (*f_iter)->get_xceptions()->get_members(); vector<t_field*>::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { indent(f_service_) << "[FaultContract(typeof(" + type_name((*x_iter)->get_type(), false, false) + "Fault))]" << endl; } } indent(f_service_) << function_signature(*f_iter) << ";" << endl; if (!async_) { indent(f_service_) << "#if SILVERLIGHT" << endl; } indent(f_service_) << function_signature_async_begin(*f_iter, "Begin_") << ";" << endl; indent(f_service_) << function_signature_async_end(*f_iter, "End_") << ";" << endl; if (async_ || async_ctp_) { indent(f_service_) << function_signature_async(*f_iter) << ";" << endl; } if (!async_) { indent(f_service_) << "#endif" << endl; } } indent_down(); f_service_ << indent() << "}" << endl << endl; } void t_csharp_generator::generate_service_helpers(t_service* tservice) { vector<t_function*> functions = tservice->get_functions(); vector<t_function*>::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_struct* ts = (*f_iter)->get_arglist(); generate_csharp_struct_definition(f_service_, ts, false, true); generate_function_helpers(*f_iter); } } void t_csharp_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; if (tservice->get_extends() != NULL) { extends = type_name(tservice->get_extends()); extends_client = extends + ".Client, "; } else { extends_client = "IDisposable, "; } generate_csharp_doc(f_service_, tservice); indent(f_service_) << "public class Client : " << extends_client << "Iface {" << endl; indent_up(); indent(f_service_) << "public Client(TProtocol prot) : this(prot, prot)" << endl; scope_up(f_service_); scope_down(f_service_); f_service_ << endl; indent(f_service_) << "public Client(TProtocol iprot, TProtocol oprot)"; if (!extends.empty()) { f_service_ << " : base(iprot, oprot)"; } f_service_ << endl; scope_up(f_service_); if (extends.empty()) { f_service_ << indent() << "iprot_ = iprot;" << endl << indent() << "oprot_ = oprot;" << endl; } scope_down(f_service_); f_service_ << endl; if (extends.empty()) { f_service_ << indent() << "protected TProtocol iprot_;" << endl << indent() << "protected TProtocol oprot_;" << endl << indent() << "protected int seqid_;" << endl << endl; f_service_ << indent() << "public TProtocol InputProtocol" << endl; scope_up(f_service_); indent(f_service_) << "get { return iprot_; }" << endl; scope_down(f_service_); f_service_ << indent() << "public TProtocol OutputProtocol" << endl; scope_up(f_service_); indent(f_service_) << "get { return oprot_; }" << endl; scope_down(f_service_); f_service_ << endl << endl; indent(f_service_) << "#region \" IDisposable Support \"" << endl; indent(f_service_) << "private bool _IsDisposed;" << endl << endl; indent(f_service_) << "// IDisposable" << endl; indent(f_service_) << "public void Dispose()" << endl; scope_up(f_service_); indent(f_service_) << "Dispose(true);" << endl; scope_down(f_service_); indent(f_service_) << endl << endl; indent(f_service_) << "protected virtual void Dispose(bool disposing)" << endl; scope_up(f_service_); indent(f_service_) << "if (!_IsDisposed)" << endl; scope_up(f_service_); indent(f_service_) << "if (disposing)" << endl; scope_up(f_service_); indent(f_service_) << "if (iprot_ != null)" << endl; scope_up(f_service_); indent(f_service_) << "((IDisposable)iprot_).Dispose();" << endl; scope_down(f_service_); indent(f_service_) << "if (oprot_ != null)" << endl; scope_up(f_service_); indent(f_service_) << "((IDisposable)oprot_).Dispose();" << endl; scope_down(f_service_); scope_down(f_service_); scope_down(f_service_); indent(f_service_) << "_IsDisposed = true;" << endl; scope_down(f_service_); indent(f_service_) << "#endregion" << endl; f_service_ << endl << endl; } vector<t_function*> functions = tservice->get_functions(); vector<t_function*>::const_iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { string funname = (*f_iter)->get_name(); indent(f_service_) << endl; if (!async_) { indent(f_service_) << "#if SILVERLIGHT" << endl; } // Begin_ indent(f_service_) << "public " << function_signature_async_begin(*f_iter, "Begin_") << endl; scope_up(f_service_); indent(f_service_) << "return " << "send_" << funname << "(callback, state"; t_struct* arg_struct = (*f_iter)->get_arglist(); prepare_member_name_mapping(arg_struct); const vector<t_field*>& fields = arg_struct->get_members(); vector<t_field*>::const_iterator fld_iter; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { f_service_ << ", "; f_service_ << normalize_name((*fld_iter)->get_name()); } f_service_ << ");" << endl; scope_down(f_service_); f_service_ << endl; // End indent(f_service_) << "public " << function_signature_async_end(*f_iter, "End_") << endl; scope_up(f_service_); indent(f_service_) << "oprot_.Transport.EndFlush(asyncResult);" << endl; if (!(*f_iter)->is_oneway()) { f_service_ << indent(); if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << "return "; } f_service_ << "recv_" << funname << "();" << endl; } scope_down(f_service_); f_service_ << endl; // async bool first; if (async_ || async_ctp_) { indent(f_service_) << "public async " << function_signature_async(*f_iter, "") << endl; scope_up(f_service_); if (!(*f_iter)->get_returntype()->is_void()) { indent(f_service_) << type_name((*f_iter)->get_returntype()) << " retval;" << endl; indent(f_service_) << "retval = "; } else { indent(f_service_); } if (async_) { f_service_ << "await Task.Run(() =>" << endl; } else { f_service_ << "await TaskEx.Run(() =>" << endl; } scope_up(f_service_); indent(f_service_); if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << "return "; } f_service_ << funname << "("; first = true; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { if (first) { first = false; } else { f_service_ << ", "; } f_service_ << (*fld_iter)->get_name(); } f_service_ << ");" << endl; indent_down(); indent(f_service_) << "});" << endl; if (!(*f_iter)->get_returntype()->is_void()) { indent(f_service_) << "return retval;" << endl; } scope_down(f_service_); f_service_ << endl; } if (!async_) { indent(f_service_) << "#endif" << endl << endl; } // "Normal" Synchronous invoke generate_csharp_doc(f_service_, *f_iter); indent(f_service_) << "public " << function_signature(*f_iter) << endl; scope_up(f_service_); if (!async_) { indent(f_service_) << "#if !SILVERLIGHT" << endl; indent(f_service_) << "send_" << funname << "("; first = true; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { if (first) { first = false; } else { f_service_ << ", "; } f_service_ << normalize_name((*fld_iter)->get_name()); } f_service_ << ");" << endl; if (!(*f_iter)->is_oneway()) { f_service_ << indent(); if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << "return "; } f_service_ << "recv_" << funname << "();" << endl; } f_service_ << endl; indent(f_service_) << "#else" << endl; } // Silverlight synchronous invoke indent(f_service_) << "var asyncResult = Begin_" << funname << "(null, null"; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { f_service_ << ", " << normalize_name((*fld_iter)->get_name()); } f_service_ << ");" << endl; if (!(*f_iter)->is_oneway()) { f_service_ << indent(); if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << "return "; } f_service_ << "End_" << funname << "(asyncResult);" << endl; } f_service_ << endl; if (!async_) { indent(f_service_) << "#endif" << endl; } scope_down(f_service_); // Send t_function send_function(g_type_void, string("send_") + (*f_iter)->get_name(), (*f_iter)->get_arglist()); string argsname = (*f_iter)->get_name() + "_args"; if (!async_) { indent(f_service_) << "#if SILVERLIGHT" << endl; } indent(f_service_) << "public " << function_signature_async_begin(&send_function) << endl; if (!async_) { indent(f_service_) << "#else" << endl; indent(f_service_) << "public " << function_signature(&send_function) << endl; indent(f_service_) << "#endif" << endl; } scope_up(f_service_); f_service_ << indent() << "oprot_.WriteMessageBegin(new TMessage(\"" << funname << "\", " << ((*f_iter)->is_oneway() ? "TMessageType.Oneway" : "TMessageType.Call") << ", seqid_));" << endl << indent() << argsname << " args = new " << argsname << "();" << endl; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { f_service_ << indent() << "args." << prop_name(*fld_iter) << " = " << normalize_name((*fld_iter)->get_name()) << ";" << endl; } f_service_ << indent() << "args.Write(oprot_);" << endl << indent() << "oprot_.WriteMessageEnd();" << endl; ; if (!async_) { indent(f_service_) << "#if SILVERLIGHT" << endl; } indent(f_service_) << "return oprot_.Transport.BeginFlush(callback, state);" << endl; if (!async_) { indent(f_service_) << "#else" << endl; indent(f_service_) << "oprot_.Transport.Flush();" << endl; indent(f_service_) << "#endif" << endl; } cleanup_member_name_mapping(arg_struct); scope_down(f_service_); f_service_ << endl; if (!(*f_iter)->is_oneway()) { string resultname = (*f_iter)->get_name() + "_result"; t_struct noargs(program_); t_function recv_function((*f_iter)->get_returntype(), string("recv_") + (*f_iter)->get_name(), &noargs, (*f_iter)->get_xceptions()); indent(f_service_) << "public " << function_signature(&recv_function) << endl; scope_up(f_service_); prepare_member_name_mapping((*f_iter)->get_xceptions()); f_service_ << indent() << "TMessage msg = iprot_.ReadMessageBegin();" << endl << indent() << "if (msg.Type == TMessageType.Exception) {" << endl; indent_up(); f_service_ << indent() << "TApplicationException x = TApplicationException.Read(iprot_);" << endl << indent() << "iprot_.ReadMessageEnd();" << endl << indent() << "throw x;" << endl; indent_down(); f_service_ << indent() << "}" << endl << indent() << resultname << " result = new " << resultname << "();" << endl << indent() << "result.Read(iprot_);" << endl << indent() << "iprot_.ReadMessageEnd();" << endl; if (!(*f_iter)->get_returntype()->is_void()) { if (nullable_) { if (type_can_be_null((*f_iter)->get_returntype())) { f_service_ << indent() << "if (result.Success != null) {" << endl << indent() << " return result.Success;" << endl << indent() << "}" << endl; } else { f_service_ << indent() << "if (result.Success.HasValue) {" << endl << indent() << " return result.Success.Value;" << endl << indent() << "}" << endl; } } else { /*f_service_ << indent() << "if (result.__isset.success aaa) {" << endl << indent() << " return result.Success;" << endl << indent() << "}" << endl;*/ /*f_service_ << " return result.Success;" << endl;*/ f_service_ << indent() << "if (result.__isset.success ) {" << endl << indent() << " return result.Success;" << endl << indent() << "}" << endl; if ((*f_iter)->get_returntype()->is_bool()) { f_service_ << indent() << "return false;" << endl; } else if ((*f_iter)->get_returntype()->is_string()) { f_service_ << indent() << "return \"failed\";" << endl; } else if ((*f_iter)->get_returntype()->is_void()) { f_service_ << indent() << "return ;" << endl; } else if ((*f_iter)->get_returntype()->is_struct()) { f_service_ << indent() << "return null;" << endl; } } } t_struct* xs = (*f_iter)->get_xceptions(); const std::vector<t_field*>& xceptions = xs->get_members(); vector<t_field*>::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { if (nullable_) { f_service_ << indent() << "if (result." << prop_name(*x_iter) << " != null) {" << endl << indent() << " throw result." << prop_name(*x_iter) << ";" << endl << indent() << "}" << endl; } else { f_service_ << indent() << "if (result.__isset." << normalize_name((*x_iter)->get_name()) << ") {" << endl << indent() << " throw result." << prop_name(*x_iter) << ";" << endl << indent() << "}" << endl; } } if ((*f_iter)->get_returntype()->is_void()) { indent(f_service_) << "return;" << endl; } else { /*f_service_ << indent() << "throw new " "TApplicationException(TApplicationException.ExceptionType.MissingResult, \"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl;*/ } cleanup_member_name_mapping((*f_iter)->get_xceptions()); scope_down(f_service_); f_service_ << endl; } } indent_down(); indent(f_service_) << "}" << endl; } void t_csharp_generator::generate_service_server(t_service* tservice) { vector<t_function*> functions = tservice->get_functions(); vector<t_function*>::iterator f_iter; string extends = ""; string extends_processor = ""; if (tservice->get_extends() != NULL) { extends = type_name(tservice->get_extends()); extends_processor = extends + ".Processor, "; } indent(f_service_) << "public class Processor : " << extends_processor << "TProcessor {" << endl; indent_up(); indent(f_service_) << "public Processor(Iface iface)"; if (!extends.empty()) { f_service_ << " : base(iface)"; } f_service_ << endl; scope_up(f_service_); f_service_ << indent() << "iface_ = iface;" << endl; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_service_ << indent() << "processMap_[\"" << (*f_iter)->get_name() << "\"] = " << (*f_iter)->get_name() << "_Process;" << endl; } scope_down(f_service_); f_service_ << endl; if (extends.empty()) { f_service_ << indent() << "protected delegate void ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot);" << endl; } f_service_ << indent() << "private Iface iface_;" << endl; if (extends.empty()) { f_service_ << indent() << "protected Dictionary<string, ProcessFunction> processMap_ = new " "Dictionary<string, ProcessFunction>();" << endl; } f_service_ << endl; if (extends.empty()) { indent(f_service_) << "public bool Process(TProtocol iprot, TProtocol oprot)" << endl; } else { indent(f_service_) << "public new bool Process(TProtocol iprot, TProtocol oprot)" << endl; } scope_up(f_service_); f_service_ << indent() << "try" << endl; scope_up(f_service_); f_service_ << indent() << "TMessage msg = iprot.ReadMessageBegin();" << endl; f_service_ << indent() << "ProcessFunction fn;" << endl << indent() << "processMap_.TryGetValue(msg.Name, out fn);" << endl << indent() << "if (fn == null) {" << endl << indent() << " TProtocolUtil.Skip(iprot, TType.Struct);" << endl << indent() << " iprot.ReadMessageEnd();" << endl << indent() << " TApplicationException x = new TApplicationException " "(TApplicationException.ExceptionType.UnknownMethod, \"Invalid method name: '\" + " "msg.Name + \"'\");" << endl << indent() << " oprot.WriteMessageBegin(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID));" << endl << indent() << " x.Write(oprot);" << endl << indent() << " oprot.WriteMessageEnd();" << endl << indent() << " oprot.Transport.Flush();" << endl << indent() << " return true;" << endl << indent() << "}" << endl << indent() << "fn(msg.SeqID, iprot, oprot);" << endl; scope_down(f_service_); f_service_ << indent() << "catch (IOException)" << endl; scope_up(f_service_); f_service_ << indent() << "return false;" << endl; scope_down(f_service_); f_service_ << indent() << "return true;" << endl; scope_down(f_service_); f_service_ << endl; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { generate_process_function(tservice, *f_iter); } indent_down(); indent(f_service_) << "}" << endl << endl; } void t_csharp_generator::generate_function_helpers(t_function* tfunction) { if (tfunction->is_oneway()) { return; } t_struct result(program_, tfunction->get_name() + "_result"); t_field success(tfunction->get_returntype(), "success", 0); if (!tfunction->get_returntype()->is_void()) { result.append(&success); } t_struct* xs = tfunction->get_xceptions(); const vector<t_field*>& fields = xs->get_members(); vector<t_field*>::const_iterator f_iter; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { result.append(*f_iter); } generate_csharp_struct_definition(f_service_, &result, false, true, true); } void t_csharp_generator::generate_process_function(t_service* tservice, t_function* tfunction) { (void)tservice; indent(f_service_) << "public void " << tfunction->get_name() << "_Process(int seqid, TProtocol iprot, TProtocol oprot)" << endl; scope_up(f_service_); string argsname = tfunction->get_name() + "_args"; string resultname = tfunction->get_name() + "_result"; f_service_ << indent() << argsname << " args = new " << argsname << "();" << endl << indent() << "args.Read(iprot);" << endl << indent() << "iprot.ReadMessageEnd();" << endl; t_struct* xs = tfunction->get_xceptions(); const std::vector<t_field*>& xceptions = xs->get_members(); vector<t_field*>::const_iterator x_iter; if (!tfunction->is_oneway()) { f_service_ << indent() << resultname << " result = new " << resultname << "();" << endl; } if (xceptions.size() > 0) { f_service_ << indent() << "try {" << endl; indent_up(); } t_struct* arg_struct = tfunction->get_arglist(); const std::vector<t_field*>& fields = arg_struct->get_members(); vector<t_field*>::const_iterator f_iter; f_service_ << indent(); if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { f_service_ << "result.Success = "; } f_service_ << "iface_." << normalize_name(tfunction->get_name()) << "("; bool first = true; prepare_member_name_mapping(arg_struct); for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; } else { f_service_ << ", "; } f_service_ << "args." << prop_name(*f_iter); if (nullable_ && !type_can_be_null((*f_iter)->get_type())) { f_service_ << ".Value"; } } cleanup_member_name_mapping(arg_struct); f_service_ << ");" << endl; if (!tfunction->is_oneway() && xceptions.size() > 0) { indent_down(); f_service_ << indent() << "}"; prepare_member_name_mapping(xs); for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { f_service_ << " catch (" << type_name((*x_iter)->get_type(), false, false) << " " << (*x_iter)->get_name() << ") {" << endl; if (!tfunction->is_oneway()) { indent_up(); f_service_ << indent() << "result." << prop_name(*x_iter) << " = " << (*x_iter)->get_name() << ";" << endl; indent_down(); f_service_ << indent() << "}"; } else { f_service_ << "}"; } } cleanup_member_name_mapping(xs); f_service_ << endl; } if (tfunction->is_oneway()) { f_service_ << indent() << "return;" << endl; scope_down(f_service_); return; } f_service_ << indent() << "oprot.WriteMessageBegin(new TMessage(\"" << tfunction->get_name() << "\", TMessageType.Reply, seqid)); " << endl << indent() << "result.Write(oprot);" << endl << indent() << "oprot.WriteMessageEnd();" << endl << indent() << "oprot.Transport.Flush();" << endl; scope_down(f_service_); f_service_ << endl; } void t_csharp_generator::generate_csharp_union_reader(std::ofstream& out, t_struct* tunion) { // Thanks to THRIFT-1768, we don't need to check for required fields in the union const vector<t_field*>& fields = tunion->get_members(); vector<t_field*>::const_iterator f_iter; indent(out) << "public static " << tunion->get_name() << " Read(TProtocol iprot)" << endl; scope_up(out); indent(out) << tunion->get_name() << " retval;" << endl; indent(out) << "iprot.ReadStructBegin();" << endl; indent(out) << "TField field = iprot.ReadFieldBegin();" << endl; // we cannot have the first field be a stop -- we must have a single field defined indent(out) << "if (field.Type == TType.Stop)" << endl; scope_up(out); indent(out) << "iprot.ReadFieldEnd();" << endl; indent(out) << "retval = new ___undefined();" << endl; scope_down(out); indent(out) << "else" << endl; scope_up(out); indent(out) << "switch (field.ID)" << endl; scope_up(out); for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { indent(out) << "case " << (*f_iter)->get_key() << ":" << endl; indent_up(); indent(out) << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; indent_up(); indent(out) << type_name((*f_iter)->get_type()) << " temp;" << endl; generate_deserialize_field(out, (*f_iter), "temp", true); indent(out) << "retval = new " << (*f_iter)->get_name() << "(temp);" << endl; indent_down(); out << indent() << "} else { " << endl << indent() << " TProtocolUtil.Skip(iprot, field.Type);" << endl << indent() << " retval = new ___undefined();" << endl << indent() << "}" << endl << indent() << "break;" << endl; indent_down(); } indent(out) << "default: " << endl; indent_up(); indent(out) << "TProtocolUtil.Skip(iprot, field.Type);" << endl << indent() << "retval = new ___undefined();" << endl; indent(out) << "break;" << endl; indent_down(); scope_down(out); indent(out) << "iprot.ReadFieldEnd();" << endl; indent(out) << "if (iprot.ReadFieldBegin().Type != TType.Stop)" << endl; scope_up(out); indent(out) << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl; scope_down(out); // end of else for TStop scope_down(out); indent(out) << "iprot.ReadStructEnd();" << endl; indent(out) << "return retval;" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_deserialize_field(ofstream& out, t_field* tfield, string prefix, bool is_propertyless) { t_type* type = tfield->get_type(); while (type->is_typedef()) { type = ((t_typedef*)type)->get_type(); } if (type->is_void()) { throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name(); } string name = prefix + (is_propertyless ? "" : prop_name(tfield)); if (type->is_struct() || type->is_xception()) { generate_deserialize_struct(out, (t_struct*)type, name); } else if (type->is_container()) { generate_deserialize_container(out, type, name); } else if (type->is_base_type() || type->is_enum()) { indent(out) << name << " = "; if (type->is_enum()) { out << "(" << type_name(type, false, true) << ")"; } out << "iprot."; if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "compiler error: cannot serialize void field in a struct: " + name; break; case t_base_type::TYPE_STRING: if (((t_base_type*)type)->is_binary()) { out << "ReadBinary();"; } else { out << "ReadString();"; } break; case t_base_type::TYPE_BOOL: out << "ReadBool();"; break; case t_base_type::TYPE_BYTE: out << "ReadByte();"; break; case t_base_type::TYPE_I16: out << "ReadI16();"; break; case t_base_type::TYPE_I32: out << "ReadI32();"; break; case t_base_type::TYPE_I64: out << "ReadI64();"; break; case t_base_type::TYPE_DOUBLE: out << "ReadDouble();"; break; default: throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase); } } else if (type->is_enum()) { out << "ReadI32();"; } out << endl; } else { printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", tfield->get_name().c_str(), type_name(type).c_str()); } } void t_csharp_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string prefix) { if (union_ && tstruct->is_union()) { out << indent() << prefix << " = " << type_name(tstruct) << ".Read(iprot);" << endl; } else { out << indent() << prefix << " = new " << type_name(tstruct) << "();" << endl << indent() << prefix << ".Read(iprot);" << endl; } } void t_csharp_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string prefix) { scope_up(out); string obj; if (ttype->is_map()) { obj = tmp("_map"); } else if (ttype->is_set()) { obj = tmp("_set"); } else if (ttype->is_list()) { obj = tmp("_list"); } indent(out) << prefix << " = new " << type_name(ttype, false, true) << "();" << endl; if (ttype->is_map()) { out << indent() << "TMap " << obj << " = iprot.ReadMapBegin();" << endl; } else if (ttype->is_set()) { out << indent() << "TSet " << obj << " = iprot.ReadSetBegin();" << endl; } else if (ttype->is_list()) { out << indent() << "TList " << obj << " = iprot.ReadListBegin();" << endl; } string i = tmp("_i"); indent(out) << "for( int " << i << " = 0; " << i << " < " << obj << ".Count" << "; " << "++" << i << ")" << endl; scope_up(out); if (ttype->is_map()) { generate_deserialize_map_element(out, (t_map*)ttype, prefix); } else if (ttype->is_set()) { generate_deserialize_set_element(out, (t_set*)ttype, prefix); } else if (ttype->is_list()) { generate_deserialize_list_element(out, (t_list*)ttype, prefix); } scope_down(out); if (ttype->is_map()) { indent(out) << "iprot.ReadMapEnd();" << endl; } else if (ttype->is_set()) { indent(out) << "iprot.ReadSetEnd();" << endl; } else if (ttype->is_list()) { indent(out) << "iprot.ReadListEnd();" << endl; } scope_down(out); } void t_csharp_generator::generate_deserialize_map_element(ofstream& out, t_map* tmap, string prefix) { string key = tmp("_key"); string val = tmp("_val"); t_field fkey(tmap->get_key_type(), key); t_field fval(tmap->get_val_type(), val); indent(out) << declare_field(&fkey) << endl; indent(out) << declare_field(&fval) << endl; generate_deserialize_field(out, &fkey); generate_deserialize_field(out, &fval); indent(out) << prefix << "[" << key << "] = " << val << ";" << endl; } void t_csharp_generator::generate_deserialize_set_element(ofstream& out, t_set* tset, string prefix) { string elem = tmp("_elem"); t_field felem(tset->get_elem_type(), elem); indent(out) << declare_field(&felem) << endl; generate_deserialize_field(out, &felem); indent(out) << prefix << ".Add(" << elem << ");" << endl; } void t_csharp_generator::generate_deserialize_list_element(ofstream& out, t_list* tlist, string prefix) { string elem = tmp("_elem"); t_field felem(tlist->get_elem_type(), elem); indent(out) << declare_field(&felem) << endl; generate_deserialize_field(out, &felem); indent(out) << prefix << ".Add(" << elem << ");" << endl; } void t_csharp_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix, bool is_element, bool is_propertyless) { t_type* type = tfield->get_type(); while (type->is_typedef()) { type = ((t_typedef*)type)->get_type(); } string name = prefix + (is_propertyless ? "" : prop_name(tfield)); if (type->is_void()) { throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name; } if (type->is_struct() || type->is_xception()) { generate_serialize_struct(out, (t_struct*)type, name); } else if (type->is_container()) { generate_serialize_container(out, type, name); } else if (type->is_base_type() || type->is_enum()) { indent(out) << "oprot."; string nullable_name = nullable_ && !is_element && !field_is_required(tfield) ? name + ".Value" : name; if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "compiler error: cannot serialize void field in a struct: " + name; break; case t_base_type::TYPE_STRING: if (((t_base_type*)type)->is_binary()) { out << "WriteBinary("; } else { out << "WriteString("; } out << name << ");"; break; case t_base_type::TYPE_BOOL: out << "WriteBool(" << nullable_name << ");"; break; case t_base_type::TYPE_BYTE: out << "WriteByte(" << nullable_name << ");"; break; case t_base_type::TYPE_I16: out << "WriteI16(" << nullable_name << ");"; break; case t_base_type::TYPE_I32: out << "WriteI32(" << nullable_name << ");"; break; case t_base_type::TYPE_I64: out << "WriteI64(" << nullable_name << ");"; break; case t_base_type::TYPE_DOUBLE: out << "WriteDouble(" << nullable_name << ");"; break; default: throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase); } } else if (type->is_enum()) { out << "WriteI32((int)" << nullable_name << ");"; } out << endl; } else { printf("DO NOT KNOW HOW TO SERIALIZE '%s%s' TYPE '%s'\n", prefix.c_str(), tfield->get_name().c_str(), type_name(type).c_str()); } } void t_csharp_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { (void)tstruct; out << indent() << prefix << ".Write(oprot);" << endl; } void t_csharp_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { scope_up(out); if (ttype->is_map()) { indent(out) << "oprot.WriteMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix << ".Count));" << endl; } else if (ttype->is_set()) { indent(out) << "oprot.WriteSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << prefix << ".Count));" << endl; } else if (ttype->is_list()) { indent(out) << "oprot.WriteListBegin(new TList(" << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".Count));" << endl; } string iter = tmp("_iter"); if (ttype->is_map()) { indent(out) << "foreach (" << type_name(((t_map*)ttype)->get_key_type()) << " " << iter << " in " << prefix << ".Keys)"; } else if (ttype->is_set()) { indent(out) << "foreach (" << type_name(((t_set*)ttype)->get_elem_type()) << " " << iter << " in " << prefix << ")"; } else if (ttype->is_list()) { indent(out) << "foreach (" << type_name(((t_list*)ttype)->get_elem_type()) << " " << iter << " in " << prefix << ")"; } out << endl; scope_up(out); if (ttype->is_map()) { generate_serialize_map_element(out, (t_map*)ttype, iter, prefix); } else if (ttype->is_set()) { generate_serialize_set_element(out, (t_set*)ttype, iter); } else if (ttype->is_list()) { generate_serialize_list_element(out, (t_list*)ttype, iter); } scope_down(out); if (ttype->is_map()) { indent(out) << "oprot.WriteMapEnd();" << endl; } else if (ttype->is_set()) { indent(out) << "oprot.WriteSetEnd();" << endl; } else if (ttype->is_list()) { indent(out) << "oprot.WriteListEnd();" << endl; } scope_down(out); } void t_csharp_generator::generate_serialize_map_element(ofstream& out, t_map* tmap, string iter, string map) { t_field kfield(tmap->get_key_type(), iter); generate_serialize_field(out, &kfield, "", true); t_field vfield(tmap->get_val_type(), map + "[" + iter + "]"); generate_serialize_field(out, &vfield, "", true); } void t_csharp_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { t_field efield(tset->get_elem_type(), iter); generate_serialize_field(out, &efield, "", true); } void t_csharp_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { t_field efield(tlist->get_elem_type(), iter); generate_serialize_field(out, &efield, "", true); } void t_csharp_generator::generate_property(ofstream& out, t_field* tfield, bool isPublic, bool generateIsset) { generate_csharp_property(out, tfield, isPublic, generateIsset, "_"); } void t_csharp_generator::generate_csharp_property(ofstream& out, t_field* tfield, bool isPublic, bool generateIsset, std::string fieldPrefix) { if ((serialize_ || wcf_) && isPublic) { indent(out) << "[DataMember(Order = 0)]" << endl; } bool has_default = field_has_default(tfield); bool is_required = field_is_required(tfield); if ((nullable_ && !has_default) || (is_required)) { indent(out) << (isPublic ? "public " : "private ") << type_name(tfield->get_type(), false, false, true, is_required) << " " << prop_name(tfield) << " { get; set; }" << endl; } else { indent(out) << (isPublic ? "public " : "private ") << type_name(tfield->get_type(), false, false, true) << " " << prop_name(tfield) << endl; scope_up(out); indent(out) << "get" << endl; scope_up(out); bool use_nullable = false; if (nullable_) { t_type* ttype = tfield->get_type(); while (ttype->is_typedef()) { ttype = ((t_typedef*)ttype)->get_type(); } if (ttype->is_base_type()) { use_nullable = ((t_base_type*)ttype)->get_base() != t_base_type::TYPE_STRING; } } indent(out) << "return " << fieldPrefix + tfield->get_name() << ";" << endl; scope_down(out); indent(out) << "set" << endl; scope_up(out); if (use_nullable) { if (generateIsset) { indent(out) << "__isset." << normalize_name(tfield->get_name()) << " = value.HasValue;" << endl; } indent(out) << "if (value.HasValue) this." << fieldPrefix + tfield->get_name() << " = value.Value;" << endl; } else { if (generateIsset) { indent(out) << "__isset." << normalize_name(tfield->get_name()) << " = true;" << endl; } indent(out) << "this." << fieldPrefix + tfield->get_name() << " = value;" << endl; } scope_down(out); scope_down(out); } out << endl; } std::string t_csharp_generator::make_valid_csharp_identifier(std::string const& fromName) { std::string str = fromName; if (str.empty()) { return str; } // tests rely on this assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9')); // if the first letter is a number, we add an additional underscore in front of it char c = str.at(0); if (('0' <= c) && (c <= '9')) { str = "_" + str; } // following chars: letter, number or underscore for (size_t i = 0; i < str.size(); ++i) { c = str.at(i); if ((('A' > c) || (c > 'Z')) && (('a' > c) || (c > 'z')) && (('0' > c) || (c > '9')) && ('_' != c)) { str.replace(i, 1, "_"); } } return str; } void t_csharp_generator::cleanup_member_name_mapping(void* scope) { if (member_mapping_scope != scope) { if (member_mapping_scope == NULL) { throw "internal error: cleanup_member_name_mapping() not active"; } else { throw "internal error: cleanup_member_name_mapping() called for wrong struct"; } } member_mapping_scope = NULL; member_name_mapping.clear(); } string t_csharp_generator::get_mapped_member_name(string name) { map<string, string>::iterator iter = member_name_mapping.find(name); if (member_name_mapping.end() != iter) { return iter->second; } pverbose("no mapping for member %s\n", name.c_str()); return name; } void t_csharp_generator::prepare_member_name_mapping(t_struct* tstruct) { prepare_member_name_mapping(tstruct, tstruct->get_members(), tstruct->get_name()); } void t_csharp_generator::prepare_member_name_mapping(void* scope, const vector<t_field*>& members, const string& structname) { if (member_mapping_scope != NULL) { if (member_mapping_scope != scope) { throw "internal error: prepare_member_name_mapping() already active for different struct"; } else { throw "internal error: prepare_member_name_mapping() already active for this struct"; } } member_mapping_scope = scope; member_name_mapping.clear(); std::set<std::string> used_member_names; vector<t_field*>::const_iterator iter; // current C# generator policy: // - prop names are always rendered with an Uppercase first letter // - struct names are used as given for (iter = members.begin(); iter != members.end(); ++iter) { string oldname = (*iter)->get_name(); string newname = prop_name(*iter, true); while (true) { // name conflicts with struct (CS0542 error) if (structname.compare(newname) == 0) { pverbose("struct %s: member %s conflicts with struct (preventing CS0542)\n", structname.c_str(), newname.c_str()); newname += '_'; } // new name conflicts with another member if (used_member_names.find(newname) != used_member_names.end()) { pverbose("struct %s: member %s conflicts with another member\n", structname.c_str(), newname.c_str()); newname += '_'; continue; } // add always, this helps us to detect edge cases like // different spellings ("foo" and "Foo") within the same struct pverbose("struct %s: member mapping %s => %s\n", structname.c_str(), oldname.c_str(), newname.c_str()); member_name_mapping[oldname] = newname; used_member_names.insert(newname); break; } } } std::string t_csharp_generator::prop_name(t_field* tfield, bool suppress_mapping) { string name(tfield->get_name()); if (suppress_mapping) { name[0] = toupper(name[0]); } else { name = get_mapped_member_name(name); } return name; } string t_csharp_generator::type_name(t_type* ttype, bool in_container, bool in_init, bool in_param, bool is_required) { (void)in_init; while (ttype->is_typedef()) { ttype = ((t_typedef*)ttype)->get_type(); } if (ttype->is_base_type()) { return base_type_name((t_base_type*)ttype, in_container, in_param, is_required); } else if (ttype->is_map()) { t_map* tmap = (t_map*)ttype; return "Dictionary<" + type_name(tmap->get_key_type(), true) + ", " + type_name(tmap->get_val_type(), true) + ">"; } else if (ttype->is_set()) { t_set* tset = (t_set*)ttype; return "THashSet<" + type_name(tset->get_elem_type(), true) + ">"; } else if (ttype->is_list()) { t_list* tlist = (t_list*)ttype; return "List<" + type_name(tlist->get_elem_type(), true) + ">"; } t_program* program = ttype->get_program(); string postfix = (!is_required && nullable_ && in_param && ttype->is_enum()) ? "?" : ""; if (program != NULL && program != program_) { string ns = program->get_namespace("csharp"); if (!ns.empty()) { return ns + "." + normalize_name(ttype->get_name()) + postfix; } } return normalize_name(ttype->get_name()) + postfix; } string t_csharp_generator::base_type_name(t_base_type* tbase, bool in_container, bool in_param, bool is_required) { (void)in_container; string postfix = (!is_required && nullable_ && in_param) ? "?" : ""; switch (tbase->get_base()) { case t_base_type::TYPE_VOID: return "void"; case t_base_type::TYPE_STRING: if (tbase->is_binary()) { return "byte[]"; } else { return "string"; } case t_base_type::TYPE_BOOL: return "bool" + postfix; case t_base_type::TYPE_BYTE: return "sbyte" + postfix; case t_base_type::TYPE_I16: return "short" + postfix; case t_base_type::TYPE_I32: return "int" + postfix; case t_base_type::TYPE_I64: return "long" + postfix; case t_base_type::TYPE_DOUBLE: return "double" + postfix; default: throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase->get_base()); } } string t_csharp_generator::declare_field(t_field* tfield, bool init, std::string prefix) { string result = type_name(tfield->get_type()) + " " + prefix + tfield->get_name(); if (init) { t_type* ttype = tfield->get_type(); while (ttype->is_typedef()) { ttype = ((t_typedef*)ttype)->get_type(); } if (ttype->is_base_type() && field_has_default(tfield)) { ofstream dummy; result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value()); } else if (ttype->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "NO T_VOID CONSTRUCT"; case t_base_type::TYPE_STRING: result += " = null"; break; case t_base_type::TYPE_BOOL: result += " = false"; break; case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: case t_base_type::TYPE_I64: result += " = 0"; break; case t_base_type::TYPE_DOUBLE: result += " = (double)0"; break; } } else if (ttype->is_enum()) { result += " = (" + type_name(ttype, false, true) + ")0"; } else if (ttype->is_container()) { result += " = new " + type_name(ttype, false, true) + "()"; } else { result += " = new " + type_name(ttype, false, true) + "()"; } } return result + ";"; } string t_csharp_generator::function_signature(t_function* tfunction, string prefix) { t_type* ttype = tfunction->get_returntype(); return type_name(ttype) + " " + normalize_name(prefix + tfunction->get_name()) + "(" + argument_list(tfunction->get_arglist()) + ")"; } string t_csharp_generator::function_signature_async_begin(t_function* tfunction, string prefix) { string comma = (tfunction->get_arglist()->get_members().size() > 0 ? ", " : ""); return "IAsyncResult " + normalize_name(prefix + tfunction->get_name()) + "(AsyncCallback callback, object state" + comma + argument_list(tfunction->get_arglist()) + ")"; } string t_csharp_generator::function_signature_async_end(t_function* tfunction, string prefix) { t_type* ttype = tfunction->get_returntype(); return type_name(ttype) + " " + normalize_name(prefix + tfunction->get_name()) + "(IAsyncResult asyncResult)"; } string t_csharp_generator::function_signature_async(t_function* tfunction, string prefix) { t_type* ttype = tfunction->get_returntype(); string task = "Task"; if (!ttype->is_void()) { task += "<" + type_name(ttype) + ">"; } return task + " " + normalize_name(prefix + tfunction->get_name()) + "Async(" + argument_list(tfunction->get_arglist()) + ")"; } string t_csharp_generator::argument_list(t_struct* tstruct) { string result = ""; const vector<t_field*>& fields = tstruct->get_members(); vector<t_field*>::const_iterator f_iter; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; } else { result += ", "; } result += type_name((*f_iter)->get_type()) + " " + normalize_name((*f_iter)->get_name()); } return result; } string t_csharp_generator::type_to_enum(t_type* type) { while (type->is_typedef()) { type = ((t_typedef*)type)->get_type(); } if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "NO T_VOID CONSTRUCT"; case t_base_type::TYPE_STRING: return "TType.String"; case t_base_type::TYPE_BOOL: return "TType.Bool"; case t_base_type::TYPE_BYTE: return "TType.Byte"; case t_base_type::TYPE_I16: return "TType.I16"; case t_base_type::TYPE_I32: return "TType.I32"; case t_base_type::TYPE_I64: return "TType.I64"; case t_base_type::TYPE_DOUBLE: return "TType.Double"; } } else if (type->is_enum()) { return "TType.I32"; } else if (type->is_struct() || type->is_xception()) { return "TType.Struct"; } else if (type->is_map()) { return "TType.Map"; } else if (type->is_set()) { return "TType.Set"; } else if (type->is_list()) { return "TType.List"; } throw "INVALID TYPE IN type_to_enum: " + type->get_name(); } void t_csharp_generator::generate_csharp_docstring_comment(ofstream& out, string contents) { generate_docstring_comment(out, "/// <summary>\n", "/// ", contents, "/// </summary>\n"); } void t_csharp_generator::generate_csharp_doc(ofstream& out, t_field* field) { if (field->get_type()->is_enum()) { string combined_message = field->get_doc() + "\n<seealso cref=\"" + get_enum_class_name(field->get_type()) + "\"/>"; generate_csharp_docstring_comment(out, combined_message); } else { generate_csharp_doc(out, (t_doc*)field); } } void t_csharp_generator::generate_csharp_doc(ofstream& out, t_doc* tdoc) { if (tdoc->has_doc()) { generate_csharp_docstring_comment(out, tdoc->get_doc()); } } void t_csharp_generator::generate_csharp_doc(ofstream& out, t_function* tfunction) { if (tfunction->has_doc()) { stringstream ps; const vector<t_field*>& fields = tfunction->get_arglist()->get_members(); vector<t_field*>::const_iterator p_iter; for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) { t_field* p = *p_iter; ps << "\n<param name=\"" << p->get_name() << "\">"; if (p->has_doc()) { std::string str = p->get_doc(); str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); // remove the newlines that appear from the parser ps << str; } ps << "</param>"; } generate_docstring_comment(out, "", "/// ", "<summary>\n" + tfunction->get_doc() + "</summary>" + ps.str(), ""); } } std::string t_csharp_generator::get_enum_class_name(t_type* type) { string package = ""; t_program* program = type->get_program(); if (program != NULL && program != program_) { package = program->get_namespace("csharp") + "."; } return package + type->get_name(); } THRIFT_REGISTER_GENERATOR( csharp, "C#", " async: Adds Async support using Task.Run.\n" " asyncctp: Adds Async CTP support using TaskEx.Run.\n" " wcf: Adds bindings for WCF to generated classes.\n" " serial: Add serialization support to generated classes.\n" " nullable: Use nullable types for properties.\n" " hashcode: Generate a hashcode and equals implementation for classes.\n" " union: Use new union typing, which includes a static read function for union " "types.\n")
本篇博文描述的是对一个很简单的java文件的对应的class文件的二进制分析. 要分析class文件的二进制格式,必须了解对应版本的jvm specification. 由于jvm specification中关于class文件的内容有点多,所以这里不在描述相同的内容. 说明:本篇博文中设计的class文件对应的jvm specification版本是 jvm 8. java文件01:SimpleClassDemo1.java package com.rocky.jvm.classfile; /** * Created by rocky on 14-12-23. */ public class SimpleClassDemo1 { } 对应的class文件二进制内容:SimpleClassDemo1.class (16进制表示) cafe babe 0000 0034 0010 0a00 0300 0d07 000e 0700 0f01 0006 3c69 6e69 743e 0100 0328 2956 0100 0443 6f64 6501 000f 4c69 6e65 4e75 6d62 6572 5461 626c 6501 0012 4c6f 6361 6c56 6172 6961 626c 6554 6162 6c65 0100 0474 6869 7301 002a 4c63 6f6d 2f72 6f63 6b79 2f6a 766d 2f63 6c61 7373 6669 6c65 2f53 696d 706c 6543 6c61 7373 4465 6d6f 313b 0100 0a53 6f75 7263 6546 696c 6501 0015 5369 6d70 6c65 436c 6173 7344 656d 6f31 2e6a 6176 610c 0004 0005 0100 2863 6f6d 2f72 6f63 6b79 2f6a 766d 2f63 6c61 7373 6669 6c65 2f53 696d 706c 6543 6c61 7373 4465 6d6f 3101 0010 6a61 7661 2f6c 616e 672f 4f62 6a65 6374 0021 0002 0003 0000 0000 0001 0001 0004 0005 0001 0006 0000 002f 0001 0001 0000 0005 2ab7 0001 b100 0000 0200 0700 0000 0600 0100 0000 0600 0800 0000 0c00 0100 0000 0500 0900 0a00 0000 0100 0b00 0000 0200 0c 分析结果如下: (直接帖文字会变形,所以截的图.这里提供下载:http://download.csdn.net/detail/u012358984/8319813) java文件02:SimpleClassDemo2.java package com.rocky.jvm.classfile; /** * Created by rocky on 15-1-2. */ public class SimpleClassDemo2 implements Interface01,Interface02 { public long add(int i, long j) { return i + j; } private void calc() { int i = 5 + 5; System.out.println(i); } private int field1 = 0; public int field2 ; protected static int field3 = 6; public final int field4 = 7; public static final long field5 = 8; @Override public Integer interface01() { return 0; } @Override public boolean interface02() { return false; } } SimpleClassDemo2.class二进制内容(16进制表示) cafe babe 0000 0034 0041 0a00 0900 2a09 0008 002b 0900 0800 2c09 002d 002e 0a00 2f00 300a 0031 0032 0900 0800 3307 0034 0700 3507 0036 0700 3701 0006 6669 656c 6431 0100 0149 0100 0666 6965 6c64 3201 0006 6669 656c 6433 0100 0666 6965 6c64 3401 000d 436f 6e73 7461 6e74 5661 6c75 6503 0000 0007 0100 0666 6965 6c64 3501 0001 4a05 0000 0000 0000 0008 0100 063c 696e 6974 3e01 0003 2829 5601 0004 436f 6465 0100 0f4c 696e 654e 756d 6265 7254 6162 6c65 0100 124c 6f63 616c 5661 7269 6162 6c65 5461 626c 6501 0004 7468 6973 0100 2a4c 636f 6d2f 726f 636b 792f 6a76 6d2f 636c 6173 7366 696c 652f 5369 6d70 6c65 436c 6173 7344 656d 6f32 3b01 0003 6164 6401 0005 2849 4a29 4a01 0001 6901 0001 6a01 0004 6361 6c63 0100 0b69 6e74 6572 6661 6365 3031 0100 1528 294c 6a61 7661 2f6c 616e 672f 496e 7465 6765 723b 0100 0b69 6e74 6572 6661 6365 3032 0100 0328 295a 0100 083c 636c 696e 6974 3e01 000a 536f 7572 6365 4669 6c65 0100 1553 696d 706c 6543 6c61 7373 4465 6d6f 322e 6a61 7661 0c00 1700 180c 000c 000d 0c00 1000 0d07 0038 0c00 3900 3a07 003b 0c00 3c00 3d07 003e 0c00 3f00 400c 000f 000d 0100 2863 6f6d 2f72 6f63 6b79 2f6a 766d 2f63 6c61 7373 6669 6c65 2f53 696d 706c 6543 6c61 7373 4465 6d6f 3201 0010 6a61 7661 2f6c 616e 672f 4f62 6a65 6374 0100 2363 6f6d 2f72 6f63 6b79 2f6a 766d 2f63 6c61 7373 6669 6c65 2f49 6e74 6572 6661 6365 3031 0100 2363 6f6d 2f72 6f63 6b79 2f6a 766d 2f63 6c61 7373 6669 6c65 2f49 6e74 6572 6661 6365 3032 0100 106a 6176 612f 6c61 6e67 2f53 7973 7465 6d01 0003 6f75 7401 0015 4c6a 6176 612f 696f 2f50 7269 6e74 5374 7265 616d 3b01 0013 6a61 7661 2f69 6f2f 5072 696e 7453 7472 6561 6d01 0007 7072 696e 746c 6e01 0004 2849 2956 0100 116a 6176 612f 6c61 6e67 2f49 6e74 6567 6572 0100 0776 616c 7565 4f66 0100 1628 4929 4c6a 6176 612f 6c61 6e67 2f49 6e74 6567 6572 3b00 2100 0800 0900 0200 0a00 0b00 0500 0200 0c00 0d00 0000 0100 0e00 0d00 0000 0c00 0f00 0d00 0000 1100 1000 0d00 0100 1100 0000 0200 1200 1900 1300 1400 0100 1100 0000 0200 1500 0600 0100 1700 1800 0100 1900 0000 4200 0200 0100 0000 102a b700 012a 03b5 0002 2a10 07b5 0003 b100 0000 0200 1a00 0000 0e00 0300 0000 0600 0400 1000 0900 1600 1b00 0000 0c00 0100 0000 1000 1c00 1d00 0000 0100 1e00 1f00 0100 1900 0000 4300 0400 0400 0000 051b 8520 61ad 0000 0002 001a 0000 0006 0001 0000 0008 001b 0000 0020 0003 0000 0005 001c 001d 0000 0000 0005 0020 000d 0001 0000 0005 0021 0014 0002 0002 0022 0018 0001 0019 0000 0047 0002 0002 0000 000b 100a 3cb2 0004 1bb6 0005 b100 0000 0200 1a00 0000 0e00 0300 0000 0c00 0300 0d00 0a00 0e00 1b00 0000 1600 0200 0000 0b00 1c00 1d00 0000 0300 0800 2000 0d00 0100 0100 2300 2400 0100 1900 0000 2f00 0100 0100 0000 0503 b800 06b0 0000 0002 001a 0000 0006 0001 0000 001c 001b 0000 000c 0001 0000 0005 001c 001d 0000 0001 0025 0026 0001 0019 0000 002c 0001 0001 0000 0002 03ac 0000 0002 001a 0000 0006 0001 0000 0021 001b 0000 000c 0001 0000 0002 001c 001d 0000 0008 0027 0018 0001 0019 0000 001e 0001 0000 0000 0006 1006 b300 07b1 0000 0001 001a 0000 0006 0001 0000 0014 0001 0028 0000 0002 0029 二进制分析结果: (txt下载:http://download.csdn.net/detail/u012358984/8321477)
1. 系统最大打开文件描述符数:/proc/sys/fs/file-max a. 查看 $ cat /proc/sys/fs/file-max 186405 2. 设置 a. 临时性 # echo 1000000 > /proc/sys/fs/file-max 2. 永久性:在/etc/sysctl.conf中设置 fs.file-max = 1000000 2. 进程最大打开文件描述符数:user limit中nofile的soft limit a. 查看 $ ulimit -n 1700000 2. 设置 a. 临时性:通过ulimit -Sn设置最大打开文件描述符数的soft limit,注意soft limit不能大于hard limit(ulimit -Hn可查看hard limit),另外ulimit -n默认查看的是soft limit,但是ulimit -n 1800000则是同时设置soft limit和hard limit。对于非root用户只能设置比原来小的hard limit。 查看hard limit: $ ulimit -Hn 1700000 设置soft limit,必须小于hard limit: $ ulimit -Sn 1600000 2. 永久性:上面的方法只是临时性的,注销重新登录就失效了,而且不能增大hard limit,只能在hard limit范围内修改soft limit。若要使修改永久有效,则需要在/etc/security/limits.conf中进行设置(需要root权限),可添加如下两行,表示用户chanon最大打开文件描述符数的soft limit为1800000,hard limit为2000000。以下设置需要注销之后重新登录才能生效: chanon soft nofile 1800000 chanon hard nofile 2000000 设置nofile的hard limit还有一点要注意的就是hard limit不能大于/proc/sys/fs/nr_open,假如hard limit大于nr_open,注销后无法正常登录。可以修改nr_open的值: # echo 2000000 > /proc/sys/fs/nr_open 3. 查看当前系统使用的打开文件描述符数 [root@localhost bin]# cat /proc/sys/fs/file-nr 5664 0 186405 其中第一个数表示当前系统已分配使用的打开文件描述符数,第二个数为分配后已释放的(目前已不再使用),第三个数等于file-max。 4. 总结: a. 所有进程打开的文件描述符数不能超过/proc/sys/fs/file-max b. 单个进程打开的文件描述符数不能超过user limit中nofile的soft limit c. nofile的soft limit不能超过其hard limit d. nofile的hard limit不能超过/proc/sys/fs/nr_open 原文链接:http://blog.csdn.net/superchanon/article/details/13303705
解决办法: sudo apt-get autoremove openjdk-7-jre-headless 我自己没有使用自带的openjdk,而是安装的jdk8.
问题描述 在使用visual studio 2010 SP1编译thrift-0.9.1的compiler时,出现: The command "flex -o "src\\thriftl.cc" src/thriftl.ll bison -y -o "src\thrifty.cc" –defines="src/thrifty.hh" src/thrifty.yy 问题原因 flex和bison原本是linux下的可执行程序,windows上需要分别安装bison.exe和flex.exe。 解决步骤 1)下载并安装bison 从此处下载bison安装程序,安装路径随意,比如我安装在D:\dev_tools\GnuWin32目录下,安装之后GnuWin32目录下将包括: -GnuWin32/ –bin/ –contrib/ –doc/ –include/ –info/ –lib/ –man/ –manifest/ –share/ –uninstall/ 2)下载并安装flex 从此处下载flex解压包,flex包内包含bin,contrib,man以及manifest四个目录。这里将包内的所有文件夹复制到GnuWin32目录,比如我就复制: flex/bin -> GnuWin32/ flex/contrib -> GnuWin32/ flex/man ->GnuWin32/ flex/manifest ->GnuWin32/ 3)添加环境变量 将GnuWin32/bin(需要全路径)添加到Windows环境变量中。 4)重启Visual Studio 2010 SP1。如果不重启,此时编译依然会报错。 5)此时编译会报错Cannot open source file: ‘src\thriftl.cc’: No such file or directory,原因是compiler工程里边的Pre-Build Event有错。 flex-o "src\\thriftl.cc" src/thriftl.ll bison -y -o "src\thrifty.cc" –defines="src/thrifty.hh" src/thrifty.yy flex的语法需要-o之后没有空格,所以需要将-o和src之间的空格去掉,即 flex -o"src\\thriftl.cc" src/thriftl.ll bison -y -o "src\thrifty.cc" –defines="src/thrifty.hh" src/thrifty.yy 6)接着就是最后一个错误了 Cannot open include file: ‘unistd.h’: No such file or directory 这里会提示找不到unistd.h,unistd.h是linux下的头文件,相当于Windows下的Windows.h。将thriftl.cc中的#include <unistd.h>屏蔽是没有用的,因为这个文件是自动生成的。 在{thrift}/compiler/cpp目录下建立unistd.h空文件,并将compiler工程属性(Properties -> C/C++ -> General -> Additional Include Directories里加入当前目录.即可。 All Done! 原文链接:http://mojijs.com/2014/12/162929/index.html ==================================================================== 我自己还遇到了 "不能找到 version.h"的错误,answer: 在项目文件夹-->windows文件夹下新建version.h文件,内容如下: /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _THRIFT_WINDOWS_VERSION_H_ #define _THRIFT_WINDOWS_VERSION_H_ 1 #if defined(_MSC_VER) && (_MSC_VER > 1200) #pragma once #endif // _MSC_VER #ifndef _WIN32 #error "This is a Windows header only" #endif #define THRIFT_VERSION "@PACKAGE_VERSION@" #endif // _THRIFT_WINDOWS_VERSION_H_ 然后可能还会遇到: LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏 answer: 项目——项目属性——配置属性——连接器——清单文件——嵌入清单 “是”改为“否”
本文献给对git很迷茫的新手,注意是新手,但至少会点基本操作,有点基本概念的新手,我不会从怎么用github和git是什么开始讲的。如果作为新手你看书又看不进去,原理又太复杂,有没有直接了当告诉我们怎么给项目贡献代码,并和项目同步代码的大体流程。于是我快速写了这么个东西。一来整理下自己混乱的思路,二来想号召大家一起用git开发点东西,可是好几个同鞋只会寂寞的给自己push。我先说下 我之前对github操作的一些迷茫历程,然后之后是怎么解惑的。1. 最最开始,我以为git clone [项目地址],也就是把代码clone下来 然后修改,然后push到项目里就可以了。后来发现,这种情况只适合该项目属于你自己的情况,如果你git clone别人的项目代码,你想push都push不上去,因为 git push 不是需要你输入github帐号密码么。2. 然后 我就知道了 github上 不是有个fork么, 好,那么给别人的项目提交代码的方式就有了,先fork下别人的代码,于是你的github上就出现了同名的项目,这个项目就属于你自己了,你把这个自己的项目git clone到本地,修修改改,然后push到你自己的项目里,那么你如何把你对自己项目的改动,给发到之前fork的那个原项目呢,看见了没,在github上你的项目页面有个按钮,叫Pull request,对 你点它就会把你的修改发到对方的项目里,人还会收到邮件呢,由原项目主人决定是否接受你的修改。但是,这样问题就出来了,在你fork他的项目之后,如果他又更新了代码,你自己fork的项目怎么做到和原项目同步呢? 我就想啊,是不是 我还得重新git clone原项目的代码,然后手动合并到我fork的项目里呢。。。梁老师说,你丫这太蛋疼了,肯定不是这么麻烦,我细想,也是啊,这不2么。。。3,然后,从《Pro git》上看到一个知识点,我擦,github居然可以给项目添加合作者,也就是说,假如你,对,说你呢,戴眼镜那个,你想参与我的项目,你跟我说一声,我就把你添加到我的项目里作为合作者,这个选项在项目的设置里面有,只要我添加你了,你就可以git clone我的代码然后修修改改,然后直接push上来就行了,就不用fork那么麻烦了,如果你要更新服务器代码,只要git pull就行了,看 合作者这东西多方便,就像我们在公司用svn似的。4.然后我就想啊,有了合作者还需要你丫fork这个功能干啥?仔细一想,你写个好项目,不能随便加合作者啊,加了个熊孩子把你代码改废了可咋整,这年头熊孩子很多,我自己不就是一个么。所以fork肯定还是需要,fork就是专门预防熊孩子的,这就是真相!那么前面说道到fork之后如何与原项目同步的问题还在啊,没有得到解决。5.于是《Pro git》再次给了我一个解答,具体流程是你啊想给我的项目做贡献,你先git clone我的代码到本地,然后修修改改,然后你不是不能push到我的项目里么,你可以先在github页面上fork我的项目,有了你自己的项目地址(url)之后呢,你在本地操作git remote add [sort name] [your url],意思就是添加第二个远程仓库地址,这个仓库的“昵称”就是你刚指定的[sort name],然后,你之后push文件呢 就通过指定这个[sort name]来push到这个你自己的仓库里。等你觉得想要把你改的发给原项目同步,就在你的项目上点Pull request按钮.说下另一种情况,如果是,原项目发生了改动,你要想同步到本地,就直接从git fetch origin 从原项目的地址同步代码,然后再merge就好了。当然,如《Pro git》上所写,你可以通过新建分支的方式往自己的项目上push,这样同步的时候直接fetch就行了。这块如果我没写明白或者你想知道怎么新建分支的方式push到自己的项目里,可以直接参考《Pro git》的“公开的小型项目”一节,那我的贡献就是指点你如何从这本书里快速的找到你想要的。= =。好了,时间有限,我写的快不一定讲明白,欢迎跟我讨论。感谢linus,感谢git,感谢github,感谢kindle,感谢《Pro git》,感谢梁老师,感谢我自己,感谢所有的熊孩子们。 原文链接:http://site.douban.com/196781/widget/notes/12161495/note/269163206/
NodeJS基础 什么是NodeJS JS是脚本语言,脚本语言都需要一个解析器才能运行。对于写在HTML页面里的JS,浏览器充当了解析器的角色。而对于需要独立运行的JS,NodeJS就是一个解析器。 每一种解析器都是一个运行环境,不但允许JS定义各种数据结构,进行各种计算,还允许JS使用运行环境提供的内置对象和方法做一些事情。例如运行在浏览器中的JS的用途是操作DOM,浏览器就提供了document之类的内置对象。而运行在NodeJS中的JS的用途是操作磁盘文件或搭建HTTP服务器,NodeJS就相应提供了fs、http等内置对象。 有啥用处 尽管存在一听说可以直接运行JS文件就觉得很酷的同学,但大多数同学在接触新东西时首先关心的是有啥用处,以及能带来啥价值。 NodeJS的作者说,他创造NodeJS的目的是为了实现高性能Web服务器,他首先看重的是事件机制和异步IO模型的优越性,而不是JS。但是他需要选择一种编程语言实现他的想法,这种编程语言不能自带IO功能,并且需要能良好支持事件机制。JS没有自带IO功能,天生就用于处理浏览器中的DOM事件,并且拥有一大群程序员,因此就成为了天然的选择。 如他所愿,NodeJS在服务端活跃起来,出现了大批基于NodeJS的Web服务。而另一方面,NodeJS让前端众如获神器,终于可以让自己的能力覆盖范围跳出浏览器窗口,更大批的前端工具如雨后春笋。 因此,对于前端而言,虽然不是人人都要拿NodeJS写一个服务器程序,但简单可至使用命令交互模式调试JS代码片段,复杂可至编写工具提升工作效率。 NodeJS生态圈正欣欣向荣。 如何安装 安装程序 NodeJS提供了一些安装程序,都可以在nodejs.org这里下载并安装。 Windows系统下,选择和系统版本匹配的.msi后缀的安装文件。Mac OS X系统下,选择.pkg后缀的安装文件。 编译安装 Linux系统下没有现成的安装程序可用,虽然一些发行版可以使用apt-get之类的方式安装,但不一定能安装到最新版。因此Linux系统下一般使用以下方式编译方式安装NodeJS。 确保系统下g++版本在4.6以上,python版本在2.6以上。 从nodejs.org下载tar.gz后缀的NodeJS最新版源代码包并解压到某个位置。 进入解压到的目录,使用以下命令编译和安装。 $ ./configure $ make $ sudo make install 如何运行 打开终端,键入node进入命令交互模式,可以输入一条代码语句后立即执行并显示结果,例如: $ node > console.log('Hello World!'); Hello World! 如果要运行一大段代码的话,可以先写一个JS文件再运行。例如有以下hello.js。 function hello() { console.log('Hello World!'); } hello(); 写好后在终端下键入node hello.js运行,结果如下: $ node hello.js Hello World! 权限问题 在Linux系统下,使用NodeJS监听80或443端口提供HTTP(S)服务时需要root权限,有两种方式可以做到。 一种方式是使用sudo命令运行NodeJS。例如通过以下命令运行的server.js中有权限使用80和443端口。一般推荐这种方式,可以保证仅为有需要的JS脚本提供root权限。 $ sudo node server.js 另一种方式是使用chmod +s命令让NodeJS总是以root权限运行,具体做法如下。因为这种方式让任何JS脚本都有了root权限,不太安全,因此在需要很考虑安全的系统下不推荐使用。 $ sudo chown root /usr/local/bin/node $ sudo chmod +s /usr/local/bin/node 模块 编写稍大一点的程序时一般都会将代码模块化。在NodeJS中,一般将代码合理拆分到不同的JS文件中,每一个文件就是一个模块,而文件路径就是模块名。 在编写每个模块时,都有require、exports、module三个预先定义好的变量可供使用。 require require函数用于在当前模块中加载和使用别的模块,传入一个模块名,返回一个模块导出对象。模块名可使用相对路径(以./开头),或者是绝对路径(以/或C:之类的盘符开头)。另外,模块名中的.js扩展名可以省略。以下是一个例子。 var foo1 = require('./foo'); var foo2 = require('./foo.js'); var foo3 = require('/home/user/foo'); var foo4 = require('/home/user/foo.js'); // foo1至foo4中保存的是同一个模块的导出对象。 另外,可以使用以下方式加载和使用一个JSON文件。 var data = require('./data.json'); exports exports对象是当前模块的导出对象,用于导出模块公有方法和属性。别的模块通过require函数使用当前模块时得到的就是当前模块的exports对象。以下例子中导出了一个公有方法。 exports.hello = function () { console.log('Hello World!'); }; module 通过module对象可以访问到当前模块的一些相关信息,但最多的用途是替换当前模块的导出对象。例如模块导出对象默认是一个普通对象,如果想改成一个函数的话,可以使用以下方式。 module.exports = function () { console.log('Hello World!'); }; 以上代码中,模块默认导出对象被替换为一个函数。 模块初始化 一个模块中的JS代码仅在模块第一次被使用时执行一次,并在执行过程中初始化模块的导出对象。之后,缓存起来的导出对象被重复利用。 主模块 通过命令行参数传递给NodeJS以启动程序的模块被称为主模块。主模块负责调度组成整个程序的其它模块完成工作。例如通过以下命令启动程序时,main.js就是主模块。 $ node main.js 完整示例 例如有以下目录。 - /home/user/hello/ - util/ counter.js main.js 其中counter.js内容如下: var i = 0; function count() { return ++i; } exports.count = count; 该模块内部定义了一个私有变量i,并在exports对象导出了一个公有方法count。 主模块main.js内容如下: var counter1 = require('./util/counter'); var counter2 = require('./util/counter'); console.log(counter1.count()); console.log(counter2.count()); console.log(counter2.count()); 运行该程序的结果如下: $ node main.js 1 2 3 可以看到,counter.js并没有因为被require了两次而初始化两次。 二进制模块 虽然一般我们使用JS编写模块,但NodeJS也支持使用C/C++编写二进制模块。编译好的二进制模块除了文件扩展名是.node外,和JS模块的使用方式相同。虽然二进制模块能使用操作系统提供的所有功能,拥有无限的潜能,但对于前端同学而言编写过于困难,并且难以跨平台使用,因此不在本教程的覆盖范围内。 小结 本章介绍了有关NodeJS的基本概念和使用方法,总结起来有以下知识点: NodeJS是一个JS脚本解析器,任何操作系统下安装NodeJS本质上做的事情都是把NodeJS执行程序复制到一个目录,然后保证这个目录在系统PATH环境变量下,以便终端下可以使用node命令。 终端下直接输入node命令可进入命令交互模式,很适合用来测试一些JS代码片段,比如正则表达式。 NodeJS使用CMD模块系统,主模块作为程序入口点,所有模块在执行过程中只初始化一次。 除非JS模块不能满足需求,否则不要轻易使用二进制模块,否则你的用户会叫苦连天。 代码的组织和部署 有经验的C程序员在编写一个新程序时首先从make文件写起。同样的,使用NodeJS编写程序前,为了有个良好的开端,首先需要准备好代码的目录结构和部署方式,就如同修房子要先搭脚手架。本章将介绍与之相关的各种知识。 模块路径解析规则 我们已经知道,require函数支持斜杠(/)或盘符(C:)开头的绝对路径,也支持./开头的相对路径。但这两种路径在模块之间建立了强耦合关系,一旦某个模块文件的存放位置需要变更,使用该模块的其它模块的代码也需要跟着调整,变得牵一发动全身。因此,require函数支持第三种形式的路径,写法类似于foo/bar,并依次按照以下规则解析路径,直到找到模块位置。 内置模块 如果传递给require函数的是NodeJS内置模块名称,不做路径解析,直接返回内部模块的导出对象,例如require('fs')。 node_modules目录 NodeJS定义了一个特殊的node_modules目录用于存放模块。例如某个模块的绝对路径是/home/user/hello.js,在该模块中使用require('foo/bar')方式加载模块时,则NodeJS依次尝试使用以下路径。 /home/user/node_modules/foo/bar /home/node_modules/foo/bar /node_modules/foo/bar NODE_PATH环境变量 与PATH环境变量类似,NodeJS允许通过NODE_PATH环境变量来指定额外的模块搜索路径。NODE_PATH环境变量中包含一到多个目录路径,路径之间在Linux下使用:分隔,在Windows下使用;分隔。例如定义了以下NODE_PATH环境变量: NODE_PATH=/home/user/lib:/home/lib 当使用require('foo/bar')的方式加载模块时,则NodeJS依次尝试以下路径。 /home/user/lib/foo/bar /home/lib/foo/bar 包(package) 我们已经知道了JS模块的基本单位是单个JS文件,但复杂些的模块往往由多个子模块组成。为了便于管理和使用,我们可以把由多个子模块组成的大模块称做包,并把所有子模块放在同一个目录里。 在组成一个包的所有子模块中,需要有一个入口模块,入口模块的导出对象被作为包的导出对象。例如有以下目录结构。 - /home/user/lib/ - cat/ head.js body.js main.js 其中cat目录定义了一个包,其中包含了3个子模块。main.js作为入口模块,其内容如下: var head = require('./head'); var body = require('./body'); exports.create = function (name) { return { name: name, head: head.create(), body: body.create() }; }; 在其它模块里使用包的时候,需要加载包的入口模块。接着上例,使用require('/home/user/lib/cat/main')能达到目的,但是入口模块名称出现在路径里看上去不是个好主意。因此我们需要做点额外的工作,让包使用起来更像是单个模块。 index.js 当模块的文件名是index.js,加载模块时可以使用模块所在目录的路径代替模块文件路径,因此接着上例,以下两条语句等价。 var cat = require('/home/user/lib/cat'); var cat = require('/home/user/lib/cat/index'); 这样处理后,就只需要把包目录路径传递给require函数,感觉上整个目录被当作单个模块使用,更有整体感。 package.json 如果想自定义入口模块的文件名和存放位置,就需要在包目录下包含一个package.json文件,并在其中指定入口模块的路径。上例中的cat模块可以重构如下。 - /home/user/lib/ - cat/ + doc/ - lib/ head.js body.js main.js + tests/ package.json 其中package.json内容如下。 { "name": "cat", "main": "./lib/main.js" } 如此一来,就同样可以使用require('/home/user/lib/cat')的方式加载模块。NodeJS会根据包目录下的package.json找到入口模块所在位置。 命令行程序 使用NodeJS编写的东西,要么是一个包,要么是一个命令行程序,而前者最终也会用于开发后者。因此我们在部署代码时需要一些技巧,让用户觉得自己是在使用一个命令行程序。 例如我们用NodeJS写了个程序,可以把命令行参数原样打印出来。该程序很简单,在主模块内实现了所有功能。并且写好后,我们把该程序部署在/home/user/bin/node-echo.js这个位置。为了在任何目录下都能运行该程序,我们需要使用以下终端命令。 $ node /home/user/bin/node-echo.js Hello World Hello World 这种使用方式看起来不怎么像是一个命令行程序,下边的才是我们期望的方式。 $ node-echo Hello World Linux 在Linux系统下,我们可以把JS文件当作shell脚本来运行,从而达到上述目的,具体步骤如下: 在shell脚本中,可以通过#!注释来指定当前脚本使用的解析器。所以我们首先在node-echo.js文件顶部增加以下一行注释,表明当前脚本使用NodeJS解析。 #! /usr/bin/env node NodeJS会忽略掉位于JS模块首行的#!注释,不必担心这行注释是非法语句。 然后,我们使用以下命令赋予node-echo.js文件执行权限。 $ chmod +x /home/user/bin/node-echo.js 最后,我们在PATH环境变量中指定的某个目录下,例如在/usr/local/bin下边创建一个软链文件,文件名与我们希望使用的终端命令同名,命令如下: $ sudo ln -s /home/user/bin/node-echo.js /usr/local/bin/node-echo 这样处理后,我们就可以在任何目录下使用node-echo命令了。 Windows 在Windows系统下的做法完全不同,我们得靠.cmd文件来解决问题。假设node-echo.js存放在C:\Users\user\bin目录,并且该目录已经添加到PATH环境变量里了。接下来需要在该目录下新建一个名为node-echo.cmd的文件,文件内容如下: @node "C:\User\user\bin\node-echo.js" %* 这样处理后,我们就可以在任何目录下使用node-echo命令了。 工程目录 了解了以上知识后,现在我们可以来完整地规划一个工程目录了。以编写一个命令行程序为例,一般我们会同时提供命令行模式和API模式两种使用方式,并且我们会借助三方包来编写代码。除了代码外,一个完整的程序也应该有自己的文档和测试用例。因此,一个标准的工程目录都看起来像下边这样。 - /home/user/workspace/node-echo/ # 工程目录 - bin/ # 存放命令行相关代码 node-echo + doc/ # 存放文档 - lib/ # 存放API相关代码 echo.js - node_modules/ # 存放三方包 + argv/ + tests/ # 存放测试用例 package.json # 元数据文件 README.md # 说明文件 其中部分文件内容如下: /* bin/node-echo */ var argv = require('argv'), echo = require('../lib/echo'); console.log(echo(argv.join(' '))); /* lib/echo.js */ module.exports = function (message) { return message; }; /* package.json */ { "name": "node-echo", "main": "./lib/echo.js" } 以上例子中分类存放了不同类型的文件,并通过node_moudles目录直接使用三方包名加载模块。此外,定义了package.json之后,node-echo目录也可被当作一个包来使用。 NPM NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种: 允许用户从NPM服务器下载别人编写的三方包到本地使用。 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。 可以看到,NPM建立了一个NodeJS生态圈,NodeJS开发者和用户可以在里边互通有无。以下分别介绍这三种场景下怎样使用NPM。 下载三方包 需要使用三方包时,首先得知道有哪些包可用。虽然npmjs.org提供了个搜索框可以根据包名来搜索,但如果连想使用的三方包的名字都不确定的话,就请百度一下吧。知道了包名后,比如上边例子中的argv,就可以在工程目录下打开终端,使用以下命令来下载三方包。 $ npm install argv ... argv@0.0.2 node_modules\argv 下载好之后,argv包就放在了工程目录下的node_modules目录中,因此在代码中只需要通过require('argv')的方式就好,无需指定三方包路径。 以上命令默认下载最新版三方包,如果想要下载指定版本的话,可以在包名后边加上@<version>,例如通过以下命令可下载0.0.1版的argv。 $ npm install argv@0.0.1 ... argv@0.0.1 node_modules\argv 如果使用到的三方包比较多,在终端下一个包一条命令地安装未免太人肉了。因此NPM对package.json的字段做了扩展,允许在其中申明三方包依赖。因此,上边例子中的package.json可以改写如下: { "name": "node-echo", "main": "./lib/echo.js", "dependencies": { "argv": "0.0.2" } } 这样处理后,在工程目录下就可以使用npm install命令批量安装三方包了。更重要的是,当以后node-echo也上传到了NPM服务器,别人下载这个包时,NPM会根据包中申明的三方包依赖自动下载进一步依赖的三方包。例如,使用npm install node-echo命令时,NPM会自动创建以下目录结构。 - project/ - node_modules/ - node-echo/ - node_modules/ + argv/ ... ... 如此一来,用户只需关心自己直接使用的三方包,不需要自己去解决所有包的依赖关系。 安装命令行程序 从NPM服务上下载安装一个命令行程序的方法与三方包类似。例如上例中的node-echo提供了命令行使用方式,只要node-echo自己配置好了相关的package.json字段,对于用户而言,只需要使用以下命令安装程序。 $ npm install node-echo -g 参数中的-g表示全局安装,因此node-echo会默认安装到以下位置,并且NPM会自动创建好Linux系统下需要的软链文件或Windows系统下需要的.cmd文件。 - /usr/local/ # Linux系统下 - lib/node_modules/ + node-echo/ ... - bin/ node-echo ... ... - %APPDATA%\npm\ # Windows系统下 - node_modules\ + node-echo\ ... node-echo.cmd ... 发布代码 第一次使用NPM发布代码前需要注册一个账号。终端下运行npm adduser,之后按照提示做即可。账号搞定后,接着我们需要编辑package.json文件,加入NPM必需的字段。接着上边node-echo的例子,package.json里必要的字段如下。 { "name": "node-echo", # 包名,在NPM服务器上须要保持唯一 "version": "1.0.0", # 当前版本号 "dependencies": { # 三方包依赖,需要指定包名和版本号 "argv": "0.0.2" }, "main": "./lib/echo.js", # 入口模块位置 "bin" : { "node-echo": "./bin/node-echo" # 命令行程序名和主模块位置 } } 之后,我们就可以在package.json所在目录下运行npm publish发布代码了。 版本号 使用NPM下载和发布代码时都会接触到版本号。NPM使用语义版本号来管理代码,这里简单介绍一下。 语义版本号分为X.Y.Z三位,分别代表主版本号、次版本号和补丁版本号。当代码变更时,版本号按以下原则更新。 + 如果只是修复bug,需要更新Z位。 + 如果是新增了功能,但是向下兼容,需要更新Y位。 + 如果有大变动,向下不兼容,需要更新X位。 版本号有了这个保证后,在申明三方包依赖时,除了可依赖于一个固定版本号外,还可依赖于某个范围的版本号。例如"argv": "0.0.x"表示依赖于0.0.x系列的最新版argv。NPM支持的所有版本号范围指定方式可以查看官方文档。 灵机一点 除了本章介绍的部分外,NPM还提供了很多功能,package.json里也有很多其它有用的字段。除了可以在npmjs.org/doc/查看官方文档外,这里再介绍一些NPM常用命令。 NPM提供了很多命令,例如install和publish,使用npm help可查看所有命令。 使用npm help <command>可查看某条命令的详细帮助,例如npm help install。 在package.json所在目录下使用npm install . -g可先在本地安装当前命令行程序,可用于发布前的本地测试。 使用npm update <package>可以把当前目录下node_modules子目录里边的对应模块更新至最新版本。 使用npm update <package> -g可以把全局安装的对应命令行程序更新至最新版。 使用npm cache clear可以清空NPM本地缓存,用于对付使用相同版本号发布新版本代码的人。 使用npm unpublish <package>@<version>可以撤销发布自己发布过的某个版本代码。 小结 本章介绍了使用NodeJS编写代码前需要做的准备工作,总结起来有以下几点: 编写代码前先规划好目录结构,才能做到有条不紊。 稍大些的程序可以将代码拆分为多个模块管理,更大些的程序可以使用包来组织模块。 合理使用node_modules和NODE_PATH来解耦包的使用方式和物理路径。 使用NPM加入NodeJS生态圈互通有无。 想到了心仪的包名时请提前在NPM上抢注。 文件操作 让前端觉得如获神器的不是NodeJS能做网络编程,而是NodeJS能够操作文件。小至文件查找,大至代码编译,几乎没有一个前端工具不操作文件。换个角度讲,几乎也只需要一些数据处理逻辑,再加上一些文件操作,就能够编写出大多数前端工具。本章将介绍与之相关的NodeJS内置模块。 开门红 NodeJS提供了基本的文件操作API,但是像文件拷贝这种高级功能就没有提供,因此我们先拿文件拷贝程序练手。与copy命令类似,我们的程序需要能接受源文件路径与目标文件路径两个参数。 小文件拷贝 我们使用NodeJS内置的fs模块简单实现这个程序如下。 var fs = require('fs'); function copy(src, dst) { fs.writeFileSync(dst, fs.readFileSync(src)); } function main(argv) { copy(argv[0], argv[1]); } main(process.argv.slice(2)); 以上程序使用fs.readFileSync从源路径读取文件内容,并使用fs.writeFileSync将文件内容写入目标路径。 豆知识: process是一个全局变量,可通过process.argv获得命令行参数。由于argv[0]固定等于NodeJS执行程序的绝对路径,argv[1]固定等于主模块的绝对路径,因此第一个命令行参数从argv[2]这个位置开始。 大文件拷贝 上边的程序拷贝一些小文件没啥问题,但这种一次性把所有文件内容都读取到内存中后再一次性写入磁盘的方式不适合拷贝大文件,内存会爆仓。对于大文件,我们只能读一点写一点,直到完成拷贝。因此上边的程序需要改造如下。 var fs = require('fs'); function copy(src, dst) { fs.createReadStream(src).pipe(fs.createWriteStream(dst)); } function main(argv) { copy(argv[0], argv[1]); } main(process.argv.slice(2)); 以上程序使用fs.createReadStream创建了一个源文件的只读数据流,并使用fs.createWriteStream创建了一个目标文件的只写数据流,并且用pipe方法把两个数据流连接了起来。连接起来后发生的事情,说得抽象点的话,水顺着水管从一个桶流到了另一个桶。 API走马观花 我们先大致看看NodeJS提供了哪些和文件操作有关的API。这里并不逐一介绍每个API的使用方法,官方文档已经做得很好了。 Buffer(数据块) 官方文档: http://nodejs.org/api/buffer.html JS语言自身只有字符串数据类型,没有二进制数据类型,因此NodeJS提供了一个与String对等的全局构造函数Buffer来提供对二进制数据的操作。除了可以读取文件得到Buffer的实例外,还能够直接构造,例如: var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]); Buffer与字符串类似,除了可以用.length属性得到字节长度外,还可以用[index]方式读取指定位置的字节,例如: bin[0]; // => 0x68; Buffer与字符串能够互相转化,例如可以使用指定编码将二进制数据转化为字符串: var str = bin.toString('utf-8'); // => "hello" 或者反过来,将字符串转换为指定编码下的二进制数据: var bin = new Buffer('hello', 'utf-8'); // => <Buffer 68 65 6c 6c 6f> Buffer与字符串有一个重要区别。字符串是只读的,并且对字符串的任何修改得到的都是一个新字符串,原字符串保持不变。至于Buffer,更像是可以做指针操作的C语言数组。例如,可以用[index]方式直接修改某个位置的字节。 bin[0] = 0x48; 而.slice方法也不是返回一个新的Buffer,而更像是返回了指向原Buffer中间的某个位置的指针,如下所示。 [ 0x68, 0x65, 0x6c, 0x6c, 0x6f ] ^ ^ | | bin bin.slice(2) 因此对.slice方法返回的Buffer的修改会作用于原Buffer,例如: var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]); var sub = bin.slice(2); sub[0] = 0x65; console.log(bin); // => <Buffer 68 65 65 6c 6f> 也因此,如果想要拷贝一份Buffer,得首先创建一个新的Buffer,并通过.copy方法把原Buffer中的数据复制过去。这个类似于申请一块新的内存,并把已有内存中的数据复制过去。以下是一个例子。 var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]); var dup = new Buffer(bin.length); bin.copy(dup); dup[0] = 0x48; console.log(bin); // => <Buffer 68 65 6c 6c 6f> console.log(dup); // => <Buffer 48 65 65 6c 6f> 总之,Buffer将JS的数据处理能力从字符串扩展到了任意二进制数据。 Stream(数据流) 官方文档: http://nodejs.org/api/stream.html 当内存中无法一次装下需要处理的数据时,或者一边读取一边处理更加高效时,我们就需要用到数据流。NodeJS中通过各种Stream来提供对数据流的操作。 以上边的大文件拷贝程序为例,我们可以为数据来源创建一个只读数据流,示例如下: var rs = fs.createReadStream(pathname); rs.on('data', function (chunk) { doSomething(chunk); }); rs.on('end', function () { cleanUp(); }); 豆知识: Stream基于事件机制工作,所有Stream的实例都继承于NodeJS提供的EventEmitter。 上边的代码中data事件会源源不断地被触发,不管doSomething函数是否处理得过来。代码可以继续做如下改造,以解决这个问题。 var rs = fs.createReadStream(src); rs.on('data', function (chunk) { rs.pause(); doSomething(chunk, function () { rs.resume(); }); }); rs.on('end', function () { cleanUp(); }); 以上代码给doSomething函数加上了回调,因此我们可以在处理数据前暂停数据读取,并在处理数据后继续读取数据。 此外,我们也可以为数据目标创建一个只写数据流,示例如下: var rs = fs.createReadStream(src); var ws = fs.createWriteStream(dst); rs.on('data', function (chunk) { ws.write(chunk); }); rs.on('end', function () { ws.end(); }); 我们把doSomething换成了往只写数据流里写入数据后,以上代码看起来就像是一个文件拷贝程序了。但是以上代码存在上边提到的问题,如果写入速度跟不上读取速度的话,只写数据流内部的缓存会爆仓。我们可以根据.write方法的返回值来判断传入的数据是写入目标了,还是临时放在了缓存了,并根据drain事件来判断什么时候只写数据流已经将缓存中的数据写入目标,可以传入下一个待写数据了。因此代码可以改造如下: var rs = fs.createReadStream(src); var ws = fs.createWriteStream(dst); rs.on('data', function (chunk) { if (ws.write(chunk) === false) { rs.pause(); } }); rs.on('end', function () { ws.end(); }); ws.on('drain', function () { rs.resume(); }); 以上代码实现了数据从只读数据流到只写数据流的搬运,并包括了防爆仓控制。因为这种使用场景很多,例如上边的大文件拷贝程序,NodeJS直接提供了.pipe方法来做这件事情,其内部实现方式与上边的代码类似。 File System(文件系统) 官方文档: http://nodejs.org/api/fs.html NodeJS通过fs内置模块提供对文件的操作。fs模块提供的API基本上可以分为以下三类: 文件属性读写。 其中常用的有fs.stat、fs.chmod、fs.chown等等。 文件内容读写。 其中常用的有fs.readFile、fs.readdir、fs.writeFile、fs.mkdir等等。 底层文件操作。 其中常用的有fs.open、fs.read、fs.write、fs.close等等。 NodeJS最精华的异步IO模型在fs模块里有着充分的体现,例如上边提到的这些API都通过回调函数传递结果。以fs.readFile为例: fs.readFile(pathname, function (err, data) { if (err) { // Deal with error. } else { // Deal with data. } }); 如上边代码所示,基本上所有fs模块API的回调参数都有两个。第一个参数在有错误发生时等于异常对象,第二个参数始终用于返回API方法执行结果。 此外,fs模块的所有异步API都有对应的同步版本,用于无法使用异步操作时,或者同步操作更方便时的情况。同步API除了方法名的末尾多了一个Sync之外,异常对象与执行结果的传递方式也有相应变化。同样以fs.readFileSync为例: try { var data = fs.readFileSync(pathname); // Deal with data. } catch (err) { // Deal with error. } fs模块提供的API很多,这里不一一介绍,需要时请自行查阅官方文档。 Path(路径) 官方文档: http://nodejs.org/api/path.html 操作文件时难免不与文件路径打交道。NodeJS提供了path内置模块来简化路径相关操作,并提升代码可读性。以下分别介绍几个常用的API。 path.normalize 将传入的路径转换为标准路径,具体讲的话,除了解析路径中的.与..外,还能去掉多余的斜杠。如果有程序需要使用路径作为某些数据的索引,但又允许用户随意输入路径时,就需要使用该方法保证路径的唯一性。以下是一个例子: var cache = {}; function store(key, value) { cache[path.normalize(key)] = value; } store('foo/bar', 1); store('foo//baz//../bar', 2); console.log(cache); // => { "foo/bar": 2 } 坑出没注意: 标准化之后的路径里的斜杠在Windows系统下是\,而在Linux系统下是/。如果想保证任何系统下都使用/作为路径分隔符的话,需要用.replace(/\\/g, '/')再替换一下标准路径。 path.join 将传入的多个路径拼接为标准路径。该方法可避免手工拼接路径字符串的繁琐,并且能在不同系统下正确使用相应的路径分隔符。以下是一个例子: path.join('foo/', 'baz/', '../bar'); // => "foo/bar" path.extname 当我们需要根据不同文件扩展名做不同操作时,该方法就显得很好用。以下是一个例子: path.extname('foo/bar.js'); // => ".js" path模块提供的其余方法也不多,稍微看一下官方文档就能全部掌握。 遍历目录 遍历目录是操作文件时的一个常见需求。比如写一个程序,需要找到并处理指定目录下的所有JS文件时,就需要遍历整个目录。 递归算法 遍历目录时一般使用递归算法,否则就难以编写出简洁的代码。递归算法与数学归纳法类似,通过不断缩小问题的规模来解决问题。以下示例说明了这种方法。 function factorial(n) { if (n === 1) { return 1; } else { return n * factorial(n - 1); } } 上边的函数用于计算N的阶乘(N!)。可以看到,当N大于1时,问题简化为计算N乘以N-1的阶乘。当N等于1时,问题达到最小规模,不需要再简化,因此直接返回1。 陷阱: 使用递归算法编写的代码虽然简洁,但由于每递归一次就产生一次函数调用,在需要优先考虑性能时,需要把递归算法转换为循环算法,以减少函数调用次数。 遍历算法 目录是一个树状结构,在遍历时一般使用深度优先+先序遍历算法。深度优先,意味着到达一个节点后,首先接着遍历子节点而不是邻居节点。先序遍历,意味着首次到达了某节点就算遍历完成,而不是最后一次返回某节点才算数。因此使用这种遍历方式时,下边这棵树的遍历顺序是A > B > D > E > C > F。 A / \ B C / \ \ D E F 同步遍历 了解了必要的算法后,我们可以简单地实现以下目录遍历函数。 function travel(dir, callback) { fs.readdirSync(dir).forEach(function (file) { var pathname = path.join(dir, file); if (fs.statSync(pathname).isDirectory()) { travel(pathname, callback); } else { callback(pathname); } }); } 可以看到,该函数以某个目录作为遍历的起点。遇到一个子目录时,就先接着遍历子目录。遇到一个文件时,就把文件的绝对路径传给回调函数。回调函数拿到文件路径后,就可以做各种判断和处理。因此假设有以下目录: - /home/user/ - foo/ x.js - bar/ y.js z.css 使用以下代码遍历该目录时,得到的输入如下。 travel('/home/user', function (pathname) { console.log(pathname); }); ------------------------ /home/user/foo/x.js /home/user/bar/y.js /home/user/z.css 异步遍历 如果读取目录或读取文件状态时使用的是异步API,目录遍历函数实现起来会有些复杂,但原理完全相同。travel函数的异步版本如下。 function travel(dir, callback, finish) { fs.readdir(dir, function (err, files) { (function next(i) { if (i < files.length) { var pathname = path.join(dir, files[i]); fs.stat(pathname, function (err, stats) { if (stats.isDirectory()) { travel(pathname, callback, function () { next(i + 1); }); } else { callback(pathname, function () { next(i + 1); }); } }); } else { finish && finish(); } }(0)); }); } 这里不详细介绍异步遍历函数的编写技巧,在后续章节中会详细介绍这个。总之我们可以看到异步编程还是蛮复杂的。 文本编码 使用NodeJS编写前端工具时,操作得最多的是文本文件,因此也就涉及到了文件编码的处理问题。我们常用的文本编码有UTF8和GBK两种,并且UTF8文件还可能带有BOM。在读取不同编码的文本文件时,需要将文件内容转换为JS使用的UTF8编码字符串后才能正常处理。 BOM的移除 BOM用于标记一个文本文件使用Unicode编码,其本身是一个Unicode字符("\uFEFF"),位于文本文件头部。在不同的Unicode编码下,BOM字符对应的二进制字节如下: Bytes Encoding ---------------------------- FE FF UTF16BE FF FE UTF16LE EF BB BF UTF8 因此,我们可以根据文本文件头几个字节等于啥来判断文件是否包含BOM,以及使用哪种Unicode编码。但是,BOM字符虽然起到了标记文件编码的作用,其本身却不属于文件内容的一部分,如果读取文本文件时不去掉BOM,在某些使用场景下就会有问题。例如我们把几个JS文件合并成一个文件后,如果文件中间含有BOM字符,就会导致浏览器JS语法错误。因此,使用NodeJS读取文本文件时,一般需要去掉BOM。例如,以下代码实现了识别和去除UTF8 BOM的功能。 function readText(pathname) { var bin = fs.readFileSync(pathname); if (bin[0] === 0xEF && bin[1] === 0xBB && bin[2] === 0xBF) { bin = bin.slice(3); } return bin.toString('utf-8'); } GBK转UTF8 NodeJS支持在读取文本文件时,或者在Buffer转换为字符串时指定文本编码,但遗憾的是,GBK编码不在NodeJS自身支持范围内。因此,一般我们借助iconv-lite这个三方包来转换编码。使用NPM下载该包后,我们可以按下边方式编写一个读取GBK文本文件的函数。 var iconv = require('iconv-lite'); function readGBKText(pathname) { var bin = fs.readFileSync(pathname); return iconv.decode(bin, 'gbk'); } 单字节编码 有时候,我们无法预知需要读取的文件采用哪种编码,因此也就无法指定正确的编码。比如我们要处理的某些CSS文件中,有的用GBK编码,有的用UTF8编码。虽然可以一定程度可以根据文件的字节内容猜测出文本编码,但这里要介绍的是有些局限,但是要简单得多的一种技术。 首先我们知道,如果一个文本文件只包含英文字符,比如Hello World,那无论用GBK编码或是UTF8编码读取这个文件都是没问题的。这是因为在这些编码下,ASCII0~128范围内字符都使用相同的单字节编码。 反过来讲,即使一个文本文件中有中文等字符,如果我们需要处理的字符仅在ASCII0~128范围内,比如除了注释和字符串以外的JS代码,我们就可以统一使用单字节编码来读取文件,不用关心文件的实际编码是GBK还是UTF8。以下示例说明了这种方法。 1. GBK编码源文件内容: var foo = '中文'; 2. 对应字节: 76 61 72 20 66 6F 6F 20 3D 20 27 D6 D0 CE C4 27 3B 3. 使用单字节编码读取后得到的内容: var foo = '{乱码}{乱码}{乱码}{乱码}'; 4. 替换内容: var bar = '{乱码}{乱码}{乱码}{乱码}'; 5. 使用单字节编码保存后对应字节: 76 61 72 20 62 61 72 20 3D 20 27 D6 D0 CE C4 27 3B 6. 使用GBK编码读取后得到内容: var bar = '中文'; 这里的诀窍在于,不管大于0xEF的单个字节在单字节编码下被解析成什么乱码字符,使用同样的单字节编码保存这些乱码字符时,背后对应的字节保持不变。 NodeJS中自带了一种binary编码可以用来实现这个方法,因此在下例中,我们使用这种编码来演示上例对应的代码该怎么写。 function replace(pathname) { var str = fs.readFileSync(pathname, 'binary'); str = str.replace('foo', 'bar'); fs.writeFileSync(pathname, str, 'binary'); } 小结 本章介绍了使用NodeJS操作文件时需要的API以及一些技巧,总结起来有以下几点: 学好文件操作,编写各种程序都不怕。 如果不是很在意性能,fs模块的同步API能让生活更加美好。 需要对文件读写做到字节级别的精细控制时,请使用fs模块的文件底层操作API。 不要使用拼接字符串的方式来处理路径,使用path模块。 掌握好目录遍历和文件编码处理技巧,很实用。 网络操作 不了解网络编程的程序员不是好前端,而NodeJS恰好提供了一扇了解网络编程的窗口。通过NodeJS,除了可以编写一些服务端程序来协助前端开发和测试外,还能够学习一些HTTP协议与Socket协议的相关知识,这些知识在优化前端性能和排查前端故障时说不定能派上用场。本章将介绍与之相关的NodeJS内置模块。 开门红 NodeJS本来的用途是编写高性能Web服务器。我们首先在这里重复一下官方文档里的例子,使用NodeJS内置的http模块简单实现一个HTTP服务器。 var http = require('http'); http.createServer(function (request, response) { response.writeHead(200, { 'Content-Type': 'text-plain' }); response.end('Hello World\n'); }).listen(8124); 以上程序创建了一个HTTP服务器并监听8124端口,打开浏览器访问该端口http://127.0.0.1:8124/就能够看到效果。 豆知识: 在Linux系统下,监听1024以下端口需要root权限。因此,如果想监听80或443端口的话,需要使用sudo命令启动程序。 API走马观花 我们先大致看看NodeJS提供了哪些和网络操作有关的API。这里并不逐一介绍每个API的使用方法,官方文档已经做得很好了。 HTTP 官方文档: http://nodejs.org/api/http.html 'http'模块提供两种使用方式: 作为服务端使用时,创建一个HTTP服务器,监听HTTP客户端请求并返回响应。 作为客户端使用时,发起一个HTTP客户端请求,获取服务端响应。 首先我们来看看服务端模式下如何工作。如开门红中的例子所示,首先需要使用.createServer方法创建一个服务器,然后调用.listen方法监听端口。之后,每当来了一个客户端请求,创建服务器时传入的回调函数就被调用一次。可以看出,这是一种事件机制。 HTTP请求本质上是一个数据流,由请求头(headers)和请求体(body)组成。例如以下是一个完整的HTTP请求数据内容。 POST / HTTP/1.1 User-Agent: curl/7.26.0 Host: localhost Accept: */* Content-Length: 11 Content-Type: application/x-www-form-urlencoded Hello World 可以看到,空行之上是请求头,之下是请求体。HTTP请求在发送给服务器时,可以认为是按照从头到尾的顺序一个字节一个字节地以数据流方式发送的。而http模块创建的HTTP服务器在接收到完整的请求头后,就会调用回调函数。在回调函数中,除了可以使用request对象访问请求头数据外,还能把request对象当作一个只读数据流来访问请求体数据。以下是一个例子。 http.createServer(function (request, response) { var body = []; console.log(request.method); console.log(request.headers); request.on('data', function (chunk) { body.push(chunk); }); request.on('end', function () { body = Buffer.concat(body); console.log(body.toString()); }); }).listen(80); ------------------------------------ POST { 'user-agent': 'curl/7.26.0', host: 'localhost', accept: '*/*', 'content-length': '11', 'content-type': 'application/x-www-form-urlencoded' } Hello World HTTP响应本质上也是一个数据流,同样由响应头(headers)和响应体(body)组成。例如以下是一个完整的HTTP请求数据内容。 HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 11 Date: Tue, 05 Nov 2013 05:31:38 GMT Connection: keep-alive Hello World 在回调函数中,除了可以使用response对象来写入响应头数据外,还能把response对象当作一个只写数据流来写入响应体数据。例如在以下例子中,服务端原样将客户端请求的请求体数据返回给客户端。 http.createServer(function (request, response) { response.writeHead(200, { 'Content-Type': 'text/plain' }); request.on('data', function (chunk) { response.write(chunk); }); request.on('end', function () { response.end(); }); }).listen(80); 接下来我们看看客户端模式下如何工作。为了发起一个客户端HTTP请求,我们需要指定目标服务器的位置并发送请求头和请求体,以下示例演示了具体做法。 var options = { hostname: 'www.example.com', port: 80, path: '/upload', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }; var request = http.request(options, function (response) {}); request.write('Hello World'); request.end(); 可以看到,.request方法创建了一个客户端,并指定请求目标和请求头数据。之后,就可以把request对象当作一个只写数据流来写入请求体数据和结束请求。另外,由于HTTP请求中GET请求是最常见的一种,并且不需要请求体,因此http模块也提供了以下便捷API。 http.get('http://www.example.com/', function (response) {}); 当客户端发送请求并接收到完整的服务端响应头时,就会调用回调函数。在回调函数中,除了可以使用response对象访问响应头数据外,还能把response对象当作一个只读数据流来访问响应体数据。以下是一个例子。 http.get('http://www.example.com/', function (response) { var body = []; console.log(response.statusCode); console.log(response.headers); response.on('data', function (chunk) { body.push(chunk); }); response.on('end', function () { body = Buffer.concat(body); console.log(body.toString()); }); }); ------------------------------------ 200 { 'content-type': 'text/html', server: 'Apache', 'content-length': '801', date: 'Tue, 05 Nov 2013 06:08:41 GMT', connection: 'keep-alive' } <!DOCTYPE html> ... HTTPS 官方文档: http://nodejs.org/api/https.html https模块与http模块极为类似,区别在于https模块需要额外处理SSL证书。 在服务端模式下,创建一个HTTPS服务器的示例如下。 var options = { key: fs.readFileSync('./ssl/default.key'), cert: fs.readFileSync('./ssl/default.cer') }; var server = https.createServer(options, function (request, response) { // ... }); 可以看到,与创建HTTP服务器相比,多了一个options对象,通过key和cert字段指定了HTTPS服务器使用的私钥和公钥。 另外,NodeJS支持SNI技术,可以根据HTTPS客户端请求使用的域名动态使用不同的证书,因此同一个HTTPS服务器可以使用多个域名提供服务。接着上例,可以使用以下方法为HTTPS服务器添加多组证书。 server.addContext('foo.com', { key: fs.readFileSync('./ssl/foo.com.key'), cert: fs.readFileSync('./ssl/foo.com.cer') }); server.addContext('bar.com', { key: fs.readFileSync('./ssl/bar.com.key'), cert: fs.readFileSync('./ssl/bar.com.cer') }); 在客户端模式下,发起一个HTTPS客户端请求与http模块几乎相同,示例如下。 var options = { hostname: 'www.example.com', port: 443, path: '/', method: 'GET' }; var request = https.request(options, function (response) {}); request.end(); 但如果目标服务器使用的SSL证书是自制的,不是从颁发机构购买的,默认情况下https模块会拒绝连接,提示说有证书安全问题。在options里加入rejectUnauthorized: false字段可以禁用对证书有效性的检查,从而允许https模块请求开发环境下使用自制证书的HTTPS服务器。 URL 官方文档: http://nodejs.org/api/url.html 处理HTTP请求时url模块使用率超高,因为该模块允许解析URL、生成URL,以及拼接URL。首先我们来看看一个完整的URL的各组成部分。 href ----------------------------------------------------------------- host path --------------- ---------------------------- http: // user:pass @ host.com : 8080 /p/a/t/h ?query=string #hash ----- --------- -------- ---- -------- ------------- ----- protocol auth hostname port pathname search hash ------------ query 我们可以使用.parse方法来将一个URL字符串转换为URL对象,示例如下。 url.parse('http://user:pass@host.com:8080/p/a/t/h?query=string#hash'); /* => { protocol: 'http:', auth: 'user:pass', host: 'host.com:8080', port: '8080', hostname: 'host.com', hash: '#hash', search: '?query=string', query: 'query=string', pathname: '/p/a/t/h', path: '/p/a/t/h?query=string', href: 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash' } */ 传给.parse方法的不一定要是一个完整的URL,例如在HTTP服务器回调函数中,request.url不包含协议头和域名,但同样可以用.parse方法解析。 http.createServer(function (request, response) { var tmp = request.url; // => "/foo/bar?a=b" url.parse(tmp); /* => { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: '?a=b', query: 'a=b', pathname: '/foo/bar', path: '/foo/bar?a=b', href: '/foo/bar?a=b' } */ }).listen(80); .parse方法还支持第二个和第三个布尔类型可选参数。第二个参数等于true时,该方法返回的URL对象中,query字段不再是一个字符串,而是一个经过querystring模块转换后的参数对象。第三个参数等于true时,该方法可以正确解析不带协议头的URL,例如//www.example.com/foo/bar。 反过来,format方法允许将一个URL对象转换为URL字符串,示例如下。 url.format({ protocol: 'http:', host: 'www.example.com', pathname: '/p/a/t/h', search: 'query=string' }); /* => 'http://www.example.com/p/a/t/h?query=string' */ 另外,.resolve方法可以用于拼接URL,示例如下。 url.resolve('http://www.example.com/foo/bar', '../baz'); /* => http://www.example.com/baz */ Query String 官方文档: http://nodejs.org/api/querystring.html querystring模块用于实现URL参数字符串与参数对象的互相转换,示例如下。 querystring.parse('foo=bar&baz=qux&baz=quux&corge'); /* => { foo: 'bar', baz: ['qux', 'quux'], corge: '' } */ querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' }); /* => 'foo=bar&baz=qux&baz=quux&corge=' */ Zlib 官方文档: http://nodejs.org/api/zlib.html zlib模块提供了数据压缩和解压的功能。当我们处理HTTP请求和响应时,可能需要用到这个模块。 首先我们看一个使用zlib模块压缩HTTP响应体数据的例子。这个例子中,判断了客户端是否支持gzip,并在支持的情况下使用zlib模块返回gzip之后的响应体数据。 http.createServer(function (request, response) { var i = 1024, data = ''; while (i--) { data += '.'; } if ((request.headers['accept-encoding'] || '').indexOf('gzip') !== -1) { zlib.gzip(data, function (err, data) { response.writeHead(200, { 'Content-Type': 'text/plain', 'Content-Encoding': 'gzip' }); response.end(data); }); } else { response.writeHead(200, { 'Content-Type': 'text/plain' }); response.end(data); } }).listen(80); 接着我们看一个使用zlib模块解压HTTP响应体数据的例子。这个例子中,判断了服务端响应是否使用gzip压缩,并在压缩的情况下使用zlib模块解压响应体数据。 var options = { hostname: 'www.example.com', port: 80, path: '/', method: 'GET', headers: { 'Accept-Encoding': 'gzip, deflate' } }; http.request(options, function (response) { var body = []; response.on('data', function (chunk) { body.push(chunk); }); response.on('end', function () { body = Buffer.concat(body); if (response.headers['content-encoding'] === 'gzip') { zlib.gunzip(body, function (err, data) { console.log(data.toString()); }); } else { console.log(data.toString()); } }); }).end(); Net 官方文档: http://nodejs.org/api/net.html net模块可用于创建Socket服务器或Socket客户端。由于Socket在前端领域的使用范围还不是很广,这里先不涉及到WebSocket的介绍,仅仅简单演示一下如何从Socket层面来实现HTTP请求和响应。 首先我们来看一个使用Socket搭建一个很不严谨的HTTP服务器的例子。这个HTTP服务器不管收到啥请求,都固定返回相同的响应。 net.createServer(function (conn) { conn.on('data', function (data) { conn.write([ 'HTTP/1.1 200 OK', 'Content-Type: text/plain', 'Content-Length: 11', '', 'Hello World' ].join('\n')); }); }).listen(80); 接着我们来看一个使用Socket发起HTTP客户端请求的例子。这个例子中,Socket客户端在建立连接后发送了一个HTTP GET请求,并通过data事件监听函数来获取服务器响应。 var options = { port: 80, host: 'www.example.com' }; var client = net.connect(options, function () { client.write([ 'GET / HTTP/1.1', 'User-Agent: curl/7.26.0', 'Host: www.baidu.com', 'Accept: */*', '', '' ].join('\n')); }); client.on('data', function (data) { console.log(data.toString()); client.end(); }); 灵机一点 使用NodeJS操作网络,特别是操作HTTP请求和响应时会遇到一些惊喜,这里对一些常见问题做解答。 问: 为什么通过headers对象访问到的HTTP请求头或响应头字段不是驼峰的? 答: 从规范上讲,HTTP请求头和响应头字段都应该是驼峰的。但现实是残酷的,不是每个HTTP服务端或客户端程序都严格遵循规范,所以NodeJS在处理从别的客户端或服务端收到的头字段时,都统一地转换为了小写字母格式,以便开发者能使用统一的方式来访问头字段,例如headers['content-length']。 问: 为什么http模块创建的HTTP服务器返回的响应是chunked传输方式的? 答: 因为默认情况下,使用.writeHead方法写入响应头后,允许使用.write方法写入任意长度的响应体数据,并使用.end方法结束一个响应。由于响应体数据长度不确定,因此NodeJS自动在响应头里添加了Transfer-Encoding: chunked字段,并采用chunked传输方式。但是当响应体数据长度确定时,可使用.writeHead方法在响应头里加上Content-Length字段,这样做之后NodeJS就不会自动添加Transfer-Encoding字段和使用chunked传输方式。 问: 为什么使用http模块发起HTTP客户端请求时,有时候会发生socket hang up错误? 答: 发起客户端HTTP请求前需要先创建一个客户端。http模块提供了一个全局客户端http.globalAgent,可以让我们使用.request或.get方法时不用手动创建客户端。但是全局客户端默认只允许5个并发Socket连接,当某一个时刻HTTP客户端请求创建过多,超过这个数字时,就会发生socket hang up错误。解决方法也很简单,通过http.globalAgent.maxSockets属性把这个数字改大些即可。另外,https模块遇到这个问题时也一样通过https.globalAgent.maxSockets属性来处理。 小结 本章介绍了使用NodeJS操作网络时需要的API以及一些坑回避技巧,总结起来有以下几点: http和https模块支持服务端模式和客户端模式两种使用方式。 request和response对象除了用于读写头数据外,都可以当作数据流来操作。 url.parse方法加上request.url属性是处理HTTP请求时的固定搭配。 使用zlib模块可以减少使用HTTP协议时的数据传输量。 通过net模块的Socket服务器与客户端可对HTTP协议做底层操作。 小心踩坑。 进程管理 NodeJS可以感知和控制自身进程的运行环境和状态,也可以创建子进程并与其协同工作,这使得NodeJS可以把多个程序组合在一起共同完成某项工作,并在其中充当胶水和调度器的作用。本章除了介绍与之相关的NodeJS内置模块外,还会重点介绍典型的使用场景。 开门红 我们已经知道了NodeJS自带的fs模块比较基础,把一个目录里的所有文件和子目录都拷贝到另一个目录里需要写不少代码。另外我们也知道,终端下的cp命令比较好用,一条cp -r source/* target命令就能搞定目录拷贝。那我们首先看看如何使用NodeJS调用终端命令来简化目录拷贝,示例代码如下: var child_process = require('child_process'); var util = require('util'); function copy(source, target, callback) { child_process.exec( util.format('cp -r %s/* %s', source, target), callback); } copy('a', 'b', function (err) { // ... }); 从以上代码中可以看到,子进程是异步运行的,通过回调函数返回执行结果。 API走马观花 我们先大致看看NodeJS提供了哪些和进程管理有关的API。这里并不逐一介绍每个API的使用方法,官方文档已经做得很好了。 Process 官方文档: http://nodejs.org/api/process.html 任何一个进程都有启动进程时使用的命令行参数,有标准输入标准输出,有运行权限,有运行环境和运行状态。在NodeJS中,可以通过process对象感知和控制NodeJS自身进程的方方面面。另外需要注意的是,process不是内置模块,而是一个全局对象,因此在任何地方都可以直接使用。 Child Process 官方文档: http://nodejs.org/api/child_process.html 使用child_process模块可以创建和控制子进程。该模块提供的API中最核心的是.spawn,其余API都是针对特定使用场景对它的进一步封装,算是一种语法糖。 Cluster 官方文档: http://nodejs.org/api/cluster.html cluster模块是对child_process模块的进一步封装,专用于解决单进程NodeJS Web服务器无法充分利用多核CPU的问题。使用该模块可以简化多进程服务器程序的开发,让每个核上运行一个工作进程,并统一通过主进程监听端口和分发请求。 应用场景 和进程管理相关的API单独介绍起来比较枯燥,因此这里从一些典型的应用场景出发,分别介绍一些重要API的使用方法。 如何获取命令行参数 在NodeJS中可以通过process.argv获取命令行参数。但是比较意外的是,node执行程序路径和主模块文件路径固定占据了argv[0]和argv[1]两个位置,而第一个命令行参数从argv[2]开始。为了让argv使用起来更加自然,可以按照以下方式处理。 function main(argv) { // ... } main(process.argv.slice(2)); 如何退出程序 通常一个程序做完所有事情后就正常退出了,这时程序的退出状态码为0。或者一个程序运行时发生了异常后就挂了,这时程序的退出状态码不等于0。如果我们在代码中捕获了某个异常,但是觉得程序不应该继续运行下去,需要立即退出,并且需要把退出状态码设置为指定数字,比如1,就可以按照以下方式: try { // ... } catch (err) { // ... process.exit(1); } 如何控制输入输出 NodeJS程序的标准输入流(stdin)、一个标准输出流(stdout)、一个标准错误流(stderr)分别对应process.stdin、process.stdout和process.stderr,第一个是只读数据流,后边两个是只写数据流,对它们的操作按照对数据流的操作方式即可。例如,console.log可以按照以下方式实现。 function log() { process.stdout.write( util.format.apply(util, arguments) + '\n'); } 如何降权 在Linux系统下,我们知道需要使用root权限才能监听1024以下端口。但是一旦完成端口监听后,继续让程序运行在root权限下存在安全隐患,因此最好能把权限降下来。以下是这样一个例子。 http.createServer(callback).listen(80, function () { var env = process.env, uid = parseInt(env['SUDO_UID'] || process.getuid(), 10), gid = parseInt(env['SUDO_GID'] || process.getgid(), 10); process.setgid(gid); process.setuid(uid); }); 上例中有几点需要注意: 如果是通过sudo获取root权限的,运行程序的用户的UID和GID保存在环境变量SUDO_UID和SUDO_GID里边。如果是通过chmod +s方式获取root权限的,运行程序的用户的UID和GID可直接通过process.getuid和process.getgid方法获取。 process.setuid和process.setgid方法只接受number类型的参数。 降权时必须先降GID再降UID,否则顺序反过来的话就没权限更改程序的GID了。 如何创建子进程 以下是一个创建NodeJS子进程的例子。 var child = child_process.spawn('node', [ 'xxx.js' ]); child.stdout.on('data', function (data) { console.log('stdout: ' + data); }); child.stderr.on('data', function (data) { console.log('stderr: ' + data); }); child.on('close', function (code) { console.log('child process exited with code ' + code); }); 上例中使用了.spawn(exec, args, options)方法,该方法支持三个参数。第一个参数是执行文件路径,可以是执行文件的相对或绝对路径,也可以是根据PATH环境变量能找到的执行文件名。第二个参数中,数组中的每个成员都按顺序对应一个命令行参数。第三个参数可选,用于配置子进程的执行环境与行为。 另外,上例中虽然通过子进程对象的.stdout和.stderr访问子进程的输出,但通过options.stdio字段的不同配置,可以将子进程的输入输出重定向到任何数据流上,或者让子进程共享父进程的标准输入输出流,或者直接忽略子进程的输入输出。 进程间如何通讯 在Linux系统下,进程之间可以通过信号互相通信。以下是一个例子。 /* parent.js */ var child = child_process.spawn('node', [ 'child.js' ]); child.kill('SIGTERM'); /* child.js */ process.on('SIGTERM', function () { cleanUp(); process.exit(0); }); 在上例中,父进程通过.kill方法向子进程发送SIGTERM信号,子进程监听process对象的SIGTERM事件响应信号。不要被.kill方法的名称迷惑了,该方法本质上是用来给进程发送信号的,进程收到信号后具体要做啥,完全取决于信号的种类和进程自身的代码。 另外,如果父子进程都是NodeJS进程,就可以通过IPC(进程间通讯)双向传递数据。以下是一个例子。 /* parent.js */ var child = child_process.spawn('node', [ 'child.js' ], { stdio: [ 0, 1, 2, 'ipc' ] }); child.on('message', function (msg) { console.log(msg); }); child.send({ hello: 'hello' }); /* child.js */ process.on('message', function (msg) { msg.hello = msg.hello.toUpperCase(); process.send(msg); }); 可以看到,父进程在创建子进程时,在options.stdio字段中通过ipc开启了一条IPC通道,之后就可以监听子进程对象的message事件接收来自子进程的消息,并通过.send方法给子进程发送消息。在子进程这边,可以在process对象上监听message事件接收来自父进程的消息,并通过.send方法向父进程发送消息。数据在传递过程中,会先在发送端使用JSON.stringify方法序列化,再在接收端使用JSON.parse方法反序列化。 如何守护子进程 守护进程一般用于监控工作进程的运行状态,在工作进程不正常退出时重启工作进程,保障工作进程不间断运行。以下是一种实现方式。 /* daemon.js */ function spawn(mainModule) { var worker = child_process.spawn('node', [ mainModule ]); worker.on('exit', function (code) { if (code !== 0) { spawn(mainModule); } }); } spawn('worker.js'); 可以看到,工作进程非正常退出时,守护进程立即重启工作进程。 小结 本章介绍了使用NodeJS管理进程时需要的API以及主要的应用场景,总结起来有以下几点: 使用process对象管理自身。 使用child_process模块创建和管理子进程。 异步编程 NodeJS最大的卖点——事件机制和异步IO,对开发者并不是透明的。开发者需要按异步方式编写代码才用得上这个卖点,而这一点也遭到了一些NodeJS反对者的抨击。但不管怎样,异步编程确实是NodeJS最大的特点,没有掌握异步编程就不能说是真正学会了NodeJS。本章将介绍与异步编程相关的各种知识。 回调 在代码中,异步编程的直接体现就是回调。异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。我们首先可以看看以下代码。 function heavyCompute(n, callback) { var count = 0, i, j; for (i = n; i > 0; --i) { for (j = n; j > 0; --j) { count += 1; } } callback(count); } heavyCompute(10000, function (count) { console.log(count); }); console.log('hello'); -- Console ------------------------------ 100000000 hello 可以看到,以上代码中的回调函数仍然先于后续代码执行。JS本身是单线程运行的,不可能在一段代码还未结束运行时去运行别的代码,因此也就不存在异步执行的概念。 但是,如果某个函数做的事情是创建一个别的线程或进程,并与JS主线程并行地做一些事情,并在事情做完后通知JS主线程,那情况又不一样了。我们接着看看以下代码。 setTimeout(function () { console.log('world'); }, 1000); console.log('hello'); -- Console ------------------------------ hello world 这次可以看到,回调函数后于后续代码执行了。如同上边所说,JS本身是单线程的,无法异步执行,因此我们可以认为setTimeout这类JS规范之外的由运行环境提供的特殊函数做的事情是创建一个平行线程后立即返回,让JS主进程可以接着执行后续代码,并在收到平行进程的通知后再执行回调函数。除了setTimeout、setInterval这些常见的,这类函数还包括NodeJS提供的诸如fs.readFile之类的异步API。 另外,我们仍然回到JS是单线程运行的这个事实上,这决定了JS在执行完一段代码之前无法执行包括回调函数在内的别的代码。也就是说,即使平行线程完成工作了,通知JS主线程执行回调函数了,回调函数也要等到JS主线程空闲时才能开始执行。以下就是这么一个例子。 function heavyCompute(n) { var count = 0, i, j; for (i = n; i > 0; --i) { for (j = n; j > 0; --j) { count += 1; } } } var t = new Date(); setTimeout(function () { console.log(new Date() - t); }, 1000); heavyCompute(50000); -- Console ------------------------------ 8520 可以看到,本来应该在1秒后被调用的回调函数因为JS主线程忙于运行其它代码,实际执行时间被大幅延迟。 代码设计模式 异步编程有很多特有的代码设计模式,为了实现同样的功能,使用同步方式和异步方式编写的代码会有很大差异。以下分别介绍一些常见的模式。 函数返回值 使用一个函数的输出作为另一个函数的输入是很常见的需求,在同步方式下一般按以下方式编写代码: var output = fn1(fn2('input')); // Do something. 而在异步方式下,由于函数执行结果不是通过返回值,而是通过回调函数传递,因此一般按以下方式编写代码: fn2('input', function (output2) { fn1(output2, function (output1) { // Do something. }); }); 可以看到,这种方式就是一个回调函数套一个回调函多,套得太多了很容易写出>形状的代码。 遍历数组 在遍历数组时,使用某个函数依次对数据成员做一些处理也是常见的需求。如果函数是同步执行的,一般就会写出以下代码: var len = arr.length, i = 0; for (; i < len; ++i) { arr[i] = sync(arr[i]); } // All array items have processed. 如果函数是异步执行的,以上代码就无法保证循环结束后所有数组成员都处理完毕了。如果数组成员必须一个接一个串行处理,则一般按照以下方式编写异步代码: (function next(i, len, callback) { if (i < len) { async(arr[i], function (value) { arr[i] = value; next(i + 1, len, callback); }); } else { callback(); } }(0, arr.length, function () { // All array items have processed. })); 可以看到,以上代码在异步函数执行一次并返回执行结果后才传入下一个数组成员并开始下一轮执行,直到所有数组成员处理完毕后,通过回调的方式触发后续代码的执行。 如果数组成员可以并行处理,但后续代码仍然需要所有数组成员处理完毕后才能执行的话,则异步代码会调整成以下形式: (function (i, len, count, callback) { for (; i < len; ++i) { (function (i) { async(arr[i], function (value) { arr[i] = value; if (++count === len) { callback(); } }); }(i)); } }(0, arr.length, 0, function () { // All array items have processed. })); 可以看到,与异步串行遍历的版本相比,以上代码并行处理所有数组成员,并通过计数器变量来判断什么时候所有数组成员都处理完毕了。 异常处理 JS自身提供的异常捕获和处理机制——try..catch..,只能用于同步执行的代码。以下是一个例子。 function sync(fn) { return fn(); } try { sync(null); // Do something. } catch (err) { console.log('Error: %s', err.message); } -- Console ------------------------------ Error: object is not a function 可以看到,异常会沿着代码执行路径一直冒泡,直到遇到第一个try语句时被捕获住。但由于异步函数会打断代码执行路径,异步函数执行过程中以及执行之后产生的异常冒泡到执行路径被打断的位置时,如果一直没有遇到try语句,就作为一个全局异常抛出。以下是一个例子。 function async(fn, callback) { // Code execution path breaks here. setTimeout(function () { callback(fn()); }, 0); } try { async(null, function (data) { // Do something. }); } catch (err) { console.log('Error: %s', err.message); } -- Console ------------------------------ /home/user/test.js:4 callback(fn()); ^ TypeError: object is not a function at null._onTimeout (/home/user/test.js:4:13) at Timer.listOnTimeout [as ontimeout] (timers.js:110:15) 因为代码执行路径被打断了,我们就需要在异常冒泡到断点之前用try语句把异常捕获住,并通过回调函数传递被捕获的异常。于是我们可以像下边这样改造上边的例子。 function async(fn, callback) { // Code execution path breaks here. setTimeout(function () { try { callback(null, fn()); } catch (err) { callback(err); } }, 0); } async(null, function (err, data) { if (err) { console.log('Error: %s', err.message); } else { // Do something. } }); -- Console ------------------------------ Error: object is not a function 可以看到,异常再次被捕获住了。在NodeJS中,几乎所有异步API都按照以上方式设计,回调函数中第一个参数都是err。因此我们在编写自己的异步函数时,也可以按照这种方式来处理异常,与NodeJS的设计风格保持一致。 有了异常处理方式后,我们接着可以想一想一般我们是怎么写代码的。基本上,我们的代码都是做一些事情,然后调用一个函数,然后再做一些事情,然后再调用一个函数,如此循环。如果我们写的是同步代码,只需要在代码入口点写一个try语句就能捕获所有冒泡上来的异常,示例如下。 function main() { // Do something. syncA(); // Do something. syncB(); // Do something. syncC(); } try { main(); } catch (err) { // Deal with exception. } 但是,如果我们写的是异步代码,就只有呵呵了。由于每次异步函数调用都会打断代码执行路径,只能通过回调函数来传递异常,于是我们就需要在每个回调函数里判断是否有异常发生,于是只用三次异步函数调用,就会产生下边这种代码。 function main(callback) { // Do something. asyncA(function (err, data) { if (err) { callback(err); } else { // Do something asyncB(function (err, data) { if (err) { callback(err); } else { // Do something asyncC(function (err, data) { if (err) { callback(err); } else { // Do something callback(null); } }); } }); } }); } main(function (err) { if (err) { // Deal with exception. } }); 可以看到,回调函数已经让代码变得复杂了,而异步方式下对异常的处理更加剧了代码的复杂度。如果NodeJS的最大卖点最后变成这个样子,那就没人愿意用NodeJS了,因此接下来会介绍NodeJS提供的一些解决方案。 域(Domain) 官方文档: http://nodejs.org/api/domain.html NodeJS提供了domain模块,可以简化异步代码的异常处理。在介绍该模块之前,我们需要首先理解“域”的概念。简单的讲,一个域就是一个JS运行环境,在一个运行环境中,如果一个异常没有被捕获,将作为一个全局异常被抛出。NodeJS通过process对象提供了捕获全局异常的方法,示例代码如下 process.on('uncaughtException', function (err) { console.log('Error: %s', err.message); }); setTimeout(function (fn) { fn(); }); -- Console ------------------------------ Error: undefined is not a function 虽然全局异常有个地方可以捕获了,但是对于大多数异常,我们希望尽早捕获,并根据结果决定代码的执行路径。我们用以下HTTP服务器代码作为例子: function async(request, callback) { // Do something. asyncA(request, function (err, data) { if (err) { callback(err); } else { // Do something asyncB(request, function (err, data) { if (err) { callback(err); } else { // Do something asyncC(request, function (err, data) { if (err) { callback(err); } else { // Do something callback(null, data); } }); } }); } }); } http.createServer(function (request, response) { async(request, function (err, data) { if (err) { response.writeHead(500); response.end(); } else { response.writeHead(200); response.end(data); } }); }); 以上代码将请求对象交给异步函数处理后,再根据处理结果返回响应。这里采用了使用回调函数传递异常的方案,因此async函数内部如果再多几个异步函数调用的话,代码就变成上边这副鬼样子了。为了让代码好看点,我们可以在每处理一个请求时,使用domain模块创建一个子域(JS子运行环境)。在子域内运行的代码可以随意抛出异常,而这些异常可以通过子域对象的error事件统一捕获。于是以上代码可以做如下改造: function async(request, callback) { // Do something. asyncA(request, function (data) { // Do something asyncB(request, function (data) { // Do something asyncC(request, function (data) { // Do something callback(data); }); }); }); } http.createServer(function (request, response) { var d = domain.create(); d.on('error', function () { response.writeHead(500); response.end(); }); d.run(function () { async(request, function (data) { response.writeHead(200); response.end(data); }); }); }); 可以看到,我们使用.create方法创建了一个子域对象,并通过.run方法进入需要在子域中运行的代码的入口点。而位于子域中的异步函数回调函数由于不再需要捕获异常,代码一下子瘦身很多。 陷阱 无论是通过process对象的uncaughtException事件捕获到全局异常,还是通过子域对象的error事件捕获到了子域异常,在NodeJS官方文档里都强烈建议处理完异常后立即重启程序,而不是让程序继续运行。按照官方文档的说法,发生异常后的程序处于一个不确定的运行状态,如果不立即退出的话,程序可能会发生严重内存泄漏,也可能表现得很奇怪。 但这里需要澄清一些事实。JS本身的throw..try..catch异常处理机制并不会导致内存泄漏,也不会让程序的执行结果出乎意料,但NodeJS并不是存粹的JS。NodeJS里大量的API内部是用C/C++实现的,因此NodeJS程序的运行过程中,代码执行路径穿梭于JS引擎内部和外部,而JS的异常抛出机制可能会打断正常的代码执行流程,导致C/C++部分的代码表现异常,进而导致内存泄漏等问题。 因此,使用uncaughtException或domain捕获异常,代码执行路径里涉及到了C/C++部分的代码时,如果不能确定是否会导致内存泄漏等问题,最好在处理完异常后重启程序比较妥当。而使用try语句捕获异常时一般捕获到的都是JS本身的异常,不用担心上诉问题。 小结 本章介绍了JS异步编程相关的知识,总结起来有以下几点: 不掌握异步编程就不算学会NodeJS。 异步编程依托于回调来实现,而使用回调不一定就是异步编程。 异步编程下的函数间数据传递、数组遍历和异常处理与同步编程有很大差别。 使用domain模块简化异步代码的异常处理,并小心陷阱。 大示例 学习讲究的是学以致用和融会贯通。至此我们已经分别介绍了NodeJS的很多知识点,本章作为最后一章,将完整地介绍一个使用NodeJS开发Web服务器的示例。 需求 我们要开发的是一个简单的静态文件合并服务器,该服务器需要支持类似以下格式的JS或CSS文件合并请求。 http://assets.example.com/foo/??bar.js,baz.js 在以上URL中,??是一个分隔符,之前是需要合并的多个文件的URL的公共部分,之后是使用,分隔的差异部分。因此服务器处理这个URL时,返回的是以下两个文件按顺序合并后的内容。 /foo/bar.js /foo/baz.js 另外,服务器也需要能支持类似以下格式的普通的JS或CSS文件请求。 http://assets.example.com/foo/bar.js 以上就是整个需求。 第一次迭代 快速迭代是一种不错的开发方式,因此我们在第一次迭代时先实现服务器的基本功能。 设计 简单分析了需求之后,我们大致会得到以下的设计方案。 +---------+ +-----------+ +----------+ request -->| parse |-->| combine |-->| output |--> response +---------+ +-----------+ +----------+ 也就是说,服务器会首先分析URL,得到请求的文件的路径和类型(MIME)。然后,服务器会读取请求的文件,并按顺序合并文件内容。最后,服务器返回响应,完成对一次请求的处理。 另外,服务器在读取文件时需要有个根目录,并且服务器监听的HTTP端口最好也不要写死在代码里,因此服务器需要是可配置的。 实现 根据以上设计,我们写出了第一版代码如下。 var fs = require('fs'), path = require('path'), http = require('http'); var MIME = { '.css': 'text/css', '.js': 'application/javascript' }; function combineFiles(pathnames, callback) { var output = []; (function next(i, len) { if (i < len) { fs.readFile(pathnames[i], function (err, data) { if (err) { callback(err); } else { output.push(data); next(i + 1, len); } }); } else { callback(null, Buffer.concat(output)); } }(0, pathnames.length)); } function main(argv) { var config = JSON.parse(fs.readFileSync(argv[0], 'utf-8')), root = config.root || '.', port = config.port || 80; http.createServer(function (request, response) { var urlInfo = parseURL(root, request.url); combineFiles(urlInfo.pathnames, function (err, data) { if (err) { response.writeHead(404); response.end(err.message); } else { response.writeHead(200, { 'Content-Type': urlInfo.mime }); response.end(data); } }); }).listen(port); } function parseURL(root, url) { var base, pathnames, parts; if (url.indexOf('??') === -1) { url = url.replace('/', '/??'); } parts = url.split('??'); base = parts[0]; pathnames = parts[1].split(',').map(function (value) { return path.join(root, base, value); }); return { mime: MIME[path.extname(pathnames[0])] || 'text/plain', pathnames: pathnames }; } main(process.argv.slice(2)); 以上代码完整实现了服务器所需的功能,并且有以下几点值得注意: 使用命令行参数传递JSON配置文件路径,入口函数负责读取配置并创建服务器。 入口函数完整描述了程序的运行逻辑,其中解析URL和合并文件的具体实现封装在其它两个函数里。 解析URL时先将普通URL转换为了文件合并URL,使得两种URL的处理方式可以一致。 合并文件时使用异步API读取文件,避免服务器因等待磁盘IO而发生阻塞。 我们可以把以上代码保存为server.js,之后就可以通过node server.js config.json命令启动程序,于是我们的第一版静态文件合并服务器就顺利完工了。 另外,以上代码存在一个不那么明显的逻辑缺陷。例如,使用以下URL请求服务器时会有惊喜。 http://assets.example.com/foo/bar.js,foo/baz.js 经过分析之后我们会发现问题出在/被自动替换/??这个行为上,而这个问题我们可以到第二次迭代时再解决。 第二次迭代 在第一次迭代之后,我们已经有了一个可工作的版本,满足了功能需求。接下来我们需要从性能的角度出发,看看代码还有哪些改进余地。 设计 把map方法换成for循环或许会更快一些,但第一版代码最大的性能问题存在于从读取文件到输出响应的过程当中。我们以处理/??a.js,b.js,c.js这个请求为例,看看整个处理过程中耗时在哪儿。 发送请求 等待服务端响应 接收响应 ---------+----------------------+-------------> -- 解析请求 ------ 读取a.js ------ 读取b.js ------ 读取c.js -- 合并数据 -- 输出响应 可以看到,第一版代码依次把请求的文件读取到内存中之后,再合并数据和输出响应。这会导致以下两个问题: 当请求的文件比较多比较大时,串行读取文件会比较耗时,从而拉长了服务端响应等待时间。 由于每次响应输出的数据都需要先完整地缓存在内存里,当服务器请求并发数较大时,会有较大的内存开销。 对于第一个问题,很容易想到把读取文件的方式从串行改为并行。但是别这样做,因为对于机械磁盘而言,因为只有一个磁头,尝试并行读取文件只会造成磁头频繁抖动,反而降低IO效率。而对于固态硬盘,虽然的确存在多个并行IO通道,但是对于服务器并行处理的多个请求而言,硬盘已经在做并行IO了,对单个请求采用并行IO无异于拆东墙补西墙。因此,正确的做法不是改用并行IO,而是一边读取文件一边输出响应,把响应输出时机提前至读取第一个文件的时刻。这样调整后,整个请求处理过程变成下边这样。 发送请求 等待服务端响应 接收响应 ---------+----+-------------------------------> -- 解析请求 -- 检查文件是否存在 -- 输出响应头 ------ 读取和输出a.js ------ 读取和输出b.js ------ 读取和输出c.js 按上述方式解决第一个问题后,因为服务器不需要完整地缓存每个请求的输出数据了,第二个问题也迎刃而解。 实现 根据以上设计,第二版代码按以下方式调整了部分函数。 function main(argv) { var config = JSON.parse(fs.readFileSync(argv[0], 'utf-8')), root = config.root || '.', port = config.port || 80; http.createServer(function (request, response) { var urlInfo = parseURL(root, request.url); validateFiles(urlInfo.pathnames, function (err, pathnames) { if (err) { response.writeHead(404); response.end(err.message); } else { response.writeHead(200, { 'Content-Type': urlInfo.mime }); outputFiles(pathnames, response); } }); }).listen(port); } function outputFiles(pathnames, writer) { (function next(i, len) { if (i < len) { var reader = fs.createReadStream(pathnames[i]); reader.pipe(writer, { end: false }); reader.on('end', function() { next(i + 1, len); }); } else { writer.end(); } }(0, pathnames.length)); } function validateFiles(pathnames, callback) { (function next(i, len) { if (i < len) { fs.stat(pathnames[i], function (err, stats) { if (err) { callback(err); } else if (!stats.isFile()) { callback(new Error()); } else { next(i + 1, len); } }); } else { callback(null, pathnames); } }(0, pathnames.length)); } 可以看到,第二版代码在检查了请求的所有文件是否有效之后,立即就输出了响应头,并接着一边按顺序读取文件一边输出响应内容。并且,在读取文件时,第二版代码直接使用了只读数据流来简化代码。 第三次迭代 第二次迭代之后,服务器本身的功能和性能已经得到了初步满足。接下来我们需要从稳定性的角度重新审视一下代码,看看还需要做些什么。 设计 从工程角度上讲,没有绝对可靠的系统。即使第二次迭代的代码经过反复检查后能确保没有bug,也很难说是否会因为NodeJS本身,或者是操作系统本身,甚至是硬件本身导致我们的服务器程序在某一天挂掉。因此一般生产环境下的服务器程序都配有一个守护进程,在服务挂掉的时候立即重启服务。一般守护进程的代码会远比服务进程的代码简单,从概率上可以保证守护进程更难挂掉。如果再做得严谨一些,甚至守护进程自身可以在自己挂掉时重启自己,从而实现双保险。 因此在本次迭代时,我们先利用NodeJS的进程管理机制,将守护进程作为父进程,将服务器程序作为子进程,并让父进程监控子进程的运行状态,在其异常退出时重启子进程。 实现 根据以上设计,我们编写了守护进程需要的代码。 var cp = require('child_process'); var worker; function spawn(server, config) { worker = cp.spawn('node', [ server, config ]); worker.on('exit', function (code) { if (code !== 0) { spawn(server, config); } }); } function main(argv) { spawn('server.js', argv[0]); process.on('SIGTERM', function () { worker.kill(); process.exit(0); }); } main(process.argv.slice(2)); 此外,服务器代码本身的入口函数也要做以下调整。 function main(argv) { var config = JSON.parse(fs.readFileSync(argv[0], 'utf-8')), root = config.root || '.', port = config.port || 80, server; server = http.createServer(function (request, response) { ... }).listen(port); process.on('SIGTERM', function () { server.close(function () { process.exit(0); }); }); } 我们可以把守护进程的代码保存为daemon.js,之后我们可以通过node daemon.js config.json启动服务,而守护进程会进一步启动和监控服务器进程。此外,为了能够正常终止服务,我们让守护进程在接收到SIGTERM信号时终止服务器进程。而在服务器进程这一端,同样在收到SIGTERM信号时先停掉HTTP服务再正常退出。至此,我们的服务器程序就靠谱很多了。 第四次迭代 在我们解决了服务器本身的功能、性能和可靠性的问题后,接着我们需要考虑一下代码部署的问题,以及服务器控制的问题。 设计 一般而言,程序在服务器上有一个固定的部署目录,每次程序有更新后,都重新发布到部署目录里。而一旦完成部署后,一般也可以通过固定的服务控制脚本启动和停止服务。因此我们的服务器程序部署目录可以做如下设计。 - deploy/ - bin/ startws.sh killws.sh + conf/ config.json + lib/ daemon.js server.js 在以上目录结构中,我们分类存放了服务控制脚本、配置文件和服务器代码。 实现 按以上目录结构分别存放对应的文件之后,接下来我们看看控制脚本怎么写。首先是start.sh。 #!/bin/sh if [ ! -f "pid" ] then node ../lib/daemon.js ../conf/config.json & echo $! > pid fi 然后是killws.sh。 #!/bin/sh if [ -f "pid" ] then kill $(tr -d '\r\n' < pid) rm pid fi 于是这样我们就有了一个简单的代码部署目录和服务控制脚本,我们的服务器程序就可以上线工作了。 后续迭代 我们的服务器程序正式上线工作后,我们接下来或许会发现还有很多可以改进的点。比如服务器程序在合并JS文件时可以自动在JS文件之间插入一个;来避免一些语法问题,比如服务器程序需要提供日志来统计访问量,比如服务器程序需要能充分利用多核CPU,等等。而此时的你,在学习了这么久NodeJS之后,应该已经知道该怎么做了。 小结 本章将之前零散介绍的知识点串了起来,完整地演示了一个使用NodeJS开发程序的例子,至此我们的课程就全部结束了。以下是对新诞生的NodeJSer的一些建议。 要熟悉官方API文档。并不是说要熟悉到能记住每个API的名称和用法,而是要熟悉NodeJS提供了哪些功能,一旦需要时知道查询API文档的哪块地方。 要先设计再实现。在开发一个程序前首先要有一个全局的设计,不一定要很周全,但要足够能写出一些代码。 要实现后再设计。在写了一些代码,有了一些具体的东西后,一定会发现一些之前忽略掉的细节。这时再反过来改进之前的设计,为第二轮迭代做准备。 要充分利用三方包。NodeJS有一个庞大的生态圈,在写代码之前先看看有没有现成的三方包能节省不少时间。 不要迷信三方包。任何事情做过头了就不好了,三方包也是一样。三方包是一个黑盒,每多使用一个三方包,就为程序增加了一份潜在风险。并且三方包很难恰好只提供程序需要的功能,每多使用一个三方包,就让程序更加臃肿一些。因此在决定使用某个三方包之前,最好三思而后行。 原文链接:http://nqdeng.github.io/7-days-nodejs/#1
设置1:build project的时候,让编译器支持 三字母词。 项目文件右击--> Properties-->C/C++ Build--> Settings 如图设置: 再 Command line pattern 如图的位置加上 -ansi 或者 -trigraphs 即可。 现在可以尝试下 加 和 不加 这个参数下面代码的执行效果: printf("delete files? (are you really sure about this ??) "); ok,this work has been completed! good luck!
序列化对象在Java中 主要有两个目的,一个是钝化存储对象,另一个是通过网络传输对象。 后者是移动或者远程计算的基础。前者比较好办,对象存储之后,往往由同一个程序再读出, 对象在解析的时候不存在类加载的问题。后者比较麻烦,接收序列化对象的一端往往同发送端的类加载器环境不一样,很有可能找不到发送端才有的类代码,因此也 就无法反序列化对象,造成ClassNotFoundException。 Java中利用序列化机制进行移动/远程计算的一个机制是RMI。RMI客户端通过网络通信将序列化的参数对象传送给服务器端,服务器端将客户端传送来得 的参数反序列化之后,调用相应对象的方法,并将结果序列化之后传送给客户端,客户端收到序列化的结果后,再使用反序列化功能将结果对象还原返回给调用程 序。这是一般远程过程调用的基本原理,只是不同的远程过程调用采用的序列化和反序列化的方法不一样,更一般的远程过程调用系统比如Web服务(如SOAP 协议)或者Corba协议,则采用XML或者定义中间数据类型的模式,客户端和服务器端将调用参数和返回结果采用XML或者中间语言表达的方法实现类型系统的转化。 不像一般远程过程调用系统需要你自定义数据类型转化过程,Java的序列化机制将类描述和反射机制结合起来,实现了自动序列化的过程,降低了开发难度和复 杂度。为了能序列化和反序列化,序列化的双方必须知道(懂得)序列化的类的结构,这就是为甚么要在序列化和反序列化过程使用类加载器来加载所需的类。序列化流中保存了类的表述信息,比如类的名称,类的签名等,反序列化的一方在读取这些信息之后,就需要根据这些信息加载该类,以便通过反射生成对象,并将后续 的对象数据读取进来。 那么反序列化的一方如何加载对方的类呢?缺省的ObjectInputStream类获取调用栈上最近用户自定义的类加载器,如果没有用户自定义的类加载 器,则使用当前线程的ContextClassLoader。一般来说这个ContextClassLoader就是应用程序的 BootstrapClassLoader,它包括了JRE和应用程序的类路径。然而服务器方的类路经一般不可能覆盖客户端的类路径,这时就需要替换这个 类加载器,让它按照自定义的地方找类代码。 RMI替换了这个类加载器,它自定义的类加载器可以从指定的URL下载特定的类代码,并根据需要加载这个类,从而完成反序列化过程。所以熟悉RMI的朋友 就会发现为了实现一个RMI调用,过程是非常复杂的,你不仅要指定服务器端供应类代码的URL,还要指定客户端供应类代码的URL,毕竟服务器端和客户端 都不仅仅需要序列化,也需要反序列化,才能完成对象交换。代码从另一个位置,典型的是网络上,传输另一个地方被解析执行,这就是通常移动计算的来历。由于 代码是传自另一个位置,又会涉及到执行移动代码的安全问题。幸亏Java的安全系统设计的非常完美,它的模型完美的解决了移动计算的问题。 我最近帮朋友做的这个平台,一个基本机制就是远程过程调用。没有采用RMI机制,原因是RMI机制太过底层,对于一般Java程序员来说完成一个调用过程 非常痛苦。也没有采用封装RMI的形式,因为RMI的基本模型即接口和实现未免太过复杂,对于程序员要求还是过高。我们的原型是普通一个Java业务类, 通过自己写的动态代理封装出客户端和服务器端来。前面有篇文章:Java动态代理、ASM与AOP就是描述这个机制的。这样带来的开发模式时,程序员按照 桌面应用的模式开发软件,可以不用修改地部署到客户端/服务器端的这种模式上,将复杂的企业应用的开发和部署变成了简单的桌面软件的开发。 当然在写这个平台时候,我就遇到这个困惑,如何将应用程序的运行空间同平台的运行空间分离开,如何将A程序的运行空间同B程序的运行空间分离开?开始由于 这个问题没有搞清楚,结果使用了缺省的ObjectInputStrea类,使得平台进行序列化和反序列化过程中老是使用平台的类加载器,当要序列化和反 序列化应用程序的类对象时,n多的ClassNotFoundException就出来了。 可能我的脑筋当时就是转不过来了,死活没有想到用自定义的ObjectInputStream来取代JDK的ObjectInputStream,就是在 琢磨如何将ObjectInputStream的调用栈的类加载器替换成自己的。后来想清楚了,在使用自定义的ObjectInputStream的情况 是不可能的。今天天气不错,我喝了杯回到座位上时,突然想起来为什么不自己写ObjectInputStream呢,自己来控制序列化的过程,起码是部分 控制就已经足够,这样不就可以替换自己的了吗?^_^ 有了这个想法,实现就很容易了。我赶紧打开JDK的src.zip,研究ObjectInputStream,看哪些方法是可以覆盖来改变行为的。结果 是,其实这个问题很简单,只是我太固执。人家已经明明白白的写好了如果你继承这个方法会怎么怎么样改变行为,还有例子。 人要是固执起来,真是不可理喻。我差点没有撞到南墙上,总算还想起来可以绕过南墙过去。 如何解决,很简单,覆盖ObjectInputStream中的resolveClass方法。这个方法就是在反序列化过程中读取类描述后加载类而使用的。这儿你只要给它返回它的描述需要的类就行了,至于你怎么加载,全部在你的控制之下。 看了这个东西,顺便看了一下这个方法对应的ObjectOutputStream方法是annotateClass,它的意思是在序列化改类时如何描述改 类。缺省的实现是空。你可以覆盖它,加上你自己的东西,比如干脆将类代码放在这儿,防止对方找不到类。在ObjectInputStream的自定义类 中,你就可以将这个写类代码解析了使用。代码随着对象一起传输,再一次形象表述了所谓移动计算。 原文链接:http://blog.csdn.net/xiemk2005/article/details/5850449
在百度上怎么搜,就是没有找到满意的答案。后来把谷歌想起了,终于找到了想要的答案: http://plugins.jetbrains.com/plugin/6529?pr=idea 把这个下载下来,把jar文件放进 IntelliJ IDEA安装包中plugins文件夹,然后重启IntelliJ IDEA。 在菜单中 VCS --> SVN Disconnect,搞定。
很遗憾由于这篇博文是专门搜集各个开源项目的各种官方连接地址的,所以链接较多,csdn不允许保存。 请点击这里下载。 由于我的积分不多了,所以这个文档需要一个积分。。应该不多吧。。。确实没有积分的童鞋,可以向我索要:113+88+57+971@qq.com,一定发送。 就是不方便更新了。。我会尽量一个月更新一次。 终于找到个方法,把链接的 / 换成了 & 。 JDK 5文档:http:&&docs.oracle.com&javase&1.5.0&docs& JDK 6文档:http:&&docs.oracle.com&javase&6&docs& JDK 7文档:http:&&docs.oracle.com&javase&7&docs& JDK 8文档:http:&&download.java.net&jdk8&docs& JPA Binnary Code & Source Code:https:&&www.google.com&#q=java+persistence+2.1+source+code iBATIS、MyBatis官方下载地址:https:&&code.google.com&p&mybatis& Java EE Documents : http:&&www.oracle.com&technetwork&java&javaee&documentation&index.html Java EE 7 各种技术Specification官方文档: http:&&www.oracle.com&technetwork&java&javaee&tech&index.html Java EE 6 各种技术Specification官方文档: http:&&www.oracle.com&technetwork&java&javaee&tech&javaee6technologies-1955512.html Java EE 5 各种技术Specification官方文档: http:&&www.oracle.com&technetwork&java&javaee&tech&javaee5-jsp-135162.html CGLIB:http:&&sourceforge.net&projects&cglib&files& ; http:&&cglib.sourceforge.net& Commons-Digester : http:&&commons.apache.org&proper&commons-digester& Apache SVN服务器:http:&&svn.apache.org&repos&asf&tomcat&tc6.0.x&tags&TOMCAT_6_0_0& Ant Apache:http:&&ant.apache.org&bindownload.cgi 有个疑问:就是Oracle对Java EE 7 的API文档(http:&&docs.oracle.com&javaee&7&api& )都公布出来了,为什么就是不发布javaee的二进制包和源代码呢?这个无论是哪个实现厂商,都应该是一样的阿,是规范都嘛。难道不是这样的?想不通。。如果各个实现厂商的不一样,那如何称之为规范呢? MySql Community Server下载地址:http:&&dev.mysql.com&downloads&mysql& Java 虚拟机规范在线文档:http:&&docs.oracle.com&javase&specs&jvms&se7&html& Java语言和虚拟机规范: http:&&docs.oracle.com&javase&specs& Dom4J:http://sourceforge.net/projects/dom4j/
直接上代码,痛快点。 package algorithm; import junit.framework.TestCase; /** * Created by Rocky on 14-3-31. * 下午7:59 */ public class MyMaze extends TestCase { /** * @param maze 0表示可走,1表示不可走,2表示这步已走 * @param startI 入口横坐标 * @param startJ 入口竖坐标 * @param endI 出口横坐标 * @param endJ 出口竖坐标 */ public void resolveMaze(int[][] maze, int startI, int startJ, int endI, int endJ) { maze[startI][startJ] = 2; System.out.println("*************走一步***************"); printMaze(maze); if (startI == endI && startJ == endJ) { System.out.println("********************成功*******************"); printMaze(maze); } else { /*向右*/ if(startJ + 1 < maze[startI].length){ if(maze[startI][startJ+1]==0 ){ resolveMaze(maze, startI, startJ+1, endI, endJ); } } /*向下*/ if(startI + 1 < maze.length){ if(maze[startI+1][startJ]==0 ){ resolveMaze(maze, startI+1, startJ, endI, endJ); } } /*向上*/ if(startI -1 >= 0){ if(maze[startI-1][startJ]==0 ){ resolveMaze(maze, startI-1, startJ, endI, endJ); } } /*向左*/ if(startJ- 1 >=0){ if(maze[startI][startJ-1]==0 ){ resolveMaze(maze, startI, startJ-1, endI, endJ); } } } if (maze[startI][startJ] != 0) { maze[startI][startJ] = 0; } System.out.println("*************退一步***************"); printMaze(maze); } private void printMaze(int[][] maze) { for (int i = 0; i < maze.length; i++) { for (int j = 0; j < maze[i].length; j++) { if (maze[i][j] == 2) { System.out.print("*\t"); } else { System.out.print(maze[i][j] + "\t"); } } System.out.println(); } } public void test() { int maze[][] = { {0, 0, 3, 3, 3, 3, 3, 3, 3}, {3, 0, 0, 0, 0, 0, 0, 0, 3}, {3, 0, 3, 3, 0, 3, 3, 0, 3}, {3, 0, 3, 0, 0, 3, 0, 0, 3}, {3, 0, 3, 0, 3, 0, 3, 0, 3}, {3, 0, 0, 0, 0, 0, 3, 0, 3}, {3, 3, 0, 3, 3, 0, 3, 3, 3}, {3, 0, 0, 0, 0, 0, 0, 0, 0}, {3, 3, 3, 3, 3, 3, 3, 3, 0}}; resolveMaze(maze,0,0,8,8); } } 运行结果:
第一部分:A*算法简介 写这篇文章的初衷是应一个网友的要求,当然我也发现现在有关人工智能的中文站点实在太少,我在这里 抛砖引玉,希望大家都来热心的参与。 还是说正题,我先拿A*算法开刀,是因为A*在游戏中有它很典型的用法,是人工智能在游戏中的代表。 A*算法在人工智能中是一种典型的启发式搜索算法,为了说清楚A*算法,我看还是先说说何谓启发式算法。 一、何谓启发式搜索算法: 在说它之前先提提状态空间搜索。状态空间搜索,如果按专业点的说法就是将问题求解过程表现为从 初始状态到目标状态寻找这个路径的过程。通俗点说,就是在解一个问题时,找到一条解题的过程可以从 求解的开始到问题的结果(好象并不通俗哦)。由于求解问题的过程中分枝有很多,主要是求解过程中求 解条件的不确定性,不完备性造成的,使得求解的路径很多这就构成了一个图,我们说这个图就是状态空 间。问题的求解实际上就是在这个图中找到一条路径可以从开始到结果。这个寻找的过程就是状态空间搜 索。 常用的状态空间搜索有深度优先和广度优先。广度优先是从初始状态一层一层向下找,直到找到目标 为止。深度优先是按照一定的顺序前查找完一个分支,再查找另一个分支,以至找到目标为止。这两种算 法在数据结构书中都有描述,可以参看这些书得到更详细的解释。 前面说的广度和深度优先搜索有一个很大的缺陷就是他们都是在一个给定的状态空间中穷举。这在状 态空间不大的情况下是很合适的算法,可是当状态空间十分大,且不预测的情况下就不可取了。他的效率 实在太低,甚至不可完成。在这里就要用到启发式搜索了。 启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置 进行搜索直到目标。这样可以省略大量无畏的搜索路径,提到了效率。在启发式搜索中,对位置的估价是 十分重要的。采用了不同的估价可以有不同的效果。我们先看看估价是如何表示的。 启发中的估价是用估价函数表示的,如: f(n) = g(n) + h(n) 其中f(n) 是节点n的估价函数,g(n)实在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目 标节点最佳路径的估计代价。在这里主要是h(n)体现了搜索的启发信息,因为g(n)是已知的。如果说详细 点,g(n)代表了搜索的广度的优先趋势。但是当h(n) >> g(n)时,可以省略g(n),而提高效率。这些就深了, 不懂也不影响啦!我们继续看看何谓A*算法。 二、初识A*算法: 启发式搜索其实有很多的算法,比如:局部择优搜索法、最好优先搜索法等等。当然A*也是。这些算法 都使用了启发函数,但在具体的选取最佳搜索节点时的策略不同。象局部择优搜索法,就是在搜索的过程中 选取“最佳节点”后舍弃其他的兄弟节点,父亲节点,而一直得搜索下去。这种搜索的结果很明显,由于舍 弃了其他的节点,可能也把最好的节点都舍弃了,因为求解的最佳节点只是在该阶段的最佳并不一定是全局 的最佳。最好优先就聪明多了,他在搜索时,便没有舍弃节点(除非该节点是死节点),在每一步的估价中 都把当前的节点和以前的节点的估价值比较得到一个“最佳的节点”。这样可以有效的防止“最佳节点”的 丢失。那么A*算法又是一种什么样的算法呢?其实A*算法也是一种最好优先的算法。只不过要加上一些约束 条件罢了。由于在一些问题求解时,我们希望能够求解出状态空间搜索的最短路径,也就是用最快的方法求 解问题,A*就是干这种事情的!我们先下个定义,如果一个估价函数可以找出最短的路径,我们称之为可采 纳性。A*算法是一个可采纳的最好优先算法。A*算法的估价函数克表示为: f’(n) = g’(n) + h’(n) 这里,f’(n)是估价函数,g’(n)是起点到终点的最短路径值,h’(n)是n到目标的最断路经的启发值。由 于这个f’(n)其实是无法预先知道的,所以我们用前面的估价函数f(n)做近似。g(n)代替g’(n),但 g(n)>=g’(n) 才可(大多数情况下都是满足的,可以不用考虑),h(n)代替h’(n),但h(n)<=h’(n)才可(这一点特别的重 要)。可以证明应用这样的估价函数是可以找到最短路径的,也就是可采纳的。我们说应用这种估价函数的 最好优先算法就是A*算法。哈!你懂了吗?肯定没懂!接着看! 举一个例子,其实广度优先算法就是A*算法的特例。其中g(n)是节点所在的层数,h(n)=0,这种h(n)肯 定小于h’(n),所以由前述可知广度优先算法是一种可采纳的。实际也是。当然它是一种最臭的A*算法。 再说一个问题,就是有关h(n)启发函数的信息性。h(n)的信息性通俗点说其实就是在估计一个节点的值 时的约束条件,如果信息越多或约束条件越多则排除的节点就越多,估价函数越好或说这个算法越好。这就 是为什么广度优先算法的那么臭的原因了,谁叫它的h(n)=0,一点启发信息都没有。但在游戏开发中由于实 时性的问题,h(n)的信息越多,它的计算量就越大,耗费的时间就越多。就应该适当的减小h(n)的信息,即 减小约束条件。但算法的准确性就差了,这里就有一个平衡的问题。可难了,这就看你的了! 好了我的话也说得差不多了,我想你肯定是一头的雾水了,其实这是写给懂A*算法的同志看的。哈哈! 你还是找一本人工智能的书仔细看看吧!我这几百字是不足以将A*算法讲清楚的。只是起到抛砖引玉的作用 希望大家热情参与吗! 预知A*算法的应用,请看《深入A*算法》。 第二部分:深入A*算法———浅析A*算法在搜索最短路径中的应用 一、前言 在这里我将对A*算法的实际应用进行一定的探讨,并且举一个有关A*算法在最短路径搜索 的例子。值得注意的是这里并不对A*的基本的概念作介绍,如果你还对A*算法不清楚的话, 请看姊妹篇《初识A*算法》。 这里所举的例子是参考AMIT主页中的一个源程序,你可以在AMIT的站点上下载也可以在我 的站点上下载。你使用这个源程序时,应该遵守一定的公约。 二、A*算法的程序编写原理 我在《初识A*算法》中说过,A*算法是最好优先算法的一种。只是有一些约束条件而已。 我们先来看看最好优先算法是如何编写的吧。 如图有如下的状态空间:(起始位置是A,目标位置是P,字母后的数字表示节点的估价值) 搜索过程中设置两个表:OPEN和CLOSED。OPEN表保存了所有已生成而未考察的节点,CLOSED 表中记录已访问过的节点。算法中有一步是根据估价函数重排OPEN表。这样循环中的每一 步只考虑OPEN表中状态最好的节点。具体搜索过程如下: 1)初始状态: OPEN=[A5];CLOSED=[]; 2)估算A5,取得搜有子节点,并放入OPEN表中; OPEN=[B4,C4,D6];CLOSED=[A5] 3)估算B4,取得搜有子节点,并放入OPEN表中; OPEN=[C4,E5,F5,D6];CLOSED=[B4,A5] 4)估算C4;取得搜有子节点,并放入OPEN表中; OPEN=[H3,G4,E5,F5,D6];CLOSED=[C4,B4,A5] 5)估算H3,取得搜有子节点,并放入OPEN表中; OPEN=[O2,P3,G4,E5,F5,D6];CLOSED=H3C4,B4,A5] 6)估算O2,取得搜有子节点,并放入OPEN表中; OPEN=[P3,G4,E5,F5,D6];CLOSED=[O2,H3,C4,B4,A5] 7)估算P3,已得到解; 看了具体的过程,再看看伪程序吧。算法的伪程序如下: Best_First_Search() { Open = [起始节点]; Closed = []; while ( Open表非空 ) { 从Open中取得一个节点X,并从OPEN表中删除。 if (X是目标节点) { 求得路径PATH;返回路径PATH; } for (每一个X的子节点Y) { if( Y不在OPEN表和CLOSE表中 ) { 求Y的估价值;并将Y插入OPEN表中;//还没有排序 } else if( Y在OPEN表中 ) { if( Y的估价值小于OPEN表的估价值 ) 更新OPEN表中的估价值; } else //Y在CLOSE表中 { if( Y的估价值小于CLOSE表的估价值 ) { 更新CLOSE表中的估价值; 从CLOSE表中移出节点,并放入OPEN表中; } } 将X节点插入CLOSE表中; 按照估价值将OPEN表中的节点排序; }//end for }//end while }//end func 啊!伪程序出来了,写一个源程序应该不是问题了,依葫芦画瓢就可以。A*算法的程序与此 是一样的,只要注意估价函数中的g(n)的h(n)约束条件就可以了。不清楚的可以看看《初识A*算法》。好了,我们可以进入另一个重要的话题,用A*算法实现最短路径的搜索。在此之 前你最好认真的理解前面的算法。不清楚可以找我。我的Email在文章尾。 三、用A*算法实现最短路径的搜索 在游戏设计中,经常要涉及到最短路径的搜索,现在一个比较好的方法就是用A*算法进行设 计。他的好处我们就不用管了,反正就是好!^_* 注意下面所说的都是以 ClassAstar 这个程序为蓝本,你可以在这里下载这个程序。这个程 序是一个完整的工程。里面带了一个EXE文件。可以先看看。 先复习一下,A*算法的核心是估价函数f(n),它包括g(n)和h(n)两部分。g(n) 是已经走过的 代价,h(n)是n到目标的估计代价。在这个例子中g(n)表示在状态空间从起始节点到 n节点的 深度,h(n)表示n节点所在地图的位置到目标位置的直线距离。啊!一个是状态空间,一个是 实际的地图,不要搞错了。再详细点说,有一个物体A,在地图上的坐标是(xa,ya),A所要到
那些读过的感觉不错的技术书 主要是java方面的 《深入JAVA虚拟机 第二版》 从中可以了解到JVM工作原理,字节码执行过程等,虽然出版日期有些久远,部分内容或许有些过时,但仍然值得一读。 《Java虚拟机规范(Java SE 7)》 如果不太熟悉jvm,这个读起来非常乏味(只有规范,没有多余解释),如果读过上面那一本,再读这个,非常有亲切感。 《深入理解JAVA虚拟机 JVM高级特性与最佳实践》 国内JVM相关水平很高的一本书,作者周志明结合自己的理解与实践经验对JVM工作原理做了阐述。相比《深入JAVA虚拟机 第二版》,这本书讲到的内容比较新,包括最新的垃圾收集算法的介绍,常用编译期、运行期优化方式等,全书结合openjdk,不至枯燥乏味;但class文件结构的介绍似乎有些简单,初识此内容可能不易读懂。与《深入Java虚拟机 第二版》重复的内容并不多,值得一读。 《head first 设计模式》 设计模式入门首选,无论是《java与模式》《大话设计模式》还是《设计模式之禅》都比不上head first的通俗易懂与印象深刻。 《Java Concurrency in Practice》 并发大神力作,想了解java并发必读,理论非常强大。辅助读物《Programming Concurrency on the JVM》,当然这本和前者没有可比性,但此书偏实践、其主要目的在于讲解“Software Transaction Memory”,有其独到的一面,扩充知识面不错。 《精通正则表达式第三版》 如果想深入了解正则表达式,哪能不读这本书呢,java Pattern类的api中也有推荐此书,其权威毋庸置疑。中文版由余晟翻译,2012年5月出版了其自著的《正则指引》一书,但与前者相比还是有些差距,错误不少。 《Java核心技术》 不同于一般的Java入门书,不同点在于此书对api的讲解非常详细,细节部分颇多,举个例子,java对象序列化时会写些什么东西进文件,这本书就有详细的介绍。 《Java解惑》 挺有意思的一本书。它列举了许多平常不太注意到的各种问题。可能大部分时候我们不会碰到此类问题,但如果读过,一旦碰到,会留意这类问题。若读过《深入JAVA虚拟机 第二版》,对其中大部分例子是可以很快给出解释的。 《HTTP权威指南》 如果以前没有深入了解http,读了此书,会觉得以前了解的关于http相关的内容弱爆了。经典书籍,不用多说。中文版2012年9月才出来,姗姗来迟。翻译的还过得去,读着不费劲。 《Java Performance》 详解Hotspot调优,内容很细。中文版叫《Java性能优化权威指南》 原文链接:http://www.ticmy.com/?page_id=24
网上一搜一大把,搜出来的结果几乎都是我很崇敬的张孝祥老师写的这道题的思路,甚至有的直接把原文copy paste过来,没有一个用代码实现了的。于是自己琢磨了下,这里发布出来。虽然标题是一百亿,但实现结果可用于任意大整数。 直接上代码。这里只实现了大整数相加。有了这个,不难实现减、乘等其他操作。代码复制粘帖即可运行。 MyBigInteger.java import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Created by Rocky on 14-3-26. */ public class MyBigInteger { private char sign = '0'; // 0 表示正数 - 表示负数 private byte[] data; public MyBigInteger() { this.data = "0".getBytes(); } public MyBigInteger(String value) throws Exception { //正则表达式,输入字符串要求以 零个或一个 - 开头,其余都是数字 Pattern pattern = Pattern.compile("^-?\\d+$"); if (value == null || value.length() <= 0) { value = "0"; } Matcher matcher = pattern.matcher(value); if (!matcher.find()) { throw new Exception("the value is not a number string :" + value); } //获取字符串的第一个字符 char firstChar = value.charAt(0); //data应该保存的是第一个非0数字后的字符串 if (firstChar == '-') { //说明输入的是个负数 if (value.length() >=2) { sign = firstChar; value = value.substring(1); value = getTemp(value); //得到value中第一个非0后的子字符串。 } } else { value = getTemp(value); } this.data = value.getBytes(); } /** * 得到一个字符串第一个非0后的字符串,如果没有找到,则返回 "0" 。如:00003435534,则返回3435534 * @return */ private String getTemp(String value){ Pattern pattern = Pattern.compile("[^0]{1}"); Matcher matcher = pattern.matcher(value); if (matcher.find()) { value = value.substring(matcher.start()); } else { value = "0"; } return value; } public MyBigInteger add(MyBigInteger other) { MyBigInteger result = new MyBigInteger(); int thisLength = this.data.length; int otherLength = other.data.length; int shorterLength = thisLength > otherLength ? otherLength : thisLength; ArrayList<Byte> resultData = new ArrayList<Byte>(); int flag = 0; //表示相加时的 进位,或相减时的 借位 int i = thisLength - 1; int j = otherLength - 1; int k = shorterLength; //两个数的符号相同 if (other.sign == this.sign) { //从两个整数的个位开始依次相加 while (k > 0) { Integer temp = new Integer(new String(new byte[]{this.data[i]})) + new Integer(new String(new byte[]{other.data[j]})) + flag; flag = temp / 10; //相加结果超过10时的进位。没有超过10,进位为 0 resultData.add(0, ((temp % 10) + "").getBytes()[0]); //把相加结果保存起来 k--; i--; j--; } //把多出的位加入到结果中 if (i == -1) { while (j >= 0) { Integer temp = new Integer(new String(new byte[]{other.data[j]})) + flag; flag = temp / 10; resultData.add(0, ((temp % 10) + "").getBytes()[0]); j--; } } else if (j == -1) { while (i >= 0) { Integer temp = new Integer(new String(new byte[]{this.data[i]})) + flag; flag = temp / 10; resultData.add(0, ((temp % 10) + "").getBytes()[0]); i--; } } //最后把flag加进结果中 if (flag != 0) { for (byte by : (flag + "").getBytes()) { resultData.add(0, by); } } result.sign = other.sign; } else { //符号不同 if (thisLength > otherLength) { //说明this表示的整数绝对值大,所以最终结果的符号为this的符号 result.sign = this.sign; resultData = subtract(this.data, other.data); //执行减法 } else if (thisLength < otherLength) { //other表示的整数绝对值大,所以最终结果的符号为other的符号 result.sign = other.sign; resultData = subtract(other.data, this.data); } else { //如果两个数据的位数相同 Integer thisInt = 0; Integer otherInt = 0; //从第一位开始比较,直到两者不相等 for (int n = 0; n < thisLength; n++) { thisInt = new Integer(new String(new byte[]{this.data[n]})); otherInt = new Integer(new String(new byte[]{other.data[n]})); if (!thisInt.equals(otherInt)) { //注意这里要使用equals方法,因为这里需要比较的是两者的内容 break; } } //如果this的绝对值大 if (thisInt > otherInt) { result.sign = this.sign; resultData = subtract(this.data, other.data); } else { result.sign = other.sign; resultData = subtract(other.data, this.data); } } } result.data = new byte[resultData.size()]; for (int m = 0; m < resultData.size(); m++) { result.data[m] = resultData.get(m); } return result; } private ArrayList<Byte> subtract(byte[] larger, byte[] smaller) { ArrayList<Byte> resultData = new ArrayList<Byte>(); int flag = 0; int i = smaller.length - 1; int j = larger.length - 1; int k = smaller.length; while (k > 0) { Integer temp = new Integer(new String(new byte[]{larger[j]})) + flag - new Integer(new String(new byte[]{smaller[i]})); if (temp < 0) { //如果相减结果小于0,说明需要借位,则把flag置为 -1,以便下一位减去 flag = -1; temp += 10; } else { //如果大于零,需要把flag置为 0.不要忘记了 flag = 0; } resultData.add(0, (temp + "").getBytes()[0]); j--; i--; k--; } //下面的代码就不写注释了 while (j >= 0) { Integer temp = new Integer(new String(new byte[]{larger[j]})) + flag; if (temp < 0) { flag = -1; temp += 10; } else { flag = 0; } resultData.add(0, (temp + "").getBytes()[0]); j--; } return resultData; } @Override public String toString() { String str = new String(this.data); str = getTemp(str); if (sign == '-' && str !="0") { str = sign + str; } return str; } } MyBigIntegerTest.java import junit.framework.TestCase; import java.math.BigInteger; /** * Created by Rocky on 14-3-26. */ public class MyBigIntegerTest extends TestCase { public void test1() throws Exception { String a1 = "-5453450543044355356576980545345054545453453454344435353254545345054304435535657698087756454543454345454534534543444353532545453450543044355356454543454354353450136546534534545345345054353450136546534534545345345043044355356576980657698087756454543454354353450136546534534545345345054353450136546534534545345345043044355356576980877564545434543543534501877564545434543543534501"; String b1 = "4545453453454344435353254545345054304435535657698087756454543454354345454534534543444353532545453450543044355356576980877564545434545454534534564545434543543534501365465345345453453450543534501365465345345453453450430443553565769804344435353254545345054304435535657698087756454543454354353450136546534534545345345043543534501365465345345453453450534501365465345345453453450"; MyBigInteger a = new MyBigInteger(a1); MyBigInteger b = new MyBigInteger(b1); MyBigInteger c = a.add(b); System.out.println(c); BigInteger a2 = new BigInteger(a1); BigInteger b2 = new BigInteger(b1); BigInteger c2 = a2.add(b2); System.out.println(c2); System.out.println(c2.toString().equals(c.toString())); } } 结果:
1、将一整数逆序后放入一数组中(要求递归实现) void convert(int *result, int n) { if(n>=10) convert(result+1, n/10); *result = n%10; } int main(int argc, char* argv[]) { int n = 123456789, result[20]={}; convert(result, n); printf("%d:", n); for(int i=0; i<9; i++) printf("%d", result[i]); return getchar(); } 2、求高于平均分的学生学号及成绩(学号和成绩人工输入) double find(int total, int n) { int number, score, average; scanf("%d", &number); if(number != 0){ scanf("%d", &score); average = find(total+score, n+1); if(score >= average) printf("%d:%d/n", number, score); return average; }else{ printf("Average=%d/n", total/n); return total/n; } } int main(int argc, char* argv[]) { find(0, 0); return getchar(); } 3、递归实现回文判断(如:abcdedbca就是回文) int find(char *str, int n) { if(n<=1) return 1; else if(str[0]==str[n-1]) return find(str+1, n-2); else return 0; } int main(int argc, char* argv[]) { char *str = "abcdedcba"; printf("%s: %s/n", str, find(str, strlen(str)) ? "Yes" : "No"); return getchar(); } 4、组合问题(从M个不同字符中任取N个字符的所有组合) void find(char *source, char *result, int n) { if(n==1){ while(*source) printf("%s%c/n", result, *source++); }else{ int i, j; for(i=0; source[i] != 0; i++); for(j=0; result[j] != 0; j++); for(; i>=n; i--) { result[j] = *source++; result[j+1] = '/0'; find(source, result, n-1); } } } int main(int argc, char* argv[]) { int const n = 3; char *source = "ABCDE", result[n+1] = {0}; if(n>0 && strlen(source)>0 && n<=strlen(source)) find(source, result, 3); return getchar(); } 5、分解成质因数(如435234=251*17*17*3*2) void prim(int m, int n) { if(m>n){ while(m%n != 0) n++; m /= n; prim(m, n); printf("%d*", n); } } int main(int argc, char* argv[]) { int n = 435234; printf("%d=", n); prim(n, 2); return getchar(); } 6、寻找迷宫的一条出路(o:通路; X障碍) #define MAX_SIZE 8 int H[4] = {0, 1, 0, -1}; int V[4] = {-1, 0, 1, 0}; char Maze[MAX_SIZE][MAX_SIZE] = {{'X','X','X','X','X','X','X','X'}, {'o','o','o','o','o','X','X','X'}, {'X','o','X','X','o','o','o','X'}, {'X','o','X','X','o','X','X','o'}, {'X','o','X','X','X','X','X','X'}, {'X','o','X','X','o','o','o','X'}, {'X','o','o','o','o','X','o','o'}, {'X','X','X','X','X','X','X','X'}}; void FindPath(int X, int Y) { if(X == MAX_SIZE || Y == MAX_SIZE){ for(int i = 0; i < MAX_SIZE; i++) for(int j = 0; j < MAX_SIZE; j++) printf("%c%c", Maze[i][j], j < MAX_SIZE-1 ? ' ' : '/n'); }else for(int k = 0; k < 4; k++) if(X >= 0 && Y >= 0 && Y < MAX_SIZE && X < MAX_SIZE && 'o' == Maze[X][Y]){ Maze[X][Y] = ' '; FindPath(X+V[k], Y+H[k]); Maze[X][Y] ='o'; } } int main(int argc, char* argv[]) { FindPath(1,0); return getchar(); } 7、随机分配座位,共50个学生,使学号相邻的同学座位不能相邻(早些时候用C#写的,没有用C改写)。 static void Main(string[] args) { int Tmp = 0, Count = 50; int[] Seats = new int[Count]; bool[] Students = new bool[Count]; System.Random RandStudent=new System.Random(); Students[Seats[0]=RandStudent.Next(0,Count)]=true; for(int i = 1; i < Count; ) { Tmp=(int)RandStudent.Next(0,Count); if((!Students[Tmp])&&(Seats[i-1]-Tmp!=1) && (Seats[i-1] - Tmp) != -1){ Seats[i++] = Tmp; Students[Tmp] = true; } } foreach(int Student in Seats) System.Console.Write(Student + " "); System.Console.Read(); } 8、求网格中的黑点分布(有6*7的网格,在某些格子中有黑点,已知各行与各列中有黑点的点数之和) #define ROWS 6 #define COLS 7 int iPointsR[ROWS] = {2, 0, 4, 3, 4, 0}; // 各行黑点数和的情况 int iPointsC[COLS] = {4, 1, 2, 2, 1, 2, 1}; // 各列黑点数和的情况 int iCount, iFound; int iSumR[ROWS], iSumC[COLS], Grid[ROWS][COLS]; int Set(int iRowNo) { if(iRowNo == ROWS){ for(int iColNo=0; iColNo < COLS && iSumC[iColNo]==iPointsC[iColNo]; iColNo++) if(iColNo == COLS-1){ printf("/nNo.%d:/n", ++iCount); for(int i=0; i < ROWS; i++) for(int j=0; j < COLS; j++) printf("%d%c", Grid[i][j], (j+1) % COLS ? ' ' : '/n'); iFound = 1; // iFound = 1,有解 } }else{ for(int iColNo=0; iColNo < COLS; iColNo++) { if(iPointsR[iRowNo] == 0){ Set(iRowNo + 1); }else if(Grid[iRowNo][iColNo]==0){ Grid[iRowNo][iColNo] = 1; iSumR[iRowNo]++; iSumC[iColNo]++; if(iSumR[iRowNo]<iPointsR[iRowNo] && iSumC[iColNo]<=iPointsC[iColNo]) Set(iRowNo); else if(iSumR[iRowNo]==iPointsR[iRowNo] && iRowNo < ROWS) Set(iRowNo + 1); Grid[iRowNo][iColNo] = 0; iSumR[iRowNo]--; iSumC[iColNo]--; } } } return iFound; // 用于判断是否有解 } int main(int argc, char* argv[]) { if(!Set(0)) printf("Failure!"); return getchar(); } 9、有4种面值(面值为1, 4, 12, 21)的邮票很多枚,从中最多任取5张进行组合,求邮票最大连续组合值 #define N 5 #define M 5 int k, Found, Flag[N]; int Stamp[M] = {0, 1, 4, 12, 21}; // 在剩余张数n中组合出面值和Value int Combine(int n, int Value) { if(n >= 0 && Value == 0){ Found = 1; int Sum = 0; for(int i=0; i<N && Flag[i] != 0; i++){ Sum += Stamp[Flag[i]]; printf("%d ", Stamp[Flag[i]]); } printf("/tSum=%d/n/n", Sum); }else for(int i=1; i<M && !Found && n>0; i++) if(Value-Stamp[i] >= 0){ Flag[k++] = i; Combine(n-1, Value-Stamp[i]); Flag[--k] = 0; } return Found; } int main(int argc, char* argv[]) { for(int i=1; Combine(N, i); i++, Found=0); return getchar(); } 10、大整数数相乘的问题。 void Multiple(char A[], char B[], char C[]) { int TMP, In=0, LenA=-1, LenB=-1; while(A[++LenA] != '/0'); while(B[++LenB] != '/0'); int Index, Start = LenA + LenB - 1; for(int i=LenB-1; i>=0; i--) { Index = Start--; if(B[i] != '0'){ for(int In=0, j=LenA-1; j>=0; j--) { TMP = (C[Index]-'0') + (A[j]-'0') * (B[i] - '0') + In; C[Index--] = TMP % 10 + '0'; In = TMP / 10; } C[Index] = In + '0'; } } } int main(int argc, char* argv[]) { char A[] = "21839244444444448880088888889"; char B[] = "38888888888899999999999999988"; char C[sizeof(A) + sizeof(B) - 1]; for(int k=0; k<sizeof(C); k++) C[k] = '0'; C[sizeof(C)-1] = '/0'; Multiple(A, B, C); for(int i=0; C[i] != '/0'; i++) printf("%c", C[i]); return getchar(); } 11、求最大连续递增数字串(如“ads3sl456789DF3456ld345AA”中的“456789”) int GetSubString(char *strSource, char *strResult) { int iTmp=0, iHead=0, iMax=0; for(int Index=0, iLen=0; strSource[Index]; Index++) { if(strSource[Index] >= '0' && strSource[Index] <= '9' && strSource[Index-1] > '0' && strSource[Index] == strSource[Index-1]+1) { iLen++; // 连续数字的长度增1 }else{ // 出现字符或不连续数字 if(iLen > iMax) { iMax = iLen; iHead = iTmp; } // 该字符是数字,但数字不连续 if(strSource[Index] >= '0' && strSource[Index] <= '9'){ iTmp = Index; iLen = 1; } } } for(iTmp=0 ; iTmp < iMax; iTmp++) // 将原字符串中最长的连续数字串赋值给结果串 strResult[iTmp] = strSource[iHead++]; strResult[iTmp]='/0'; return iMax; // 返回连续数字的最大长度 } int main(int argc, char* argv[]) { char strSource[]="ads3sl456789DF3456ld345AA", char strResult[sizeof(strSource)]; printf("Len=%d, strResult=%s /nstrSource=%s/n", GetSubString(strSource, strResult), strResult, strSource); return getchar(); } 12、四个工人,四个任务,每个人做不同的任务需要的时间不同,求任务分配的最优方案。(2005年5月29日全国计算机软件资格水平考试——软件设计师的算法题)。 #include "stdafx.h" #define N 4 int Cost[N][N] = { {2, 12, 5, 32}, // 行号:任务序号,列号:工人序号 {8, 15, 7, 11}, // 每行元素值表示这个任务由不同工人完成所需要的时间 {24, 18, 9, 6}, {21, 1, 8, 28}}; int MinCost=1000; int Task[N], TempTask[N], Worker[N]; void Assign(int k, int cost) { if(k==N) { MinCost = cost; for(int i=0; i<N; i++) TempTask[i] = Task[i]; }else{ for(int i=0; i<N; i++){ if(Worker[i]==0 && cost+Cost[k][i] < MinCost) { Worker[i] = 1; Task[k] = i; Assign(k+1, cost+Cost[k][i]); Worker[i] = 0; Task[k] = 0; } } } } int main(int argc, char* argv[]) { Assign(0, 0); printf("最佳方案总费用=%d/n", MinCost); for(int i=0; i<N; i++) /* 输出最佳方案 */ printf("/t任务%d由工人%d来做:%d/n", i, TempTask[i], Cost[i][TempTask[i]]); return getchar(); } 13、八皇后问题(输出所有情况,不过有些结果只是旋转了90度而已)。哈哈:)回溯算法的典型例题 #define N 8 int Board[N][N]; int Valid(int i, int j) // 所下棋子有效性的严正 { int k = 1; for(k=1; i>=k && j>=k;k++) if(Board[i-k][j-k]) return 0; for(k=1; i>=k;k++) if(Board[i-k][j]) return 0; for(k=1; i>=k && j+k<N;k++) if(Board[i-k][j+k]) return 0; return 1; } void Trial(int i, int n) { if(i==n){ for(int k=0; k<n; k++){ for(int m=0; m<n; m++) printf("%d ", Board[k][m]); printf("/n"); } printf("/n"); }else{ for(int j=0; j<n; j++){ Board[i][j] = 1; if(Valid(i,j)) Trial(i+1, n); Board[i][j] = 0; } } } int main(int argc, char* argv[]) { Trial(0, N); return getchar(); } 14、实现strstr功能(寻找子串在父串中首次出现的位置) char * strstring(char *ParentString, char *SubString) { char *pSubString, *pPareString; for(char *pTmp=ParentString; *pTmp; pTmp++) { pSubString = SubString; pPareString = pTmp; while(*pSubString == *pPareString && *pSubString != '/0') { pSubString++; pPareString++; } if(*pSubString == '/0') return pTmp; } return NULL; } int main(int argc, char* argv[]) { char *ParentString = "happy birthday to you!"; char *SubString = "birthday"; printf("%s",strstring(ParentString, SubString)); return getchar(); }} 1,写出一个函数,比较两个字符串,返回最大公串,例如abacdaccbadc和cedaccbe返回daccb; 2,有100个数字,其中有正数也有负数,找出连续三个相加之和最大部分; 要求:尽量不要使用库函数! 两道一起来:支持搜索中文, import java.util.Random; public class Test { private static int maxSubStart = 0; private static int maxSubLength = 0; private static char[] c1, c2; private static boolean isSame(int i, int j) { return c1[i] == c2[j]; } private static void setMaxSub(int start1, int start2) { int i = start1, j = start2; int maxStart = 0; int maxLength = 0; for (; i < c1.length && j < c2.length; i++,j++) { if (isSame(i, j)) { maxLength++; } else break; } if (maxLength > maxSubLength) { maxSubLength = maxLength; maxSubStart = start1; } } private static String getMaxCommonString(String s1, String s2) { c1 = s1.toCharArray(); c2 = s2.toCharArray(); if (c1.length > c2.length) // swap s1, s2 so s1.length < s2.length { char[] c = c1; c1 = c2; c2 = c; } int minLength = c1.length; int maxLength = c2.length; for (int i = 0; i < minLength; i++) { char ch = c1[i]; for (int j = 0; j < maxLength; j++) { if (ch == c2[j]) { setMaxSub(i, j); } } } return new String(c1, maxSubStart, maxSubLength); } private static int[] getRandomInt(int length) { Random r = new Random(); int[] res = new int[length]; for (int i = 0; i < length; i++) { res[i] = r.nextInt(200) - 100; } return res; } private static int count(int[] num, int i) { return num[i]+num[i+1]+num[i+2]; } private static int getMaxThreeStart(int[] num) { int end = num.length - 3; int max = 0; int start = 0; for (int i = 0; i < end; i++) { int c = count(num, i); if (c > max) { max = c; start = i; } } return start; } public static void main(String[] args) { //abacdaccbadc和cedaccbe String s1 = "abacd测试汉字 accbadc", s2 = "ced测试汉字 accbe"; System.out.println(s1 + " " + s2 + " 的最大公串为: " + getMaxCommonString(s1, s2)); int[] num = getRandomInt(100); int start = getMaxThreeStart(num); for (int i = 0; i < 100; i++) { System.out.print(num[i] + " "); if (i%10 == 9) { System.out.println(); } } System.out.println("三个连续数字之和最大的三个数子为: " + num[start] + " " + num[start+1] + " " + num[start+2]); } }; 原文链接:http://blog.csdn.net/shiyanming1223/article/details/6862166
一.算法的基本概念 计算机解题的过程实际上是在实施某种算法,这种算法称为计算机算法。 1.算法的基本特征:可行性,确定性,有穷性,拥有足够的情报。 2.算法的基本要素:算法中对数据的运算和操作、算法的控制结构。 3.算法设计的基本方法:列举法、归纳法、递推、递归、减半递推技术、回溯法。 4.算法设计的要求:正确性、可读性、健壮性、效率与低存储量需求 二.算法的复杂度 1.算法的时间复杂度:指执行算法所需要的计算工作量 2.算法的空间复杂度:执行这个算法所需要的内存空间 三.数据结构的定义 1.数据的逻辑结构:反映数据元素之间的关系的数据元素集合的表示。数据的逻辑结构包括集合、线形结构、树形结构和图形结构四种。 2.数据的存储结构:数据的逻辑结构在计算机存储空间种的存放形式称为数据的存储结构。常用的存储结构有顺序、链接、索引等存储结构。 四.数据结构的图形表示: 在数据结构中,没有前件的结点称为根结点;没有后件的结点成为终端结点。插入和删除是对数据结构的两种基本运算。还有查找、分类、合并、分解、复制和修改等。 五.线性结构和非线性结构 根据数据结构中各数据元素之间前后件关系的复杂程度,一般将数据结构分为两大类型:线性结构和非线性结构。 线性结构:非空数据结构满足:有且只有一个根结点;每个结点最多有一个前件,最多只有一个后件。非线性结构:如果一个数据结构不是线性结构,称之为非线性结构。 常见的线性结构:线性表、栈、队列 六.线性表的定义 线 性表是n 个元素构成的有限序列(A1,A2,A3……)。表中的每一个数据元素,除了第一个以外,有且只有一个前件。除了最后一个以外有且只有一个后件。即线性表 是一个空表,或可以表示为(a1,a2,……an), 其中ai(I=1,2,……n)是属于数据对象的元素,通常也称其为线性表中的一个结点。 非空线性表有如下一些特征: (1)有且只有一个根结点a1,它无前件; (2)有且只有一个终端结点an,它无后件; (3)除根结点与终端结点外,其他所有结点有且只有一个前件,也有且只有一个后件。线性表中结点的个数n称为线性表的长度。当n=0时称为空表。 七.线性表的顺序存储结构 线性表的顺序表指的是用一组地址连续的存储单元依次存储线性表的数据元素。 线性表的顺序存储结构具备如下两个基本特征: 1.线性表中的所有元素所占的存储空间是连续的; 2.线性表中各数据元素在存储空间中是按逻辑顺序依次存放的。 即线性表逻辑上相邻、物理也相邻,则已知第一个元素首地址和每个元素所占字节数,则可求出任一个元素首地址。 假设线性表的每个元素需占用K个存储单元,并以所占的第一个单元的存储地址作为数据元素的存储位置。则线性表中第i+1个数据元素的存储位置LOC(ai+1)和第i个数据元素的存储位置LOC(ai)之间满足下列关系: LOC(ai+1)=LOC(ai)+K LOC(ai)=LOC(a1)+(i-1)*K ① 其中,LOC(a1)是线性表的第一个数据元素a1的存储位置,通常称做线性表的起始位置或基地址。 因为在顺序存储结构中,每个数据元素地址可以通过公式①计算得到,所以线性表的顺序存储结构是随机存取的存储结构。 在线性表的顺序存储结构下,可以对线性表做以下运算: 插入、删除、查找、排序、分解、合并、复制、逆转 八.顺序表的插入运算 线性表的插入运算是指在表的第I个位置上,插入一个新结点x,使长度为n的线性表(a1,a2 …ai…an)变成长度为n+1的线性表(a1,a2…x,ai…an). 该算法的时间主要花费在循环的结点后移语句上,执行次数是n-I+1。 当I=n+1,最好情况,时间复杂度o(1) 当I=1, 最坏情况,时间复杂度o(n) 算法的平均时间复杂度为o(n) 九.顺序表的删除运算 线性表的删除运算是指在表的第I个位置上,删除一个新结点x,使长度为n的线性表(a1,a2 …ai…an)变成长度为n-1的线性表(a1,a2…ai-1,ai+1…an). 当I=n,时间复杂度o(1),当I=1,时间复杂度o(n) ,平均时间复杂度为o(n) 十.栈及其基本运算 1. 什么是栈? 栈实际上也是一个线性表,只不过是一种特殊的线性表。栈是只能在表的一端进行插入和删除运算的线性表,通常称插入、删除这一端为栈顶(TOP),另一端为 栈底(BOTTOM)。当表中没有元素时称为空栈。栈顶元素总是后被插入的元素,从而也是最先被删除的元素;栈底元素总是最先被插入的元素,从而也是最后 才能被删除的元素。 假设栈S=(a1,a2,a3,……an),则a1 称为栈底元素,an称为栈顶元素。栈中元素按a1,a2,a3……an的次序进栈,退栈的第一个元素应该是栈顶元素。即后进先出。 2.栈的顺序存储及其运算 用S(1:M)作为栈的顺序存储空间。M为栈的最大容量。 栈的基本运算有三种:入栈、退栈与读栈顶元素。 入栈运算:在栈顶位置插入一个新元素。 首先将栈顶指针进一(TOP+1),然后将新元素插入到栈顶指针指向的位置。 退栈运算:指取出栈顶元素并赋给一个指定的变量。 首先将栈顶元素赋给一个指定的变量,然后将栈顶指针退一(TOP-1) 读栈顶元素:将栈顶元素赋给一个指定的变量。栈顶指针不会改变。 十一.队列及其基本运算 1.什么是队列 队列是只允许在一端删除,在另一端插入的顺序表,允许删除的一端叫做对头,允许插入的一端叫做对尾。 队列的修改是先进先出。往队尾插入一个元素成为入队运算。从对头删除一个元素称为退队运算。 2.循环队列及其运算 在 实际应用中,队列的顺序存储结构一般采用循环队列的形式。所谓循环队列,就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间。在循环 队列中,,用队尾指针rear指向队列中的队尾元素,用排头指针front指向排头元素的前一个位置,因此,从排头指针front指向的后一个位置直到队 尾指针 rear指向的位置之间所有的元素均为队列中的元素。 在实际使用循环队列时,为了能区分队满还是队列空,通常需要增加一个标志S: 队列空,则S=0,rear=front=m 队列满,则S=1,rear=front=m 循环队列主要有两种基本运算:入队运算和退队运算 n 入队运算 指在循环队列的队尾加入一个新元素,首先rear=rear+1,当rear=m+1时,置rear=1,然后将新元素插入到队尾指针指向的位置。当S=1,rear=front,说明队列已满,不能进行入队运算,称为“上溢”。 n 退队运算 指在循环队列的排头位置退出一个元素并赋给指定的变量。首先front=front+1,并当front=m+1时,置front=1,然后将排头指针指向的元素赋给指定的变量。当循环队列为空S=0,不能进行退队运算,这种情况成为“下溢”。 十二.线性单链表的结构及其基本运算 1.线性单链表的基本概念 一 组任意的存储单元存储线性表的数据元素,因此,为了表示每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储其本 身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。这两部分信息组成数据元素ai的存储映象,成为结点。它包括两个域:其中存储 数据元素信息的域称为数据域,存储直接后继存储位置的域称为指针域。指针域中存储的信息称做指针或链。N个结点链结成一个链表,即为线性表(a1, a2,……,an)的链式存储结构。又由于此链表的每个结点中只包含一个指针域,故又称线性链表或单链表。 有时,我们在单链表的第一个结点之前附设一个结点,称之为头结点,它指向表中第一个结点。头结点的数据域可以不存储任何信息,也可存储如线性表的长度等类的附加信息,头结点的指针域存储指向第一个结点的指针(即第一个元素结点的存储位置)。 在单链表中,取得第I个数据元素必须从头指针出发寻找,因此,单链表是非随机存取的存储结构 链表的形式:单向,双向 2.线性单链表的存储结构 3带链 3.带列的栈与队列 栈也是线性表,也可以采用链式存储结构。 队列也是线性表,也可以采用链式存储结构。 十三.线性链表的基本运算 1.线性链表的插入 2.线性链表的删除 十四.双向链表的结构及其基本运算 在双向链表的结点中有两个指针域,其一指向直接后继,另一指向直接前驱。 十五.循环链表的结构及其基本运算 是另一种形式的链式存储结构,它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。因此,从表中任一结点出发均可找到表中其他结点。 十六.树的定义 树是一种简单的非线性结构。树型结构的特点: 1.每个结点只有一个前件,称为父结点,没有前件的结点只有一个,称为树的根结点。 2.每一个结点可以有多个后件结点,称为该结点的子结点。没有后件的结点称为叶子结点 3.一个结点所拥有的后件个数称为树的结点度 4.树的最大层次称为树的深度。 十七.二叉树的定义及其基本性质 1.二叉树是另一种树型结构,它的特点是每个结点至多只有二棵子树(即二叉树中不存在度大于2的结点),并且,二叉树的子树有左右之分,其次序不能任意颠倒。 2.二叉树的基本性质 ①在二叉树的第I层上至多有2i-1个结点。 ②深度为k的二叉树至多有2k-1个结点(k>=1) ③在任意一个二叉树中,度为0的结点总是比度为2的结点多一个; ④具有n 个结点的二叉树,其深度至少为[log2n]+1。 一棵深度为k且有2k-1个结点的二叉树称为满二叉树。这种树的特点是每一层上的结点数都是最大结点数。 3.满二叉树与完全二叉树 满二叉树:除最后一层以外,每一层上的所有结点都有两个子结点。在满二叉树的第K层上有2K-1个结点,且深度为M的满二叉树右2M-1个结点 完全二叉树:除最后一层以外,每一层上的结点数均达到最大值;在最后一层上只缺少右边的若干结点。具有N个结点的完全二叉树的深度为[log2n]+1 完全二叉树总结点数为N, 若N为奇数,则叶子结点数为(N+1)/2 若N为偶数,则叶子结点数为N/2 4.二叉树的存储结构 二叉树通常采用链式存储结构 二叉树具有下列重要特性: 性质1 在二叉树的第i层上至多有2i-1个结点(i≥1)。 利用归纳法容易证得此性质。 i=1时,只有一个根结点。 显然,2i-1=20=1是对的。 现在假定对所有的j,1≤j<i,命题成立,即第j层上至多有2j-1个结点。那么,可以证明j=i时命题也成立。 由归纳假设:第i-1层上至多有2i-2个结点。由于二叉树的每个结点的度至多为 2,故在第i层上的最大结点数为第i-1层上的最大结点数的2倍,即2*2i-2=2i-1。 性质2 深度为k的二叉树至多有2k-1个结点,(k≥1)。 由性质1可见,深度为k的二叉树的最大结点数为 k k ∑(第i层上的最大结点数) =∑2i-1=2k -1 i=1 i=1 性质3 对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。 设n1为二叉树T中度为1的结点数。因为二叉树中所有结点的度均小于或等于2所以其结点总数为 n=n0+n1+n2 (6—1) 再看二叉树中的分支数。除了根结点外,其余结点都有一个分支进入,设B为分支总数,则n=B+1。由于这些分支是由度为1或2的结点射出的,所以又有B=n1+ 2n2。 n=n1+2*n2+1 (6—2) 于是得由式(6—1)和(6—2)得 n0=n2+1 十八.二叉树的遍历 就是遵从某种次序,访问二叉树中的所有结点,使得每个结点仅被访问一次。一般先左后右。 1.前序遍历DLR 首先访问根结点,然后遍历左子树,最后遍历右子树。 2.中序遍历LDR 首先遍历左子树,然后根结点,最后右子树 3.后序遍历LRD 首先遍历左子树,然后遍历右子树,最后访问根结点。 十九.顺序查找与二分查找 1.顺序查找 在两种情况下只能用顺序查找:线性表为无序表、链式存储结构的有序表 2.二分查找 只适用于顺序存储的有序表(从小到大)。 对于长度为N的有序线性表,在最坏情况下,二分查找只需要比较log2N次,而顺序查找要比较N次。 排序:指将一个无序序列整理成按值非递减顺序排列的有序序列。 二十.交换类排序法 冒泡排序与快速排序法属于交换类的排序方法 1.冒泡排序法 假设线性表的长度为N,则在最坏的情况下,冒跑排序需要经过N/2遍的从前往后的扫描和N/2遍的从后往前的扫描,需要的比较次数为N(N-1)/2 2.快速排序法 二十一.选择类排序法 1.简单选择排序法 2.堆排序法 二十三.插入类排序法 1.简单插入排序法2.希尔排序法 最坏情况下 最好情况下 说明 交换排序 冒泡排序 n(n-1)/2 最简单的交换排序。在待排序的元素序列基本有序的前提下,效率最高 快速排序 n(n-1)/2 O(Nlog2 N) 插入排序 简单插入排序 n(n-1)/2 每个元素距其最终位置不远时适用 希尔排序 O(n1.5) 选择排序 简单选择排序 n(n-1)/2 堆排序 O(nlog2n) 适用于较大规模的线性表 练习: 1.栈和队列的共同特点是(只允许在端点处插入和删除元素) 2.如果进栈序列为e1,e2,e3,e4,则可能的出栈序列是(e2,e4,e3,e1) 3.栈底至栈顶依次存放元素A、B、C、D,在第五个元素E入栈前,栈中元素可以出栈,则出栈序列可能是(DCBEA) 4.栈通常采用的两种存储结构是(线性存储结构和链表存储结构) 5.下列关于栈的叙述正确的是(D) A.栈是非线性结构B.栈是一种树状结构C.栈具有先进先出的特征D.栈有后进先出的特征 6.链表不具有的特点是(B)A.不必事先估计存储空间 B.可随机访问任一元素 C.插入删除不需要移动元素 D.所需空间与线性表长度成正比 7.用链表表示线性表的优点是(便于插入和删除操作) 8.在单链表中,增加头结点的目的是(方便运算的实现) 9.循环链表的主要优点是(从表中任一结点出发都能访问到整个链表) 10.线性表L=(a1,a2,a3,……ai,……an),下列说法正确的是(D) A.每个元素都有一个直接前件和直接后件 B.线性表中至少要有一个元素 C.表中诸元素的排列顺序必须是由小到大或由大到小 D.除第一个和最后一个元素外,其余每个元素都有一个且只有一个直接前件和直接后件 11.线性表若采用链式存储结构时,要求内存中可用存储单元的地址(D) A.必须是连续的 B.部分地址必须是连续的C.一定是不连续的 D.连续不连续都可以 12.线性表的顺序存储结构和线性表的链式存储结构分别是(随机存取的存储结构、顺序存取的存储结构) 13.树是结点的集合,它的根结点数目是(有且只有1) 14.在深度为5的满二叉树中,叶子结点的个数为(31) 15.具有3个结点的二叉树有(5种形态) 16.设一棵二叉树中有3个叶子结点,有8个度为1的结点,则该二叉树中总的结点数为(13) 17.已知二叉树后序遍历序列是dabec,中序遍历序列是debac,它的前序遍历序列是(cedba) 18.已知一棵二叉树前序遍历和中序遍历分别为ABDEGCFH和DBGEACHF,则该二叉树的后序遍历为(DGEBHFCA) 19.若某二叉树的前序遍历访问顺序是abdgcefh,中序遍历访问顺序是dgbaechf,则其后序遍历的结点访问顺序是(gdbehfca) 20.数据库保护分为:安全性控制、 完整性控制 、并发性控制和数据的恢复。 1. 在计算机中,算法是指(解题方案的准确而完整的描述) 2.在下列选项中,哪个不是一个算法一般应该具有的基本特征(无穷性) 说明:算法的四个基本特征是:可行性、确定性、有穷性和拥有足够的情报。 3. 算法一般都可以用哪几种控制结构组合而成(顺序、选择、循环) 4.算法的时间复杂度是指(算法执行过程中所需要的基本运算次数) 5. 算法的空间复杂度是指(执行过程中所需要的存储空间) 6. 算法分析的目的是(分析算法的效率以求改进) 7. 下列叙述正确的是(C) A.算法的执行效率与数据的存储结构无关 B.算法的空间复杂度是指算法程序中指令(或语句)的条数 C.算法的有穷性是指算法必须能在执行有限个步骤之后终止 D.算法的时间复杂度是指执行算法程序所需要的时间 8.数据结构作为计算机的一门学科,主要研究数据的逻辑结构、对各种数据结构进行的运算,以及(数据的存储结构) 9. 数据结构中,与所使用的计算机无关的是数据的(C) A.存储结构 B.物理结构 C.逻辑结构 D.物理和存储结构 10. 下列叙述中,错误的是(B) A.数据的存储结构与数据处理的效率密切相关 B.数据的存储结构与数据处理的效率无关 C.数据的存储结构在计算机中所占的空间不一定是连续的 D.一种数据的逻辑结构可以有多种存储结构 11. 数据的存储结构是指(数据的逻辑结构在计算机中的表示) 12. 数据的逻辑结构是指(反映数据元素之间逻辑关系的数据结构) 13. 根据数据结构中各数据元素之间前后件关系的复杂程度,一般将数据结构分为(线性结构和非线性结构) 14. 下列数据结构具有记忆功能的是(C)A.队列B.循环队列C.栈D.顺序表 15. 下列数据结构中,按先进后出原则组织数据的是(B) A.线性链表 B.栈 C.循环链表 D.顺序表 16. 递归算法一般需要利用(队列)实现。 17. 下列关于栈的叙述中正确的是(D)A.在栈中只能插入数据B.在栈中只能删除数据 C.栈是先进先出的线性表 D.栈是先进后出的线性表 18. 栈底至栈顶依次存放元素A、B、C、D,在第五个元素E入栈前,栈中元素可以出栈,则出栈序列可能是(DCBEA) 19.如果进栈序列为e1,e2,e3,e4,则可能的出栈序列是(e2,e4,e3,e1) 20. 由两个栈共享一个存储空间的好处是(节省存储空间,降低上溢发生的机率) 21. 应用程序在执行过程中,需要通过打印机输出数据时,一般先形成一个打印作业,将其存放在硬盘中的一个指定(队列)中,当打印机空闲时,就会按先来先服务的方式从中取出待打印的作业进行打印。 22.下列关于队列的叙述中正确的是(C)A.在队列中只能插入数据 B.在队列中只能删除数据 C.队列是先进先出的线性表 D.队列是先进后出的线性表 23.下列叙述中,正确的是(D)A.线性链表中的各元素在存储空间中的位置必须是连续的 B.线性链表中的表头元素一定存储在其他元素的前面 C.线性链表中的各元素在存储空间中的位置不一定是连续的,但表头元素一定存储在其他元素的前面 D.线性链表中的各元素在存储空间中的位置不一定是连续的,且各元素的存储顺序也是任意的 24.下列叙述中正确的是(A)A.线性表是线性结构 B.栈与队列是非线性结构 C.线性链表是非线性结构 D.二叉树是线性结构 25. 线性表L=(a1,a2,a3,……ai,……an),下列说法正确的是(D) A.每个元素都有一个直接前件和直接后件 B.线性表中至少要有一个元素 C.表中诸元素的排列顺序必须是由小到大或由大到小D.除第一个元素和最后一个元素外,其余每个元素都有一个且只有一个直接前件和直接后件 26.线性表若采用链式存储结构时,要求内存中可用存储单元的地址(连续不连续都可以) 27. 链表不具有的特点是(B)A.不必事先估计存储空间 B.可随机访问任一元素 C.插入删除不需要移动元素 D.所需空间与线性表长度成正比 28. 非空的循环单链表head的尾结点(由p所指向),满足(p->next=head) 29.与单向链表相比,双向链表的优点之一是(更容易访问相邻结点) 30. 在(D)中,只要指出表中任何一个结点的位置,就可以从它出发依次访问到表中其他所有结点。A.线性单链表 B.双向链表 C.线性链表 D.循环链表 31. 以下数据结构属于非线性数据结构的是(C)A.队列 B.线性表C.二叉树 D.栈 32.树是结点的集合,它的根结点数目是(有且只有1) 33.具有3个结点的二叉树有(5种形态) 34. 在一棵二叉树上第8层的结点数最多是(128) 注:2K-1 35. 在深度为5的满二叉树中,叶子结点的个数为(16) 注:2n-1 36. 在深度为5的满二叉树中,共有(31)个结点。 注:2n-1 37.设一棵完全二叉树共有699个结点,则在该二叉树中的叶子结点数为(350) 说明:完全二叉树总结点数为N,若N为奇数,则叶子结点数为(N+1)/2;若N为偶数,则叶子结点数为N/2。 38. 设有下列二叉树,对此二叉树中序遍历的结果是(B) A.ABCDEF B.DBEAFC C.ABDECF D.DEBFCA 39.已知二叉树后序遍历序列是dabec,中序遍历序列debac,它的前序遍历序列是(cedba) 40. 已知一棵二叉树前序遍历和中序遍历分别为ABDEGCFH和DBGEACHF,则该二叉树的后序遍历为(DGEBHFCA) 41.若某二叉树的前序遍历访问顺序是abdgcefh,中序遍历访问顺序是dgbaechf,则其后序遍历的结点访问顺序是(gdbehfca) 42. 串的长度是(串中所含字符的个数) 43.设有两个串p和q,求q在p中首次出现位置的运算称做(模式匹配) 44. N个顶点的连通图中边的条数至少为(N-1) 45.N个顶点的强连通图的边数至少有(N) 46.对长度为n的线性表进行顺序查找,在最坏情况下所需要的比较次数为(N) 47. 最简单的交换排序方法是(冒泡排序) 48.假设线性表的长度为n,则在最坏情况下,冒泡排序需要的比较次数为(n(n-1)/2) 49. 在待排序的元素序列基本有序的前提下,效率最高的排序方法是(冒泡排序) 50. 在最坏情况下,下列顺序方法中时间复杂度最小的是(堆排序) 51. 希尔排序法属于(插入类排序) 52. 堆排序法属于(选择类排序) 53. 在下列几种排序方法中,要求内存量最大的是(归并排序) 54. 已知数据表A中每个元素距其最终位置不远,为节省时间,应采用(直接插入排序) 55. 算法的基本特征是可行性、确定性、 有穷性 和拥有足够的情报。 1.一个算法通常由两种基本要素组成:一是对数据对象的运算和操作,二是算法的控制结构。 1. 算法的复杂度主要包括时间复杂度和 空间 复杂度。 2. 实现算法所需的存储单元多少和算法的工作量大小分别称为算法的空间复杂度和时间复杂度 。 3.所谓数据处理是指对数据集合中的各元素以各种方式进行运算,包括插入、删除、查找、更改等运算,也包括对数据元素进行分析。 4.数据结构是指相互有关联的 数据元素 的集合。 5.数据结构分为逻辑结构与存储结构,线性链表属于 存储结构 。 6.数据结构包括数据的 逻辑 结构和数据的存储结构。 7. 数据结构包括数据的逻辑结构、数据的 存储结构 以及对数据的操作运算。 8.数据元素之间的任何关系都可以用 前趋和后继 关系来描述。 9.数据的逻辑结构有线性结构和非线性结构两大类。 10.常用的存储结构有顺序、链接、 索引 等存储结构。 11. 顺序存储方法是把逻辑上相邻的结点存储在物理位置 相邻 的存储单元中。 12. 栈的基本运算有三种:入栈、退栈与读栈顶元素 。 13. 队列主要有两种基本运算:入队运算与 退队运算 。 14. 在实际应用中,带链的栈可以用来收集计算机存储空间中所有空闲的存储结点,这种带链的栈称为 可利用栈 。 15.栈和队列通常采用的存储结构是 链式存储和顺序存储 。 16.当线性表采用顺序存储结构实现存储时,其主要特点是 逻辑结构中相邻的结点在存储结构中仍相邻 。 17. 循环队列主要有两种基本运算:入队运算与退队运算。每进行一次入队运算,队尾指针就 进1 。 18.当循环队列非空且队尾指针等于对头指针时,说明循环队列已满,不能进行入队运算。这种情况称为 上溢 。 19.当循环队列为空时,不能进行退队运算,这种情况称为 下溢 。 20. 在一个容量为25的循环队列中,若头指针front=16,尾指针rear=9,则该循环队列中共有 18 个元素。注:当rear<front时,元素个数=总容量-(front-rear); 当rear>front时,元素个数=rear-front。 21. 在一个容量为15的循环队列中,若头指针front=6,尾指针rear=9,则该循环队列中共有3 个元素。 22.顺序查找一般是指在 线性表 中查找指定的元素。 23.在计算机中存放线性表,一种最简单的方法是 顺序存储 。 24.在程序设计语言中,通常定义一个 一维数组 来表示线性表的顺序存储空间。 25.在链式存储方式中,要求每个结点由两部分组成:一部分用于存放数据元素值,称为数据域,另一部分用于存放指针,称为 指针域 。其中指针用于指向该结点的前一个或后一个结点(即前件或后件)。 26.在 线性单链表中 ,每一个结点只有一个指针域,由这个指针只能找到后继结点,但不能找到前驱结点。 27. 为了要在线性链表中插入一个新元素,首先要给该元素分配一个 新结点 ,以便用于存储该元素的值。 28. 在线性链表中删除一个元素后,只需要改变被删除元素所在结点的前一个结点的 指针域 即可。 29. 用链表表示线性表的突出优点是 便于插入和删除操作 。 30. 在树形结构中,树根结点没有 前件 。 31. 在树结构中,一个结点所拥有的后件个数称为该结点的度。叶子结点的度为 0 。 32. 设一棵二叉树中有3个叶子结点,8个度为1的结点,则该二叉树中总的结点数为 13。 33. 设一棵完全二叉树共有739个结点,则在该二叉树中有 370 个叶子结点。 34. 设一棵完全二叉树共有700个结点,则在该二叉树中有 350 个叶子结点。 35. 在先左后右的原则下,根据访问根结点的次序,二叉树的遍历可以分为三种:前序遍历、 中序 遍历和后序遍历。 36. 若串S="Program",则其子串的数目是 29 。 注:n(n+1)/2+1 37. 若串S=”MathTypes”,则其子串的数目是 46 。 38. 对长度为n的线性表进行插入一个新元素或删除一个元素时,在最坏情况下所需要的比较次数为 n 。 39. 在长度为n的有序线性表中进行顺序查找。最坏的情况下,需要的比较次数为 n 。 40. 在长度为n的有序线性表中进行二分查找。最坏的情况下,需要的比较次数为 log2n 。 41. 长度为n的顺序存储线性表中,当在任何位置上插入一个元素概率都相等时,插入一个元素所需移动元素的平均个数为 n/2 。 42. 排序是计算机程序设计中的一种重要操作,常见的排序方法有插入排序、 交换排序 和选择排序等。 43. 快速排序法可以实现通过一次交换而消除多个 逆序 。 44. 快速排序法的关键是对线性表进行 分割 。 45. 冒泡排序算法在最好的情况下的元素交换次数为 0 。 46. 在最坏情况下,冒泡排序的时间复杂度为 n(n-1) /2 。 47. 对于长度为n的线性表,在最坏情况下,快速排序所需要的比较次数为 n(n-1) /2 。 48.在最坏情况下,简单插入排序需要比较的次数为 n(n-1) /2 。 49.在最坏情况下,希尔排序需要比较的次数为 O(n1.5) 。注:括号里是n的1.5次方。 50. 在最坏情况下,简单选择排序需要比较的次数为 n(n-1) /2 。 51. 在最坏情况下,堆排序需要比较的次数为 o(nlog2n) 。 52.对于输入为N个数进行快速排序算法的平均时间复杂度是 O(Nlog2 N)。 原文链接:http://blog.chinaunix.net/uid-20767210-id-1849740.html
Problem 1 : Is it a loop ? (判断链表是否有环?) Assume that wehave a head pointer to a link-list. Also assumethat we know the list is single-linked. Can you come up an algorithm to checkwhether this link list includes a loop by using O(n) time and O(1) space wheren is the length of the list? Furthermore, can you do so with O(n) time and onlyone register? 方法:使用两个指针,从头开始,一个一次前进一个节点,一个前进2个节点,则最多2N,后两个指针可以重合;如果无环,则正常停止。 同样的,可以找到链表的中间节点。同上。 Problem 2:设计一个复杂度为n的算法找到链表倒数第m个元素。最后一个元素假定是倒数第0个。 提示:双指针查找 Problem 3:用最简单的方法判断一个LONG整形的数A是2^n(2的n次方) 提示:x&(x-1), (下面分析出自:WilsonPeng3,并非来自文章原文。 如果A是2^n,则A的二进制肯定只有一个1,且后面全是零。则A-1的二进制则全是1组成。那么A&(A-1)肯定等于零。 如果A不是2^n,则A的二进制中,至少有两个1,那么A减去1后,借位时顶多借到第一个1的位置(从右往左数),也就是说再往左走,肯定存在一个位置,使得A的二进制在这个位置上是1,同时A-1的二进制在这个位置上也是1,那么A&(A-1)就肯定不等于零。 综上: A=2^n 能推出 A&(A-1)=零。 根据逆否命题知:A&(A-1)!=零 能推出 A!=2^n; A!=2^n 能推出 A&(A-1)!=零。 同上知:A&(A-1)=零 能退出 A=2^n; ) Problem 4:两个烧杯,一个放糖一个放盐,用勺子舀一勺糖到盐,搅拌均匀,然后舀一勺混合物会放糖的烧杯,问你两个烧杯哪个杂质多? 提示:相同。假设杂质不等,那么将杂质放回原杯中,则杯中物体重量必变化,不合理。 Problem 5:给你a、b两个文件,各存放50亿条url,每条url各占用64字节,内存限制是4G,让你找出a、b文件共同的url。 法1:使用hash表。使用a中元素创建hash表,hash控制在适当规模。在hash中查找b的元素,找不到的url先存在新文件中,下次查找。如果找到,则将相应的hash表项删除,当hash表项少于某个阈值时,将a中新元素重新hash。再次循环。 法2:对于hash表项增加一项记录属于的文件a,b。只要不存在的表项即放入hash表中,一致的项则删除。注意:可能存在很多重复项,引起插入,删除频繁。 (下面分析出自:WilsonPeng3,并非来自文章原文。 可以将两个打文件,hash成1024个小文件。一般情况下,初步计算,可以知道每个小文件不会超过400M。不会超出内存。这样可分别得到a-1.....a-1024和b-1.....b-1024个小文件。如果a、b中存在相同的url,那么hash后,必定分别存在a-x和b-x文件中,所以剩下的事就是可以分别对比这(a-1,b-1).....(a-1024,b-1024) 1024组文件,如果有,则存在相同url,如果没有,就不存在相同url。可根据实际情况,将大文件hash成更少的文件。这样可以提高效率 ) Problem 6:给你一个单词a,如果通过交换单词中字母的顺序可以得到另外的单词b,那么定义b是a的兄弟单词。现在给你一个字典,用户输入一个单词,让你根据字典找出这个单词有多少个兄弟单词。 提示:将每个的单词按照字母排序,则兄弟单词拥有一致的字母排序(作为单词签名)。使用单词签名来查找兄弟单词。 Problem 7:五桶球,一桶不正常,不知道球的重量和轻重关系,用天平称一次找出那桶不正常的球。 (以下方法摘抄自另一出处。 5个桶依次编号1,2,3,4,5 方法一: 依次从编号好的前4个桶拿出5,7,11,13个球 5+13=7+11 放天平左右 if(天平平衡) 第五个坏的 if(不平衡) 用游标+砝码把天平弄平 看游标+砝码的总和(以最小刻度为标准)是5,7,11,13谁的倍数,哪个桶的球就是坏的 确保实验的正确性 可以找4个更大且满足条件的质数 原文链接:http://zylishiyu.blog.163.com/blog/static/130146674201091865144712/ 方法二: 首先假定只要不把球从天平拿下来就还算一次,另外每个桶内的球是一样的:从1 号和2 号桶各拿一个,放上天平(1 号左,2 号右),如果平衡,说明这两桶球都是正常的,可以做为砝码。如果不平衡,那么1 号和2 号桶必有一个不正常,而其他3 ,4 ,5 桶是正常的,可以作为砝码。首先考虑1 号2 号桶不平衡的情况,这时从1 号和3 号桶再各拿一个球,放上天平(1 号右,3 号左),如果这时平衡了,说明1 号桶是不正常的,如果还是不平衡,那么2 号桶是不正常的。如果第一步1 号2 号桶是平衡的,那么也好办,把3 ,4 号桶各拿一个放上天平(3 号左,4 号右),这时如果还是平衡的,那么5 号桶必然是不正常的。如果不平衡,说明不正常的就在3 ,4 号桶之中。我们再用2 )的方法找出来即可。 原文链接:http://blog.csdn.net/knightliao/article/details/5831576 ) Problem 8:给两个烧杯,容积分别是m和n升(m!=n),还有用不完的水,用这两个烧杯能量出什么容积的水? m, n, m+n, m-n以及线性叠加的组合 Problem 9:写出一个算法,对给定的n个数的序列,返回序列中的最大和最小的数。 Problem 10:你能设计出一个算法,只需要执行1.5n次比较就能找到序列中最大和最小的数吗?能否再少? 提示:先通过两两比较,区分大小放入“大”,“小”两个数组中。从而最大数在“大”数组中,最小数在“小”数组中。 Problem 11:给你一个由n-1个整数组成的未排序的序列,其元素都是1到n中的不同的整数。请写出一个寻找序列中缺失整数的线性-时间算法。 提示:累加求和 Problem 12:void strton(const char* src, const char*token) 假设src是一长串字符,token存有若干分隔符,只要src的字符是token中的任何一个,就进行分割,最终将src按照token分割成若干单词。找出一种O(n)算法? 提示:查表的方法,将所有的字符串存储在长度为128的数组中,并将作为分隔符的字符位置1,这样即可用常数时间判断字符是否为分隔符,通过n次扫描,将src分割成单词。 Problem 13:一个排好序的数组A,长度为n,现在将数组A从位置m(m<n,m未知)分开,并将两部分互换位置,假设新数组记为B,找到时间复杂度为O(lgn)的算法查找给定的数x是否存在数组B中? 提示:同样采用二分查找。核心思想就是确定所查找数所在的范围。通过比较3个数(头,尾,中间)和所查找数之间的关系,可以确定下次查找的范围。 Problem 14:一个排好序的数组A,长度为n,现在将数组A从位置m(m<n,m已知)分开,并将两部分互换位置,设计一个O(n)的算法实现这样的倒置,只允许使用一个额外空间。(循环移位的效率不高) 提示:(A’B’)’ =BA Problem 15:给出Vector的一个更好实现。(STL的vector内存的倍增的,但是每次倍增需要拷贝已存元素,平均每个元素需要拷贝一次,效率不高) 提示:可使用2^n的固定长度作为每次分配的最小单位,并有序的记录每个块的首地址。这中结构同样可以实现线性查找,并且拷贝代价很低(仅有指针) Problem 16:给出已排序数组A,B,长度分别为n,m,请找出一个时间复杂度为(lgn)的算法,找到排在第k位置的数。 提示:二分查找。 Problem 17:给出任意数组A,B,长度分别为n,m,请找出一个时间复杂度为(lgn)的算法,找到排在第k位置的数。 提示:通过最小堆记录k个数,不断更新,扫描一次完毕。 这个提示有问题,求最优算法! Problem 18:假设数组A有n个元素,元素取值范围是1~n,判定数组是否存在重复元素?要求复杂度为O(n)。 法1:使用n的数组,记录元素,存在记为1,两次出现1,即重复。 法2:使用m的数组,分别记录大小:n/m, 2n/m …..的元素个数。桶方法 法3:累加求和。可用于求仅有一个元素重复的方法。 Problem 19:给定排好序的数组A,大小为n,现给定数X,判断A中是否存在两数之和等于X。给出一个O(n)的算法。 提示:从中间向两边查找。利用有序的条件 Problem 20:给定排好序的数组A,大小为n,请给出一个O(n)的算法,删除重复元素,且不能使用额外空间。 提示,既然有重复,必有冗余空间。将元素放入数组的前面,并记录下次可放位置,不断向后扫描即可。 Problem 21:给定两个排好序的数组A,B,大小分别为n,m。给出一个高效算法查找A中的哪些元素存在B数组中。 注意:一般在大数组中执行二分查找,将小数组的元素作为需查找的对象。 更优算法(轩辕刃提供):可以使用两个指针遍历AB,比较当前大小就可以了...时间复杂度o(n+m) Problem 22:问:有1000桶酒,其中1桶有毒。而一旦吃了,毒性会在1周后发作。现在我们用小老鼠做实验,要在1周内找出那桶毒酒,问最少需要多少老鼠。 答案:10只。将酒编号为1~1000 将老鼠分别编号为1 2 4 8 16 32 64 128 256 512 喂酒时 让酒的编号等于老鼠编号的加和如:17号酒喂给1号和16号老鼠 76号酒喂给4号、8号和64号老鼠 七天后将死掉的老鼠编号加起来 得到的编号就是有毒的那桶酒 因为2的10次方等于1024 所以10只老鼠最多可以测1024桶酒 证明如下:使用二进制表示:01, 10, 100, 1000, … , 1,000,000,000。对于任何一个小于1024的数,均可以采用前面的唯一一组二进制数来表示。故成立。 Problem 23:设计一组最少个数砝码,使得天平能够称量1~1000的重量。 如果砝码只能放单边,1,2 ,4 , 512最好。(只能单加) 如果允许砝码双边放,1, 3, 9, 27…. 最好。(可加可减)已知1,3,如何计算下一个数。现可称重量1,2,3,4。设下个数为x,可称重量为, x-4, x-3, x-2, x-1, x, x+1, x+2, x+3, x+4。为使砝码最好,所称重量应该不重复(浪费)。故x=9。同理,可得后面。 图形算法题 Problem 24:如何判断一个点是否在一个多边形内? 提示:对多边形进行分割,成为一个个三角形,判断点是否在三角形内。 一个非常有用的解析几何结论:如果P2(x1,y1),P2(x2,y2), P3(x3,y3)是平面上的3个点,那么三角形P1P2P3的面积等于下面绝对值的二分之一: | x1 y1 1 | | x2 y2 1 | = x1y2 + x3y1 + x2y3 –x3y2 – x2y1 – x1y3 | x3 y3 1 | 当且仅当点P3位于直线P1P2(有向直线P1->P2)的右侧时,该表达式的符号为正。这个公式可以在固定的时间内,检查一个点位于两点确定直线的哪侧,以及点到直线的距离(面积=底*高/2)。 这个结论:可以用来判断点是否在点是否在三角形内。法1:判断点和三角形三边所行程的3个三角形的面积之和是否等于原来三角形的面积。(用了三次上面的公式)。 法2:判断是否都在三条边的同一边,相同则满足,否则不在三角形内。 Problem 25:给出两个n为向量与0点形成角的角平分线。 提示:对两条边进行归一化,得到长度为1的两点,取两个的中点即可。 原文链接:http://www.cnblogs.com/zhenjing/archive/2010/10/18/1854020.html
第一种----等差数列:是指相邻之间的差值相等,整个数字序列依次递增或递减的一组数。 1、等差数列的常规公式。设等差数列的首项为a1,公差为d ,则等差数列的通项公式为an=a1+(n-1)d (n为自然数)。 [例1]1,3,5,7,9,( ) A.7 B.8 C.11 D.13 [解析] 这是一种很简单的排列方式:其特征是相邻两个数字之间的差是一个常数。从该题中我们很容易发现相邻两个数字的差均为2,所以括号内的数字应为11。故选C。 2、二级等差数列。是指等差数列的变式,相邻两项之差之间有着明显的规律性,往往构成等差数列. [例2] 2, 5, 10, 17, 26, ( ), 50 A.35 B.33 C.37 D.36 [解析] 相邻两位数之差分别为3, 5, 7, 9, 是一个差值为2的等差数列,所以括号内的数与26的差值应为11,即括号内的数为26+11=37.故选C。 3、分子分母的等差数列。是指一组分数中,分子或分母、分子和分母分别呈现等差数列的规律性。 [例3] 2/3,3/4,4/5,5/6,6/7,( ) A、8/9 B、9/10 C、9/11 D、7/8 [解析] 数列分母依次为3,4,5,6,7;分子依次为2,3,4,5,6,故括号应为7/8。故选D。 4、混合等差数列。是指一组数中,相邻的奇数项与相邻的偶数项呈现等差数列。 [例4] 1,3,3,5,7,9,13,15,,( ),( )。 A、19 21 B、19 23 C、21 23 D、27 30 [解析] 相邻奇数项之间的差是以2为首项,公差为2的等差数列,相邻偶数项之间的差是以2为首项,公差为2的等差数列。 第二种--等比数列:是指相邻数列之间的比值相等,整个数字序列依次递增或递减的一组数。 5、等比数列的常规公式。设等比数列的首项为a1,公比为q(q不等于0),则等比数列的通项公式为an=a1q n-1(n为自然数)。 [例5] 12,4,4/3,4/9,( ) A、2/9 B、1/9 C、1/27 D、4/27 [解析] 很明显,这是一个典型的等比数列,公比为1/3。故选D。 6、二级等比数列。是指等比数列的变式,相邻两项之比有着明显的规律性,往往构成等比数列。 [例6] 4,6,10,18,34,( ) A、50 B、64 C、66 D、68 [解析] 此数列表面上看没有规律,但它们后一项与前一项的差分别为2,4,6,8,16,是一个公比为2的等比数列,故括号内的值应为34+16Ⅹ2=66 故选C。 7、等比数列的特殊变式。 [例7] 8,12,24,60,( ) A、90 B、120 C、180 D、240 [解析] 该题有一定的难度。题目中相邻两个数字之间后一项除以前一项得到的商并不是一个常数,但它们是按照一定规律排列的:3/2,4/2,5/2,因此,括号内数字应为60Ⅹ6/2=180。故选C。此题值得再分析一下,相邻两项的差分别为4,12,36,后一个值是前一个值的3倍,括号内的数减去60应为36的3倍,即108,括号数为168,如果选项中没有180只有168的话,就应选168了。同时出现的话就值得争论了,这题只是一个特例。 第三种—混合数列式:是指一组数列中,存在两种以上的数列规律。 8、双重数列式。即等差与等比数列混合,特点是相隔两项之间的差值或比值相等。 [例8] 26,11,31,6,36,1,41,( ) A、0 B、-3 C、-4 D、46 [解析] 此题是一道典型的双重数列题。其中奇数项是公差为5的等差递增数列,偶数项是公差为5的等差递减数列。故选C。 9、混合数列。是两个数列交替排列在一列数中,有时是两个相同的数列(等差或等比),有时两个数列是按不同规律排列的,一个是等差数列,另一个是等比数列。 [例9] 5,3,10,6,15,12,( ),( ) A、20 18 B、18 20 C、20 24 D、18 32 [解析] 此题是一道典型的等差、等比数列混合题。其中奇数项是以5为首项、公差为5的等差数列,偶数项是以3为首项、公比为2的等比数列。故选C。 第四种—四则混合运算:是指前两(或几)个数经过某种四则运算等到于下一个数,如前两个数之和、之差、之积、之商等于第三个数。 10、加法规律。 之一:前两个或几个数相加等于第三个数,相加的项数是固定的。 [例11] 2,4,6,10,16,( )A、26 B、32 C、35 D、20 [解析] 首先分析相邻两数间数量关系进行两两比较,第一个数2与第二个数4之和是第三个数,而第二个数4与第三个数6之和是10。依此类推,括号内的数应该是第四个数与第五个数的和26。故选A。 之二:前面所有的数相加等到于最后一项,相加的项数为前面所有项。 [例12] 1,3,4, 8,16,( ) A、22 B、24 C、28 D、32 [解析] 这道题从表面上看认为是题目出错了,第二位数应是2,以为是等比数列。其实不难看出,第三项等于前两项之和,第四项与等于前三项之和,括号内的数应为前五项之和为32。故选D。 11、减法规律。是指前一项减去第二项的差等于第三项。 [例13] 25,16,9,7,( ),5 A、8 B、2 C、3 D、6 [解析] 此题是典型的减法规律题,前两项之差等于第三项。故选B。 12、加减混合:是指一组数中需要用加法规律的同时还要使用减法,才能得出所要的项。 [例14] 1,2,2,3,4,6,( ) A、7 B、8 C、9 D、10 [解析] 即前两项之和减去1等于第三项。故选C。 13、乘法规律。之一:普通常规式:前两项之积等于第三项。 [例15] 3,4,12,48,( ) A、96 B、36 C、192 D、576 [解析] 这是一道典型的乘法规律题,仔细观察,前两项之积等于第三项。故选D。 之二:乘法规律的变式: [例16] 2,4,12,48,( ) A、96 B、120 C、240 D、480 [解析] 每个数都是相邻的前面的数乘以自已所排列的位数,所以第5位数应是5×48=240。故选D。 14、除法规律。 [例17] 60,30,2,15,( ) A、5 B、1 C、1/5 D、2/15 [解析] 本题中的数是具有典型的除法规律,前两项之商等于第三项,故第五项应是第三项与第四项的商。故选D。 15、除法规律与等差数列混合式。 [例18] 3,3,6,18,( ) A、36 B、54 C、72 D、108 [解析] 数列中后个数字与前一个数字之间的商形成一个等差数列,以此类推,第5个数与第4个数之间的商应该是4,所以18×4=72。故选C。 思路引导:快速扫描已给出的几个数字,仔细观察和分析各数之间的关系,大胆提出假设,并迅速将这种假设延伸到下面的数。如果假设被否定,立刻换一种假设,这样可以极大地提高解题速度。 第五种—平方规律:是指数列中包含一个完全平方数列,有的明显,有的隐含。 16、平方规律的常规式。 [例19] 49,64,81,( ),121 A、98 B、100 C、108 D、116 [解析] 这组数列可变形为72,82,92,( ),112,不难看出这是一组具有平方规律的数列,所以括号内的数应是102。故选B。 17、平方规律的变式。 之一、n2-n [例20] 0,3,8,15,24,( ) A、28 B、32 C、35 D、40 [解析] 这个数列没有直接规律,经过变形后就可以看出规律。由于所给数列各项分别加1,可得1,4,9,16,25,即12,22,32,42,52,故括号内的数应为62-1=35,其实就是n2-n。故选C。 之二、n2+n [例21] 2,5,10,17,26,( ) A、43 B、34 C、35 D、37 [解析] 这个数是一个二级等差数列,相邻两项的差是一个公差为2的等差数列,括号内的数是26=11=37。如将所给的数列分别减1,可得1,4,9,16,25,即12,22,32,42,52,故括号内的数应为62+1=37,,其实就是n2+n。故选D。 之三、每项自身的平方减去前一项的差等于下一项。 [例22] 1,2,3,7,46,( ) A、2109 B、1289 C、322 D、147 [解析] 本数列规律为第项自身的平方减去前一项的差等于下一项,即12-0,22-1=3,32-2=7,72-3=46,462-7=2109,故选A。 第六种—立方规律:是指数列中包含一个立方数列,有的明显,有的隐含。 16、立方规律的常规式: [例23] 1/343,1/216,1/125,( ) A、1/36 B、1/49 C、1/64 D、1/27 [解析] 仔细观察可以看出,上面的数列分别是1/73,1/63,1/53的变形,因此,括号内应该是1/43,即1/64。故选C。 17、立方规律的变式: 之一、n3-n [例24] 0,6,24,60,120,( ) A、280 B、320 C、729 D、336 [解析] 数列中各项可以变形为13-1,23-2,33-3,43-4,53-5,63-6,故后面的项应为73-7=336,其排列规律可概括为n3-n。故选D。 之二、n3+n [例25] 2,10,30,68,( ) A、70 B、90 C、130 D、225 [解析] 数列可变形为13+1,23+1,33+1,43+1,故第5项为53+=130,其排列规律可概括为n3+n。故选C。 之三、从第二项起后项是相邻前一项的立方加1。 [例26] -1,0,1,2,9,( ) A、11 B、82 C、729 D、730 [解析] 从第二项起后项分别是相邻前一项的立方加1,故括号内应为93+1=730。故选D。 思路引导:做立方型变式这类题时应从前面几种排列中跳出来,想到这种新的排列思路,再通过分析比较尝试寻找,才能找到正确答案。 第七种—特殊类型: 18、需经变形后方可看出规律的题型: [例27] 1,1/16,( ),1/256,1/625 A、1/27 B、1/81 C、1/100 D、1/121 [解析] 此题数列可变形为1/12,1/42,( ),1/162,1/252,可以看出分母各项分别为1,4,( ),16,25的平方,而1,4,16,25,分别是1,2,4,5的平方,由此可以判断这个数列是1,2,3,4,5的平方的平方,由此可以判断括号内所缺项应为1/(32)2=1/81。故选B。 19、容易出错规律的题。 [例28] 12,34,56,78,( ) A、90 B、100 C、910 D、901 [解析] 这道题表面看起来起来似乎有着明显的规律,12后是34,然后是56,78,后面一项似乎应该是910,其实,这是一个等差数列,后一项减去前一项均为22,所以括号内的数字应该是78+22=100。故选B。 原文链接:http://blog.csdn.net/slj_win/article/details/7204595