Table of Contents

怎么个加速法?

我的网络拓扑

开始编码

服务端

server.c

common.h

Makefile

配置文件server.ini

客户端代码

client.c

common.h

common.c

Makefile

运行结果


最近在做DPDK相关的网口加速的内容,开始采用DPDK+VPP的方式,但鉴于VPP的部署需要花费一定的时间,遂采用F-Stack,关于F-Stack,本文不做介绍,只说明它是腾讯开发的。

鉴于“一图胜过千言”,本文将以图+简短的话为例

怎么个加速法?

F-Stack不走内核协议栈,通过移植BSD的协议栈和DPDK进行联合,组成一套完整的用户态协议栈。

我准备用两个万兆网卡加速,如下图

然后在采用一个网卡运行服务端,一个网卡运行客户端,

也可以总结成这样

可是最后都初始化ff_init后法宝不成功,最终我确定了一个方案

也就是万兆网卡运行客户端进行收发包,内核协议栈控制的千兆网卡运行客户端收发包。

我的网络拓扑

一开始我的拓扑是下面这样

我从我的虚拟机进行对左侧X86的控制,左面服务器运行客户端和服务端,但是好像哪里不对,因为我要让数据像下面这样走

当然是少了一根线,可以有两种连接方法

我踩用第一种,如下

开始编码

https://download.csdn.net/download/Rong_Toa/12631153

服务端

server.c

