当前位置: 首页 > 编程日记 > 正文

朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型

做Linux网络开发,一般绕不开标题中几种网络编程模型。网上已有很多写的不错的分析文章,它们的基本论点是差不多的。但是我觉得他们讲的还不够详细,在一些关键论点上缺乏数据支持。所以我决定好好研究这几个模型。(转载请指明出于breaksoftware的csdn博客)

在研究这些模型前,我决定按如下步骤去做:

  1. 实现朴素模型
  2. 实现发请求的测试程序
  3. 实现Select模型,测试其效率
  4. 实现Poll模型,测试其效率
  5. 实现Epoll模型,测试其效率
  6. 分析各模型性能,分析和对比其源码
  7. 针对各模型特点,修改上述程序进行测试和分析

朴素模型是我们编程时可以使用的最简单的一种模型。因为没有一个确切的名字可以称呼,我索性叫它朴素模型。我选择先实现它,一是为了由易而难,二是为了遵循模型发展的过程、体会技术发展的历程。在实现完朴素模型之后,我们要去实现一个用于发送请求的测试程序,它将帮助我们发送大量的请求,以便于之后我们对各个模型进行可用性测试。之后我们再去实现Select、Poll和Epoll网络模型。这个顺序也是技术发展的顺序,我们可以在实现前一个模型时分析其优缺点,然后在后一个模型分析中,看到其对这些缺点的改进方案,体会技术进步的过程。

为了便于之后各个模型的对比,我会尽可能的重用代码,即各个模型功能相同的模块将使用相同的函数去实现,如果实在不可以重用,则使用参数进行区分,但是区分的代码片段将足够的小。所以,我们将在本文看到大部分重要的代码实现片段。

为了比较直观的观察各个模型的执行,我们将在各个模型执行前,启动一个打印统计信息的线程

         err = init_print_thread();if (err < 0) {perror("create print thread error");exit(EXIT_FAILURE);}

init_print_thread函数将被各个模型使用,wait_print_thread是用于等待该打印结果的线程退出。由于我并不准备让这个线程退出,所以wait_print_thread往往用来阻塞主线程。

 pthread_t g_print_thread;intinit_print_thread() {return pthread_create(&g_print_thread, NULL, print_count, NULL);}voidwait_print_thread() {pthread_join(g_print_thread, NULL);}

print_count函数是用于线程执行的实体,它每隔一秒钟打印一条记录

static int g_request_count = 0;static int g_server_suc = 0;
static int g_client_suc = 0;
static int g_read_suc = 0;
static int g_write_suc = 0;static int g_server_fai = 0;
static int g_client_fai = 0;
static int g_read_fai = 0;
static int g_write_fai = 0;void* print_count(void* arg) {struct timeval cur_time;int index = 0;fprintf(stderr, "index\tseconds_micro_seconds\tac\tst\tsr\tsw\tft\tfr\tfw\n");while (1) {sleep(1);gettimeofday(&cur_time, NULL);fprintf(stderr, "%d\t%ld\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",index,cur_time.tv_sec * 1000000 + cur_time.tv_usec,g_request_count,g_server_suc > g_client_suc ? g_server_suc : g_client_suc,g_read_suc,g_write_suc,g_server_fai > g_client_fai ? g_server_fai : g_client_fai,g_read_fai,g_write_fai);index++;}
}

上述各数据的定义如下:

  • g_request_count用于记录总请求数;
  • g_server_suc是用于记录服务行为成功数,其场景为:读取客户端成功且发送回包成功
  • g_server_fai是记录服务其行为失败数,其场景为:1 读取客户端失败;2 读取客户端成功但是发送回包失败;
  • g_client_suc用于记录客户端行为成功数,其场景为:发送包成功且读取服务器回包成功;
  • g_client_fai用于记录客户端行为失败数,其场景为:1 发送包失败; 2 发送包成功但是接收服务器回包失败;
  • g_read_suc用于记录读取行为成功数,其场景为: 1 服务器读取客户端请求包成功; 2 客户端读取服务器回包成功;
  • g_read_fai用于记录读取行为失败数,其场景为: 1 服务器读取客户端请求包失败; 2 客户端读取服务器回包失败;
  • g_write_suc用于记录发送行为成功数,其场景为: 1 客户端向服务器发送请求包成功; 2 服务器向客户端回包成功;
  • g_write_fai用于记录发送行为失败数,其场景为: 1 客户端向服务器发送请求包失败; 2 服务器向客户端回包失败;

通过数据的打印,我们将知道服务器和客户端执行执行的过程,以及出问题的环节,还有服务器的丢包情况。

下一步,我们需要创建一个供客户端连接的Socket。

	listen_sock = make_socket(0);

我们对make_socket传入了参数0,是因为我们不要求创建的监听Socket具有异步属性。

int
make_socket(int asyn) {int listen_sock = -1;int rc = -1;int on = 1;struct sockaddr_in name;listen_sock = socket(AF_INET, SOCK_STREAM, 0);if (listen_sock < 0) {perror("create socket error");exit(EXIT_FAILURE);}rc = setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));if (rc < 0) {perror("setsockopt error");exit(EXIT_FAILURE);}if (asyn) {rc = ioctl(listen_sock, FIONBIO, (char*)&on);if (rc < 0) {perror("ioctl failed");exit(EXIT_FAILURE);}}name.sin_family = AF_INET;name.sin_port = htons(PORT);name.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(listen_sock, (struct sockaddr*)&name, sizeof(name)) < 0) {perror("bind error");exit(EXIT_FAILURE);}return listen_sock;
}

这个函数中我们使用了socket函数创建了一个TCP的Socket。并使用bind函数将该socket绑定到本机特定的端口上。

在朴素模型中,我们让服务器是一个同步处理过程。于是不要求之后的连接具有异步属性,所以我们创建该Socket时传了参数0——让监听Socket不具有异步特性。在之后介绍的Select、Poll和Epoll模型中,我们需要客户端接入的连接是异步的,于是我们就传递了参数1,让监听Socket具有异步特性,这样通过它接入的连接也是异步的。

Socket绑定之后,服务器就要开始监听客户端的接入

	if (listen(listen_sock, SOMAXCONN) < 0) {perror("listen error");exit(EXIT_FAILURE);}

SOMAXCONN是可以同时处理的最大连接数,它是一个系统宏。在我系统上它的值是128。