/***  测试F-Stack的UDP发包速率*  作者:荣涛 <rongtao@sylincom.com>*  时间:2020年7月16日10:08:34*/
#include "common.h"#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <assert.h>#include <sys/socket.h>
#include <netinet/ip.h>
#include <sys/epoll.h>int sockfd;int epfd;
struct epoll_event ev;
struct epoll_event events[MAX_EVENTS];char buf[MAXLINE] = {"100000000"};int udpsocket_server()
{int sockfd;struct sockaddr_in servaddr;bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(PORT);if((sockfd = ff_socket(AF_INET, SOCK_DGRAM, 0)) < 0) {return -1;}if(ff_bind(sockfd, (struct linux_sockaddr *)&servaddr, sizeof(servaddr))) {return -1;}return sockfd;
}long int direct_sendto_addr(int sockfd, const char *dst_ip, int port, long int n_pkg)
{struct sockaddr_in cliaddr;int n = 0;long int npkg = 0, nbyte=0;socklen_t len;len = sizeof(cliaddr);struct timeval tvbrfore, tvafter;bzero(&cliaddr, sizeof(struct sockaddr_in));cliaddr.sin_family = AF_INET;cliaddr.sin_port = htons(port);inet_pton(AF_INET, dst_ip, &cliaddr.sin_addr);/* 统计时间 */gettimeval(&tvbrfore);/* 循环发包,测速率 */while(1) {if((n = ff_sendto(sockfd, buf, MAXLINE, 0, (struct linux_sockaddr *)&cliaddr, len)) < 0){log_warn("sendto error");exit(1);} else {nbyte += n;if(npkg++ > n_pkg) {break;}}}/* 统计时间 */gettimeval(&tvafter);/* 输出此段时间内的速率 */statistic_throughput("Sendto", &tvbrfore, &tvafter, nbyte, npkg-1);
}int server_loop(void *arg)
{int n;socklen_t len;struct sockaddr_in cliaddr;struct timeval tvbrfore, tvafter;#if HAVE_FSTACK!=1while(1) {log_warn("Wait for a Client epoll_wait...\n");
#endif/* Wait for events to happen */int nevents = ff_epoll_wait(epfd,  events, MAX_EVENTS, -1);int i;len = sizeof(cliaddr);#if HAVE_FSTACK!=1log_warn("nevents = %d\n", nevents);
#endiffor (i = 0; i < nevents; ++i) {/* 如果是UDP的服务端, 接收一个来自客户端的消息,获取客户端地址,用于向客户端发送数据测发送速率 */if (events[i].data.fd == sockfd) {if((n = ff_recvfrom(sockfd, buf, MAXLINE, 0,  (struct linux_sockaddr *)&cliaddr, &len)) < 0){log_warn("recvfrom error\n");} else {log_warn("Server recv   %s\n", buf);log_warn("Client Family %d\n", cliaddr.sin_family);log_warn("Client Port   %u\n", ntohs(cliaddr.sin_port));log_warn("Client Addr   %s\n", inet_ntoa(cliaddr.sin_addr));long int npkg = 0, nbyte=0;long int Nkpg = atol(buf);
#if HAVE_FSTACK==1Nkpg = Nkpg>1000000?Nkpg:1000000;
#elseNkpg = Nkpg>10000?Nkpg:10000;
#endiflog_warn("Ready Send N Pkg %ld\n", Nkpg);/* 告诉我向客户端发了啥 */
#define MY_ID   "[FSTACK-DPDK][RongTao Test][牛逼了老哥]"strcpy(buf, MY_ID);/* 统计时间 */gettimeval(&tvbrfore);/* 循环发包,测速率 */while(1) {if((n = ff_sendto(sockfd, buf, MAXLINE, 0, (struct linux_sockaddr *)&cliaddr, len)) < 0){log_warn("sendto error");exit(1);} else {nbyte += n;if(npkg++ > Nkpg) {break;}}}/* 统计时间 */gettimeval(&tvafter);/* 输出此段时间内的速率 */statistic_throughput("Sendto", &tvbrfore, &tvafter, nbyte, npkg-1);}}}#if HAVE_FSTACK!=1}
#endif}int main(int argc, char *argv[])
{/* 绑核 */setaffinity(7);#if HAVE_FSTACK==1ff_init(argc, argv);
#endiflog_warn("Init socket.\n");sockfd = udpsocket_server();assert((epfd = ff_epoll_create(10)) > 0);ev.data.fd = sockfd;ev.events = EPOLLIN;ff_epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);log_warn("Sockfd %d Wait for a Client...\n", sockfd);#if 1#if HAVE_FSTACK==1log_warn("Ready to ff_run.\n");ff_run(server_loop, NULL);
#elseserver_loop(NULL);
#endif
#else //直接发送 direct_sendto_addr(sockfd, "10.170.6.66", PORT, 10000);
#endif
}

common.h

/***  测试F-Stack的UDP发包速率*  作者:荣涛 <rongtao@sylincom.com>*  时间:2020年7月16日10:08:34*/
#ifndef __COMMON_H
#define __COMMON_H 1#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/sysinfo.h>
#include <time.h>
#include <stdint.h>#define __USE_GNU
#include <sched.h>
#include <ctype.h>
#include <string.h>#include <stdarg.h>
#include <libgen.h>#include "ff_config.h"
#include "ff_api.h"
#include "ff_epoll.h"/* 使用 fstack 进行UDP发包速率测试=1 使用 linux 进行UDP发包速率测试=1
*/
#define HAVE_FSTACK 1#define PORT 2152#define MAXLINE 1500#define MAX_EVENTS 10#define TEST_ADDR1    "10.170.7.169"
#define TEST_ADDR2    "10.170.7.170"/***  打印log debug信息*  作者: 荣涛*  时间: s2020年7月15日10:12:24*/
enum {__LV_INFO,__LV_WARNING,__LV_ERR,__LV_DEBUG,
};#define LOG_DEBUG 1
#ifdef LOG_DEBUG
#define log_info(fmt...) ___debug_log(__LV_INFO, __FILE__, __func__ ,__LINE__, fmt)
#define log_warn(fmt...) ___debug_log(__LV_WARNING, __FILE__, __func__ ,__LINE__, fmt)
#define log_error(fmt...) ___debug_log(__LV_ERR, __FILE__, __func__ ,__LINE__, fmt)
#define log_debg(fmt...) ___debug_log(__LV_DEBUG, __FILE__, __func__ ,__LINE__, fmt)
#else
#define log_info(fmt...)
#define log_warn(fmt...)
#define log_error(fmt...)
#define log_debg(fmt...)
#define log_errorno(i_errno)
#endif#if HAVE_FSTACK!=1
#define ff_socket socket
#define ff_bind bind
#define ff_recvfrom recvfrom
#define ff_sendto sendto
#define linux_sockaddr sockaddr
#define ff_epoll_wait epoll_wait
#define ff_epoll_create epoll_create
#define ff_epoll_ctl epoll_ctl
#endifstatic inline int ___debug_log(int level, char *file, const char *func, int line, char *fmt, ...)
{va_list av;va_start(av, fmt);switch(level) {case __LV_INFO:printf(" [%sINFO%s][%s:%s:%d]: ","\033[1;36m","\033[0m", basename(file), func, line);break;case __LV_WARNING:printf(" [%sWARN%s][%s:%s:%d]: ","\033[1;35m","\033[0m", basename(file), func, line);break;case __LV_ERR:printf("[%sERROR%s][%s:%s:%d]: ","\033[1;31m","\033[0m", basename(file), func, line);break;case __LV_DEBUG:printf("[%sDEBUG%s][%s:%s:%d]: ","\033[1m",   "\033[0m", basename(file), func, line);break;}int i = vprintf(fmt, av);va_end(av);return i;
}static inline long int gettimeval(struct timeval *tv)
{gettimeofday(tv, NULL);
}
static inline void statistic_throughput(char *description, struct timeval *before, struct timeval *after, unsigned long int bytes, long int npkg
)
{
//    printf("\t -- before time: %ld, %ld\n", before->tv_sec, before->tv_usec);
//    printf("\t --  after time: %ld, %ld\n", after->tv_sec, after->tv_usec);printf("-- %s: Total %.3lf Mbps, bytes = %ld(bits:%ld), npkg = %ld.\n", description?description:"Unknown Description", 8*bytes*1.0/((after->tv_sec-before->tv_sec)*1000000+after->tv_usec-before->tv_usec), bytes, bytes*8, npkg);
}static void setaffinity(long int ncpu)
{
//    long int ncpu = sysconf (_SC_NPROCESSORS_ONLN);cpu_set_t cpuset;CPU_ZERO(&cpuset);CPU_SET(ncpu>1?ncpu-1:1, &cpuset);int ret = sched_setaffinity(getpid(), sizeof(cpuset), &cpuset);log_warn("setaffinity ret = %d\n", ret);int j;for(j=0;j<CPU_SETSIZE; j++){if(CPU_ISSET(j, &cpuset)){printf("CPU_SETSIZE = %d, j = %d, cpuset = %d\n", CPU_SETSIZE, j, cpuset);CPU_CLR(j, &cpuset);printf("CPU_SETSIZE = %d, j = %d, cpuset = %d\n", CPU_SETSIZE, j, cpuset);}}ret = sched_getaffinity(getpid(), sizeof(cpuset), &cpuset);for(j=0;j<CPU_SETSIZE; j++){if(CPU_ISSET(j, &cpuset)){printf("CPU_SETSIZE = %d, j = %d, cpuset = %d\n", CPU_SETSIZE, j, cpuset);}}
}#endif /*__COMMON_H*/

Makefile

TOPDIR=..ifeq ($(FF_PATH),)FF_PATH=${TOPDIR}
endififeq ($(FF_DPDK),)FF_DPDK=${TOPDIR}/dpdk/x86_64-native-linuxapp-gcc
endifLIBS+= -L${FF_PATH}/lib -Wl,--whole-archive,-lfstack,--no-whole-archive
LIBS+= -L${FF_DPDK}/lib -Wl,--whole-archive,-ldpdk,--no-whole-archive
LIBS+= -Wl,--no-whole-archive -lrt -lm -ldl -lcrypto -pthread -lnumaall:cc -O -gdwarf-2  -I../lib -o server server.c ${LIBS}.PHONY: clean
clean:rm -f *.o server client

配置文件server.ini

# F-Stack 参数配置 <rongtao@sylincom.com>
[dpdk]
# Hexadecimal bitmask of cores to run on.
# 绑核
lcore_mask=3# Number of memory channels.
channel=8# Specify base virtual address to map.
#base_virtaddr=0x7f0000000000# Promiscuous mode of nic, defualt: enabled.
# 网卡的混杂模式
promiscuous=1
numa_on=1# TX checksum offload skip, default: disabled.
# We need this switch enabled in the following cases:
# -> The application want to enforce wrong checksum for testing purposes
# -> Some cards advertize the offload capability. However, doesn't calculate checksum.
tx_csum_offoad_skip=0# TCP segment offload, default: disabled.
tso=0# HW vlan strip, default: enabled.
vlan_strip=1# sleep when no pkts incomming
# unit: microseconds
idle_sleep=0# sent packet delay time(0-100) while send less than 32 pkts.
# default 100 us.
# if set 0, means send pkts immediately.
# if set >100, will dealy 100 us.
# unit: microseconds
pkt_tx_delay=100# enabled port list
#
# EBNF grammar:
#
#    exp      ::= num_list {"," num_list}
#    num_list ::= <num> | <range>
#    range    ::= <num>"-"<num>
#    num      ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
#
# examples
#    0-3       ports 0, 1,2,3 are enabled
#    1-3,4,7   ports 1,2,3,4,7 are enabled
#
# If use bonding, shoule config the bonding port id in port_list
# and not config slave port id in port_list
# such as, port 0 and port 1 trank to a bonding port 2,
# should set `port_list=2` and config `[port2]` section
port_list=0,1# Number of vdev.
nb_vdev=0# Number of bond.
nb_bond=0# Each core write into own pcap file, which is open one time, close one time if enough.
# Support dump the first snaplen bytes of each packet.
# if pcap file is lager than savelen bytes, it will be closed and next file was dumped into.
[pcap]
enable = 0
snaplen= 16777216
savelen= 16777216# Port config section
# Correspond to dpdk.port_list's index: port0, port1...
[port0]
addr=10.170.7.169
netmask=255.255.255.0
broadcast=10.170.7.255
gateway=10.170.7.254[port1]
addr=10.170.7.170
netmask=255.255.255.0
broadcast=10.170.7.255
gateway=10.170.7.254# lcore list used to handle this port
# the format is same as port_list
lcore_list=0,1# bonding slave port list used to handle this port
# need to config while this port is a bonding port
# the format is same as port_list
#slave_port_list=0,1# Packet capture path, this will hurt performance
#pcap=./a.pcap# Vdev config section
# orrespond to dpdk.nb_vdev's index: vdev0, vdev1...
#    iface : Shouldn't set always.
#    path : The vuser device path in container. Required.
#    queues : The max queues of vuser. Optional, default 1, greater or equal to the number of processes.
#    queue_size : Queue size.Optional, default 256.
#    mac : The mac address of vuser. Optional, default random, if vhost use phy NIC, it should be set to the phy NIC's mac.
#    cq : Optional, if queues = 1, default 0; if queues > 1 default 1.
#[vdev0]
##iface=/usr/local/var/run/openvswitch/vhost-user0
#path=/var/run/openvswitch/vhost-user0
#queues=1
#queue_size=256
#mac=00:00:00:00:00:01
#cq=0# bond config section
# See http://doc.dpdk.org/guides/prog_guide/link_bonding_poll_mode_drv_lib.html
#[bond0]
#mode=4
#slave=0000:0a:00.0,slave=0000:0a:00.1
#primary=0000:0a:00.0
#mac=f0:98:38:xx:xx:xx
## opt argument
#socket_id=0
#xmit_policy=l23
#lsc_poll_period_ms=100
#up_delay=10
#down_delay=50# Kni config: if enabled and method=reject,
# all packets that do not belong to the following tcp_port and udp_port
# will transmit to kernel; if method=accept, all packets that belong to
# the following tcp_port and udp_port will transmit to kernel.
[kni]
enable=1
method=reject
# The format is same as port_list
tcp_port=1-65535
udp_port=1-65535# FreeBSD network performance tuning configurations.
# Most native FreeBSD configurations are supported.
[freebsd.boot]
hz=100# Block out a range of descriptors to avoid overlap
# with the kernel's descriptor space.
# You can increase this value according to your app.
fd_reserve=1024kern.ipc.maxsockets=262144net.inet.tcp.syncache.hashsize=4096
net.inet.tcp.syncache.bucketlimit=100net.inet.tcp.tcbhashsize=65536kern.ncallout=262144kern.features.inet6=1
net.inet6.ip6.auto_linklocal=1
net.inet6.ip6.accept_rtadv=2
net.inet6.icmp6.rediraccept=1
net.inet6.ip6.forwarding=0[freebsd.sysctl]
kern.ipc.somaxconn=32768
kern.ipc.maxsockbuf=16777216net.link.ether.inet.maxhold=5net.inet.tcp.fast_finwait2_recycle=1
net.inet.tcp.sendspace=16384
net.inet.tcp.recvspace=8192
#net.inet.tcp.nolocaltimewait=1
net.inet.tcp.cc.algorithm=cubic
net.inet.tcp.sendbuf_max=16777216
net.inet.tcp.recvbuf_max=16777216
net.inet.tcp.sendbuf_auto=1
net.inet.tcp.recvbuf_auto=1
net.inet.tcp.sendbuf_inc=16384
net.inet.tcp.recvbuf_inc=524288
net.inet.tcp.sack.enable=1
net.inet.tcp.blackhole=1
net.inet.tcp.msl=2000
net.inet.tcp.delayed_ack=0net.inet.udp.blackhole=1
net.inet.ip.redirect=0
net.inet.ip.forwarding=0

客户端代码

client.c

/***  测试F-Stack的UDP发包速率*  作者:荣涛 <rongtao@sylincom.com>*  时间:2020年7月16日10:08:34*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>#include "common.h"int main(int argc, char *argv[])
{int sockfd, n;struct sockaddr_in servaddr;socklen_t servlen = sizeof(servaddr);struct timeval tvbrfore, tvafter;long int npkg = 0, nbyte=0;char sendline[MAXLINE], recvline[MAXLINE + 1];if(argc != 2) {perror("Usage: udpcli <IPAddress>");exit(1);}sockfd = udpsocket_client(argv[1], &servaddr);printf("Client Input:");while(fgets(sendline, MAXLINE, stdin) != NULL){if(sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *)&servaddr, servlen) < 0){perror("sendto error");exit(1);} else {printf("Client send %d: %s", sockfd, sendline);}npkg = 0, nbyte=0;memset(&tvbrfore, 0, sizeof(struct timeval));memset(&tvafter, 0, sizeof(struct timeval));/* 统计时间 */my_gettimeval(&tvbrfore);while(1) {if((n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, &servlen)) < 0){perror("recvfrom error");break;} else {
//                printf("Client recv %d: %s", sockfd, recvline);nbyte += n;if(npkg++ > 10000) {break;}}recvline[n] = '\0';}/* 统计时间 */my_gettimeval(&tvafter);/* 输出此段时间内的速率 */my_statistic_throughput("Recvfrom", &tvbrfore, &tvafter, nbyte, npkg-1);/* 准备下次触发f_stack服务端 进行发包测试 */printf(">>");}return 1;
}

common.h

/***  测试F-Stack的UDP发包速率*  作者:荣涛 <rongtao@sylincom.com>*  时间:2020年7月16日10:08:34*/
#ifndef _COMMON_H
#define _COMMON_H#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/sysinfo.h>
#include <time.h>
#include <stdint.h>#define PORT        2152
#define MAXLINE     1500 int udpsocket_server();
int udpsocket_client(const char *ipv4, struct sockaddr_in *servaddr);inline long int my_gettimeval(struct timeval *tv);inline void my_statistic_throughput(char *description, struct timeval *before, struct timeval *after, unsigned long int bytes, long int npkg);#endif /*<_COMMON_H>*/

common.c

/***  测试F-Stack的UDP发包速率*  作者:荣涛 <rongtao@sylincom.com>*  时间:2020年7月16日10:08:34*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/ip.h>#include "common.h"int udpsocket_server()
{int sockfd;struct sockaddr_in servaddr;bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(PORT);if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){return -1;}if(bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))){return -1;}return sockfd;
}int udpsocket_client(const char *ipv4, struct sockaddr_in *servaddr)
{int sockfd, t;bzero(servaddr, sizeof(struct sockaddr_in));servaddr->sin_family = AF_INET;servaddr->sin_port = htons(PORT);if((t = inet_pton(AF_INET, ipv4, &servaddr->sin_addr)) <= 0){return -1;}if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){return -1;}return sockfd;
}inline long int my_gettimeval(struct timeval *tv)
{gettimeofday(tv, NULL);
}
inline void my_statistic_throughput(char *description, struct timeval *before, struct timeval *after, unsigned long int bytes, long int npkg)
{
//    printf("\t -- before time: %ld, %ld\n", before->tv_sec, before->tv_usec);
//    printf("\t --  after time: %ld, %ld\n", after->tv_sec, after->tv_usec);printf("-- %s: Total %.3lf Mbps, bytes = %ld(bits:%ld), npkg = %ld.\n", description?description:"Unknown Description", 8*bytes*1.0/((after->tv_sec-before->tv_sec)*1000000+after->tv_usec-before->tv_usec), bytes, bytes*8, npkg);
}

Makefile

ALL:gcc client.c common.c -o client -lm
clean:rm *~

运行结果

F-Stack实现UDP服务端、客户端,并进行吞吐量测试的实现相关推荐

  1. goudp文件传输服务器,golang udp服务端客户端例子

    服务端: package main import ( "fmt" "net" ) func main() { // 创建监听 socket, err := ne ...

  2. TCP/IP网络编程之基于TCP的服务端/客户端(一)

    TCP/IP网络编程之基于TCP的服务端/客户端(一) 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于 ...

  3. linux netty udp服务端,Netty实现UDP服务端

    ### 前言 在之前的文章我已经讲过了利用`Netty`实现`UDP`客户端,大家有兴趣的话,可以参看下面文章: [Netty实现UDP客户端](https://www.jianshu.com/p/5 ...

  4. linux 进程sockfd fork,Linux下多进程服务端客户端模型一(单进程与多进程模型)...

    本文将会简单介绍Linux下如何利用C库函数与系统调用编写一个完整的.初级可用的C-S模型. 一.基本模型: 1.1   首先服务器调用socket()函数建立一个套接字,然后bind()端口,开始l ...

  5. Python Django断点下载(服务端/客户端)

    断点下载 背景     断点续传/断点下载一直是每个系统最实用的功能,最近公司在复杂的网络环境(国外vps)下载东西遇到问题,有些文件下载的时候很慢,并且可能会下不下来,这种情况对一个系统的稳定性构成 ...

  6. Java的UDP服务端

    本文主要是讲Java的UDP服务端接收客户端消息的实现过程 package com.Demo; import java.io.IOException; import java.net.Datagram ...

  7. 开源Unity服务端客户端(双端C#)网络通讯框架(Lidgren)[一]

    开源Unity服务端客户端(双端C#)网络通讯框架(Lidgren)[一] 1. 简介 1.1 结识Lidgren的机缘巧合 开发Unity的各位或多或少都可能在工作中遇到非客户端开发的一些内容.大型 ...

  8. TCP聊天文件服务器v2.2 - 服务端客户端套接字解决分包/粘包问题 - SocketQueue继承以及减少冗余

    TCP聊天+传输文件服务器服务器套接字v2.2 整个图当封面吧 所有版本记录: v1.0 : TCP聊天服务器套接字|PyQt5+socket(TCP端口映射+端口放行)+logging+Thread ...

  9. react服务端/客户端,同构代码心得

    FKP-REST是一套全栈javascript框架 react服务端/客户端,同构代码心得 作者:webkixi react服务端/客户端,同构代码心得 服务端,客户端同构一套代码,大前端的梦想,为了 ...

  10. restful服务端客户端_测试RESTful服务的客户端

    restful服务端客户端 开发使用RESTful Web API的应用程序可能意味着开发服务器和客户端. 为服务器端编写集成测试可以像使用Arquillian启动服务器一样容易,并且可以通过REST ...

最新文章

  1. 小狗钱钱_✅每次构建待办事项列表应用程序时,都会有一只小狗? 死了?
  2. ActiveReports 报表应用教程 (2)---清单类报表
  3. android 自定义loading,android_自定义Loading框
  4. 以深圳.NET俱乐部名义 的技术交流会圆满成功
  5. 傅里叶变换的意义 .
  6. 职场上有3种类型的人,最后一种类型老板最喜欢,你是哪一类?
  7. csdn下载频道资源整理
  8. html文件中flash视频格式(flv、swf)文件的嵌入方法
  9. 教育技术资源大全(05-11-28)
  10. iOS 播放视频的基本步骤
  11. 基于MATLAB的指纹识别系统仿真设计,基于Matlab的指纹识别系统的研究与实现
  12. Golang开发新手常犯的50个错误
  13. Python二级应用题代码自用(无忧二级)
  14. QT绘制区域(ROI)框(矩形框和椭圆框)
  15. java 微博第三方登录_Connect/login - 微博API
  16. shell解决买鸡问题:3文钱可以买1只公鸡,2文钱可以买一只母鸡,1文钱可以买3只小鸡。用100 文 钱买100 只鸡,那么各有公鸡、母鸡、小鸡多少只?
  17. javascript 全栈_什么是JavaScript? 全栈编程语言
  18. 如何学好编程(一):什么叫编程
  19. 天气预报发展简史:从玄学到科学
  20. 关于centos7配置网卡qqf

热门文章

  1. JavaScript和HTML事件处理程序属性中的this的含义
  2. Python之爬虫(七)正则的基本使用
  3. [ 转载 ] Java基础12--基础学习总结——数组
  4. “疯狂猜成语”软件用户体验
  5. ATL是如何实现线程安全的引用计数和多线程控制的
  6. 一个自动化测试的案例之记事狗微博篇
  7. .NET 内存管理与垃圾回收:实现IDisposable接口和析构函数
  8. md5 java .net_.net, java MD5 加密 互换
  9. linux 解压 视频插件,linux下压缩解压缩命令
  10. mysql 5.6.13-winx64_MySQL-5.6.13 zip解压版的安装与配置教程