最后,我们在一个死循环中接收并处理客户端的请求

	while (1) {int new_sock;new_sock = accept(listen_sock, NULL, NULL);if (new_sock < 0) {perror("accept error");exit(EXIT_FAILURE);}

通过accept我们将获得接入的socket。如果socket值合法,我们则需要让接受的请求数自增1

		request_add(1);

request_add函数将在之后不同模型以及测试程序中被调用,而且会是在不同的线程中调用。于是这儿就引入一个多线程的问题。我并不打算使用锁等方法,而是利用简单的原子操作来实现。

void 
request_add(int count) {__sync_fetch_and_add(&g_request_count, count);
}

由于我们设计的朴素模式是一个同步过程,所以接入的socket不是异步的。当一些特殊情况发生时,之后的读取socket内容的行为或者往socket中写入内容的行为可能会卡住。这样将导致整个服务都卡住,这是我们不希望看到的。于是我们需要对该同步socket设置操作超时属性。

		set_block_filedes_timeout(new_sock);
void
set_block_filedes_timeout(int filedes) {struct timeval tv_out, tv_in;tv_in.tv_sec = READ_TIMEOUT_S;tv_in.tv_usec = READ_TIMEOUT_US;if (setsockopt(filedes, SOL_SOCKET, SO_RCVTIMEO, &tv_in, sizeof(tv_in)) < 0) {perror("set rcv timeout error");exit(EXIT_FAILURE);}tv_out.tv_sec = WRITE_TIMEOUT_S;tv_out.tv_usec = WRITE_TIMEOUT_US;if (setsockopt(filedes, SOL_SOCKET, SO_SNDTIMEO, &tv_out, sizeof(tv_out)) < 0) {perror("set rcv timeout error");exit(EXIT_FAILURE);}
}

这儿要说明下,我在网上看过很多人提问说通过上述方法设置超时属性无效。其实是他们犯了一个错误,就是将socket设置为异步属性。如果socket既设置为异步属性,又设置了超时,socket当然是按异步特点去执行的,超时设置也就无效了。

还有一个问题,就是有些同学在自己设计服务器和客户端时发生了“死锁”问题(非严格定义意义上的死锁)。那是因为设计的服务器和客户端都是同步的,而且socket都没有设置超时。这样在客户端调用完write之后进入read时,服务器此时也是read状态,导致了“死锁”。但是这个问题并不是经常发生,因为大部分同学实现read时给了一个很大的缓存,并认为读取的内容一次性可以读完。而没有考虑到一次read操作可能读不完全部数据的情况,比如下面的实现

	while (nbytes > 0) {nbytes = recv(filedes, buffer, sizeof(buffer) - 1, 0);if (nbytes > 0) {total_length_recv += nbytes;}//buffer[nbytes] = 0;//fprintf(stderr, "%s", buffer);}

这段服务器read操作考虑到了一次性可能读不完全部数据的问题。但是如果客户端发送完数据后,服务器第一次recv可以把全部数据读取出来了。由于读取的数据大于0,于是再次进入读取操作,这个时候,客户端已经处于读取服务器返回的阶段。由于socket是同步的,且未设置超时,导致服务器一直卡在再次读取的操作中,这样就发生了“死锁”。其实这个过程非常有意思,当我们对一段不健壮的代码进行加固时,往往会掉到另外一个坑里。但是只要我们努力的从坑里跳出来,就会豁然开朗且认识到很多别人忽视的问题。

我们再回到正题,我们设置好socket超时属性后,就开始让服务器读取客户端的输入内容,如果输入内容读取成功,则往客户端回包。最后服务器将该次连接关闭

		if (0 == server_read(new_sock)) {server_write(new_sock);}close(new_sock);

server_read方法在底层调用了read_data方法,read_data方法是我们整个代码的两个关键行为之一

int
is_nonblock(int fd) {int flags = fcntl(fd, F_GETFL);if (flags == -1) {perror("get fd flags error");exit(EXIT_FAILURE);}return (flags & O_NONBLOCK) ? 1 : 0;
}int
read_data(int filedes, int from_server) {char buffer[MAXMSG];int nbytes;int total_len_recv;int wait_count = 0;int rec_suc = 0;total_len_recv = 0;while (1) {nbytes = recv(filedes, buffer, sizeof(buffer) - 1, 0);if (nbytes < 0) {if (is_nonblock(filedes)) {if (EAGAIN == errno || EWOULDBLOCK == errno || EINTR == errno) {if (wait_count < WAIT_COUNT_MAX) {wait_count++;usleep(wait_count);continue;}}}break;}if (nbytes == 0) {//fprintf(stderr, "read end\n");break;}else if (nbytes > 0) {total_len_recv += nbytes;//buffer[nbytes] = 0;//fprintf(stderr, "%s", buffer);}if ((from_server && is_server_recv_finish(total_len_recv))|| (!from_server && is_client_recv_finish(total_len_recv))) {rec_suc = 1;break;}}

read_data行为分为客户端和服务器两个版本实现,其基本逻辑是一样的。我们考虑到读取操作可能一次性读不完,所以我们使用while循环持续尝试读取。如果是一个异步的socket,我们则考虑recv函数返回小于0时各种错误值的场景,并使用渐长等待的方式进行多次尝试。如果是同步的socket,一旦recv返回值小于0,则退出读取操作。total_len_recv函数用于统计一共读取的长度,之后通过这个长度结合是否是服务器还是客户端的标识,判断读取操作是否完成。

当读取操作结束后,我们要统计读取操作的行为及其标识的整个过程的行为。

	if (from_server) {if (rec_suc) {__sync_fetch_and_add(&g_read_suc, 1);return 0;} else {__sync_fetch_and_add(&g_read_fai, 1);__sync_fetch_and_add(&g_server_fai, 1);return -1;}} else {if (rec_suc) {__sync_fetch_and_add(&g_read_suc, 1);__sync_fetch_and_add(&g_client_suc, 1);return 0;} else {__sync_fetch_and_add(&g_read_fai, 1);__sync_fetch_and_add(&g_client_fai, 1);return -1;}}}

如果读取操作成功,则进行发送操作。server_write方法在底层调用了write_data方法

int
write_data(int filedes, int from_server) {int nbytes;int total_len_send;int wait_count = 0;int index;int send_suc = 0;total_len_send = 0;index = 0;while (1) {if (from_server) {nbytes = send(filedes, get_server_send_ptr(index), get_server_send_len(index), 0);}else {nbytes = send(filedes, get_client_send_ptr(index), get_client_send_len(index), 0);}if (nbytes < 0) {if (is_nonblock(filedes)) {if (EAGAIN == errno || EWOULDBLOCK == errno || EINTR == errno) {if (wait_count < WAIT_COUNT_MAX) {wait_count++;usleep(wait_count);continue;}}}break;}else if (nbytes == 0) {break;}else if (nbytes > 0) {total_len_send += nbytes;}if ((from_server && is_server_send_finish(total_len_send)) ||(!from_server && is_client_send_finish(total_len_send))){send_suc = 1;break;}}

其实现和read_data思路一致,也考虑到一次性写不完的情况和同步异步socket问题。写入操作完成后再去统计相关行为

	if (from_server) {if (send_suc) {__sync_fetch_and_add(&g_write_suc, 1);__sync_fetch_and_add(&g_server_suc, 1);return 0;} else {__sync_fetch_and_add(&g_write_fai, 1);__sync_fetch_and_add(&g_server_fai, 1);return -1;}} else {if (send_suc) {__sync_fetch_and_add(&g_write_suc, 1);return 0;} else {__sync_fetch_and_add(&g_write_fai, 1);__sync_fetch_and_add(&g_client_fai, 1);return -1;}}
}

最后我们讲下测试程序的实现。为了便于测试,我要求测试程序可以接受至少2个参数,第一个参数是用于标识启动多少个线程发送请求;第二个参数用于指定线程中等待多少毫秒发送一次请求;第三个参数是可选的,标识一共发送多少次请求。这样我们可以通过这些参数控制测试程序的行为

#define MAXREQUESTCOUNT 100000static int g_total = 0;
static int g_max_total = 0;void* send_data(void* arg) {int wait_time;int client_sock;wait_time = *(int*)arg;while (__sync_fetch_and_add(&g_total, 1) < g_max_total) {usleep(wait_time);client_sock = make_client_socket();connect_server(client_sock);request_add(1);set_block_filedes_timeout(client_sock);if (0 == client_write(client_sock)) {client_read(client_sock);}close(client_sock);client_sock = 0;}
}int 
main(int argc, char* argv[]) {int thread_count;int index;int err;int wait_time;pthread_t thread_id;if (argc < 3) {fprintf(stderr, "error! example: client 10 50\n");return 0;}err = init_print_thread();if (err < 0) {perror("create print thread error");exit(EXIT_FAILURE);}thread_count = atoi(argv[1]);wait_time = atoi(argv[2]);g_max_total = MAXREQUESTCOUNT;if (argc > 3) {g_max_total = atoi(argv[3]);}	for (index = 0; index < thread_count; index++) {err = pthread_create(&thread_id, NULL, send_data, &wait_time);if (err != 0) {perror("can't create send thread");exit(EXIT_FAILURE);}}wait_print_thread();return 0;
}

线程中,首先通过make_client_socket创建socket并绑定到本地端口上

int
make_client_socket() {int client_sock = -1;struct sockaddr_in client_addr;client_sock = socket(AF_INET, SOCK_STREAM, 0);if (client_sock < 0) {perror("create socket error");exit(EXIT_FAILURE);}bzero(&client_addr, sizeof(client_addr));client_addr.sin_family = AF_INET;client_addr.sin_addr.s_addr = htons(INADDR_ANY);client_addr.sin_port = htons(0);if (bind(client_sock, (struct sockaddr*)&client_addr, sizeof(client_addr)) < 0) {perror("bind error");exit(EXIT_FAILURE);}return client_sock;
}

然后通过connect_server连接服务器

void 
connect_server(int client_sock) {struct sockaddr_in server_addr;bzero(&server_addr, sizeof(server_addr));server_addr.sin_family = AF_INET;if (inet_aton("127.0.0.1", &server_addr.sin_addr) == 0) {perror("set server ip error");exit(EXIT_FAILURE);}server_addr.sin_port = htons(PORT);if (connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("client connect server error");exit(EXIT_FAILURE);}
}

最后通过client_write和client_read和服务器通信。这两个函数都是调用上面介绍的write_data和read_data,所以没什么好讲的。

int
client_read(int filedes) {return read_data(filedes, 0);
}int
client_write(int filedes) {return write_data(filedes, 0);
}

我们启动一千个线程,发送30万次请求。看看朴素模型的处理能力。

首先我们看看服务器的结果打印

可以发现稳定的处理能力大概在每秒14000~15000左右。

我们再看看客户端的打印


        我们发现其发送频率差不多也是14000~16000。这儿要说明下,因为客户端是同步模型,服务器也是同步模型,所以这个速率是服务器处理的峰值。否则按照设置的1微秒的等待时间,1000个线程一秒钟发送的请求数肯定不止15000。我使用过两个测试进程同时去压,也验证了其最大的处理能力也就是在14000~15000左右(在我的配置环境下)。

我们发现,使用朴素模型实现网络通信是非常方便的。但是这个模型有个明显的缺点,就是一次只能处理一个请求——即接收请求、读socket、写socket是串行执行的。除非使用线程池去优化这个流程,否则在单线程的情况下,似乎就不能解决这个问题了。科技总是进步的,我们将在下一节讲解Select模型,它就可以解决这个问题。

相关文章:

支付宝账单出来后,除了总消费,你看到你的学习支出了吗?

当支付宝的2019年年度账单出来的时候很多人的第一反应就是我怎么花了这么多钱不少人都有这样的困惑忙忙碌碌一年到头&#xff0c;到底得到了什么&#xff1f;而这一切又和自己最开始的规划一样吗&#xff1f;其实从账单上可以看出你在2019年花费了哪些大头居家生活、穿衣打扮还…

体验Windows 7的Superbar

随着PDC 2008上Windows 7 M3 6801的发布&#xff0c;这个微软的下一代操作系统也渐渐浮出了水面。对于我们这些普通的PC用户而言&#xff0c;Windows 7相对于Windows Vista最显而易见的改变&#xff0c;无疑就是著名的Superbar任务栏了。说起它相比于原来的任务栏变化&#xff…

Linux 安装图形界面及远程连接

#可查询哪些组件是否已经安装(可用来对照组件名称&#xff09; yum grouplistyum groupinstall X Window System -y #安装GNOME桌面环境 yum groupinstall GNOME Desktop Environment -y #安装KDE桌面环境 yum groupinstall KDE (K Desktop Environment)卸载 卸载GNOME桌面环境…

朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型

在《朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型》中我们分析了朴素模型的一个缺陷——一次只能处理一个连接。本文介绍的Select模型则可以解决这个问题。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 和朴素模型一样&#xff0c;我们首先…

微软开源NAS算法Petridish,提高神经网络迁移能力

作者 | Jesus Rodriguez译者 | Rachel编辑 | 夕颜出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09; 【导读】神经架构搜索&#xff08;Neural architecture search, NAS&#xff09;是深度学习中最热门的趋势之一。NAS方法针对特定问题和数据…

[转]g++ 编译多个相关文件

三个文件&#xff0c;一个头文件&#xff0c;两个程序文件 /*d.h */#include <iostream>usingnamespacestd; classDataset { public: intgetdata(); }; /*d.cpp */#include "d.h"intDataset::getdata() { return1231; } /*out.cpp */#include <ios…

POJ--2391--Ombrophobic Bovines【分割点+Floyd+Dinic优化+二分法答案】最大网络流量

联系&#xff1a;http://poj.org/problem?id2391 题意&#xff1a;有f个草场&#xff0c;每一个草场当前有一定数目的牛在吃草&#xff0c;下雨时它能够让一定数量的牛在这里避雨&#xff0c;f个草场间有m条路连接&#xff0c;每头牛通过一条路从一点到还有一点有一定的时间花…

25年了,我总结出这些信息提取的经验教训

作者 | Ehud Reiter译者 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;【导读】近日&#xff0c;本文作者阿伯丁大学计算科学系教授 Ehud Reiter 及其带领的阅读小组读了一篇让他们印象深刻的论文——由 Ralph Grishman 发表的《信息提取 25 年》&#xff08…

朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll模型

在《朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型》中&#xff0c;我们分析了它只能支持1024个连接同时处理的原因。但是在有些需要同时处理更多连接的情况下&#xff0c;1024个连接往往是不够的&#xff0c;也就是不能够高并发。那么这个时候我们就可以采用…

flashcom中远程共享对象SharedObject的用法

觉得这篇文章比较好&#xff0c;转载回来。学习fcs也有差不多一个月了,感觉最有特色的东西还是SharedObject.SharedObject有不少东西,本地操作就不说了(相信很多人没接触fcs也用过);就说说远程共享对象吧.基本的应用流程是:my_nc new NetConnection(); my_nc.connect("rt…

Hive-1.2.0学习笔记(一)安装配置

鲁春利的工作笔记&#xff0c;好记性不如烂笔头下载hive&#xff1a;http://hive.apache.org/index.htmlHive是基于Hadoop的一个数据仓库工具&#xff0c;提供了SQL查询功能&#xff0c;能够将SQL语句解析成MapReduce任务对存储在HDFS上的数据进行处理。MySQ安装Hive有三种运行…

邮件安全隐患及其防范技术研究

邮件安全隐患及其防范技术研究<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />陈小兵【antian365.com】摘要电子邮件是Internet上使用最为频繁和广泛的服务&#xff0c;在给人们带来便利的同时&#xff0c;亦带来令人担忧的邮件…

必看!52篇深度强化学习收录论文汇总 | AAAI 2020

所有参与投票的 CSDN 用户都参加抽奖活动群内公布奖项&#xff0c;还有更多福利赠送来源 | 深度强化学习实验室&#xff08;ID:Deep-RL&#xff09;作者 | DeepRLAAAI 2020 共收到的有效论文投稿超过 8800 篇&#xff0c;其中 7737 篇论文进入评审环节&#xff0c;最终收录数量…

朴素、Select、Poll和Epoll网络编程模型实现和分析——Epoll模型

在阅读完《朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型》和《朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll模型》两篇文章后&#xff0c;我们发现一个问题&#xff0c;不管select函数还是poll函数都不够智能&#xff0c;它们只能告诉我们成功…

Scala 深入浅出实战经典 第88讲:Scala中使用For表达式实现map、flatMap、filter

高级函数 map,flatMap,filter用for循环的实现。package com.dt.scala.forexpressionobject For_Advanced {def main(args: Array[String]) {}def map[A, B](list: List[A], f: A > B): List[B] for(element <- list) yield f(element)def flatMap[A, B](list: List[A], f…

抛弃Python,我们为什么用Go编写机器学习架构?

所有参与投票的 CSDN 用户都参加抽奖活动群内公布奖项&#xff0c;还有更多福利赠送作者 | Caleb Kaiser译者 | 弯月&#xff0c;编辑 | 郭芮来源 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;如今&#xff0c;众所周知Python是机器学习项目中最流行的语言。尽管R、C…

朴素、Select、Poll和Epoll网络编程模型实现和分析——模型比较

经过之前四篇博文的介绍&#xff0c;可以大致清楚各种模型的编程步骤。现在我们来回顾下各种模型&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 模型编程步骤对比《朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型》中介绍的是最基本的网络编程…

使用VM虚拟机的一点小技巧

今天想为朋友弄一个虚拟机系统文件&#xff0c;这样就可以直接拷贝过去&#xff0c;直接让他用了。哪成想电脑里的系统镜像文件不能用&#xff0c;也不知道是不是VM不支持&#xff0c;反正怎么着也引导不起来了。无奈只好用硬件光驱来装虚拟系统&#xff0c;把2003系统盘装入光…

翻译:AKKA笔记 - Actor消息 -1(二)

消息 我们只是让QuoteRequest到ActorRef去但是我们根本没见过消息类&#xff01; 它是这样的&#xff1a;&#xff08;一个最佳实践是把你的消息类包装在一个完整的对象里以利于更好的组织&#xff09; TeacherProtocol package me.rerun.akkanotes.messaging.protocolsobject …

远程安装oracle 10.2.1 for redhat 5.0 2.6.18-53.el5xen

远程安装oracle <?xml:namespace prefix st1 ns "urn:schemas-microsoft-com:office:smarttags" />10.2.1 for redhat 5.0 2.6.18-53.el5xen<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />今天有个朋友打电…

伯克利新无监督强化学习方法:减少混沌所产生的突现行为

作者 | Glen Berseth译者 | Arvin编辑 | 夕颜出品 | AI科技大本营&#xff08;ID: rgznai100&#xff09;【导读】所有生命有机体都在环境中占据一席之地&#xff0c;使它们在周围不断增加的熵中可以保持相对可预测性。例如&#xff0c;人类竭尽全力保护自己免受意外袭击--我们…

朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll、Epoll模型处理长连接性能比较

在《朴素、Select、Poll和Epoll网络编程模型实现和分析——模型比较》一文中&#xff0c;我们分析了各种模型在处理短连接时的能力。本文我们将讨论处理长连接时各个模型的性能。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 我们可以想象下场景&#xff0c…

Topcoder SRM 663 DIV 1

ABBADiv1 题意&#xff1a; 规定两种操作&#xff0c;一种是在字符串的末尾添加A&#xff0c;另一种是在末尾添加B然后反转字符串。现在给你一个起始串&#xff0c;一个终点串&#xff0c;然后问你是否能够通过以上两种操作&#xff0c;从起始串变为终点串。 题解&#xff1a; …

跨平台PHP调试器设计及使用方法——立项

作为一个闲不住且希望一直能挑战自己的人&#xff0c;我总是在琢磨能做点什么。自从今年初开始接触PHP&#xff0c;我也总想能在这个领域内产生点贡献。那能做点什么呢&#xff1f;我经常看到很多phper说自己设计了一个什么框架&#xff0c;或者说自己搭建了一个什么系统。虽然…

机器推理文本+视觉,跨模态预训练新进展

作者 | 李根、段楠、周明来源 | 微软研究院AI头条&#xff08;ID:MSRAsia&#xff09;【导读】机器推理要求利用已有的知识和推断技术对未见过的输入信息作出判断&#xff0c;在自然语言处理领域中非常重要。本文将介绍微软亚洲研究院在跨模态预训练领域的研究进展。近年来&…

[LeetCode]:94:Binary Tree Inorder Traversal

题目&#xff1a; Given a binary tree, return the inorder traversal of its nodes values. For example:Given binary tree {1,#,2,3}, 1\2/3return [1,3,2]. 代码&#xff1a; public class Solution {public static ArrayList<Integer> listResult new ArrayList&l…

腾讯 AI 2019这一年

所有参与投票的 CSDN 用户都参加抽奖活动群内公布奖项&#xff0c;还有更多福利赠送近日&#xff0c;腾讯AI实验室总结了 2019 年其取得重大进展的两大研究方向&#xff0c;推动实现的行业应用以及前沿研究探索方面的成果。一、两大难题攻坚&#xff1a;通用人工智能与数字人用…

跨平台PHP调试器设计及使用方法——探索和设计

在《跨平台PHP调试器设计及使用方法——立项》一文中&#xff0c;我确定了使用xdebug作为调试器插件部分的基础组件。xdebug提供了一个远程调试的功能&#xff08;相关资料可以详见https://xdebug.org/docs/remote&#xff09;&#xff0c;我们这个项目便是基于这个功能实现的。…

Ubuntu下允许Root用户直接登录图形界面

ubuntu root是默认禁用了&#xff0c;不允许用root登陆&#xff0c;所以先要设置root密码。 执行&#xff1a;sudo passwd root 接着输入密码和root密码&#xff0c;重复密码。再重新启动就可以用root登陆。 另外&#xff0c;默认情况下是不允许用root帐号直接登陆图形界面的。…

携程App for Apple Watch探索

在Apple Watch发布之后&#xff0c;很多App都针对它设计了相应的版本。旅行作为与Apple Watch时间管理特性契合度较高的场景&#xff0c;同时携程旅行作为国内领先的OTA行业App&#xff0c;也成为了首批适配Apple Watch并荣登Apple官网和App Store推荐的应用之一。InfoQ就App f…