当前位置: 首页 > news >正文

Linux系统编程-多路IO套接字

目录

有限状态机

多路IO

Select IO

1.select

2.FD_SET 

3.FD_ISSET

4.FD_CLR

5.FD_ZERO

6. pselect

Poll IO

Epoll IO

1.epoll_create

2.epol_create1

3.epoll_ctl 

4.epoll_wait

5.epoll_pwait

6.readv

7.writev

内存映射

文件锁

网络套接字

1.socket

2.bind 

3.listen 

4.accept 

5.connect 

6.send 

7.recv 

8.close 

9.sendto 

10.recvfrom 

广播

多播/组播(IP_MUTICAST_IF)

端口复用

多进程TCP通信实例

proto.h

server.c

client.c

UDP多播实例

proto.h

server.c

client.c


有限状态机

        有限状态机(Finite State Machine,简称 FSM)编程是一种设计范式,它使用有限状态机的概念来设计和实现软件系统。有限状态机是一种计算模型,它由一组状态以及在这些状态之间的转移组成。每个状态都对应于系统在某一特定时间点的特定行为或条件。状态机在接收到输入或触发事件时,会根据当前状态和输入来决定转移到哪个新的状态。

有限状态机编程的关键要素包括:
        -状态(States):状态是系统可以处于的明确定义的条件或情况。每个状态都对应着系统行为的一个特定方面。
        -转移(Transitions):转移是状态之间的连接,它们定义了从一个状态到另一个状态的规则。转移通常由事件触发。
        -事件(Events):事件是导致状态转移的触发器。它们可以是外部输入、内部条件或时间延迟。
        -初始状态(Initial State):这是状态机开始时的状态。
        -终止状态(Final States):在某些状态机中,当达到某个特定状态时,状态机的执行会停止。
        -动作(Actions):与状态转移相关联的操作,可以在进入或退出某个状态时执行。

有限状态机编程的步骤通常包括:
        定义所有可能的状态。
        定义触发状态转移的事件。
        定义每个状态下可执行的动作。
        实现状态转移逻辑。


多路IO

Select IO

1.select

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);struct timeval {time_t      tv_sec;         /* seconds */suseconds_t tv_usec;        /* microseconds */
};

        检查一组文件描述符,确定它们中是否有任何一个准备好了进行非阻塞读、写或有异常条件。成功时返回准备好的文件描述符的数量,失败时返回 -1。
    -int nfds:要检查的最大文件描述符加一。
    -fd_set *readfds:指向要检查读状态的文件描述符集合的指针。
    -fd_set *writefds:指向要检查写状态的文件描述符集合的指针。
    -fd_set *exceptfds:指向要检查异常条件的文件描述符集合的指针(在大多数现代系统中,这个参数被忽略)。
    -struct timeval *timeout:指定 select 等待的时间长度,如果设置为 NULL,则 select 会无限期地等待。

2.FD_SET 

void FD_SET(int fd, fd_set *set);

        将指定的文件描述符添加到集合中。

3.FD_ISSET

int FD_ISSET(int fd, fd_set *set);

        检查文件描述符是否在集合中。通常用于 select 返回后,检查哪些文件描述符已经准备好。

4.FD_CLR

void FD_CLR(int fd, fd_set *set);

        从集合中移除指定的文件描述符。

5.FD_ZERO

void FD_ZERO(fd_set *set);

        初始化一个文件描述符集合,将所有文件描述符从集合中移除。

6. pselect

int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);struct timespec {time_t      tv_sec;         /* seconds */long        tv_nsec;        /* nanoseconds */
};

Poll IO

int poll(struct pollfd *fds, nfds_t nfds, int timeout);struct pollfd {int   fd;         /* file descriptor */short events;     /* 16位位图宏,监视的行为 */short revents;    /* returned events */
};

        以文件描述符为单位组织事件.是结构体数组的起始位置, int timeout: 指定 poll 函数等待 I/O 操作变为可进行状态的最大时间(以毫秒为单位)。如果设置为 -1,则表示无限期等待。成功时,返回正数,表示至少有一个文件描述符已经准备好进行 I/O 操作。错误时,返回 -1,并设置 errno 来指示错误类型。


Epoll IO

1.epoll_create

int epoll_create(int size);

        创建一个 epoll 实例。size 参数指定了可以监听的文件描述符的数量。从 Linux 2.6.8 开始,这个参数被忽略,因为内核可以动态调整大小.成功时返回新创建的 epoll 实例的文件描述符,失败时返回 -1。

2.epol_create1

int epoll_create1(int flags);

        与epoll_create 类似,但允许通过 flags 参数指定额外的选项。目前只有 EPOLL_CLOEXEC 标志被支持,它使得 epoll 实例在执行 exec 系列函数时不会被继承。

3.epoll_ctl 

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);struct epoll_event {uint32_t events;  /* Epoll events */epoll_data_t data; /* User data variable */
};typrdef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64;
} epoll_data_t;

        控制对 epoll 实例的操作。vent 是一个指向 epoll_event 结构的指针,指定了感兴趣的事件。成功时返回 0,失败时返回 -1。

4.epoll_wait

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

        等待 epoll 实例中的事件。epfd 是 epoll 实例的文件描述符,events 是一个数组,用于接收触发的事件,maxevents 是数组的大小,timeout 是等待时间(单位为毫秒),0 表示立即返回,-1 表示无限等待。成功时返回触发的事件数量,失败时返回 -1。

5.epoll_pwait

int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask);

        与 epoll_wait 类似,但允许在等待期间指定一个信号掩码,以屏蔽某些信号。

6.readv

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);struct iovec {void *iov_base;  // 指向数据缓冲区的指针size_t iov_len;   // 缓冲区的长度
};

        从指定的文件描述符读取数据到多个缓冲区中,iov:指向 iovec 结构数组的指针,每个 iovec 包含一个指向缓冲区的指针和一个缓冲区长度。iovcnt:iovec 数组中的元素数量。成功时返回读取的字节数;失败时返回 -1 并设置 errno。

7.writev

ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

        将多个缓冲区的数据写入到指定的文件描述符。成功时返回写入的字节数;失败时返回 -1 并设置 errno。


内存映射

void *mmap(void addr[.length], size_t length, int prot, int flags, int fd, off_t offset);

        将文件或其他对象映射到进程的地址空间中,使得文件的内容可以像访问内存一样被访问。这种方式可以提高文件访问的效率,特别是在需要频繁访问文件内容时。成功返回映射地址,失败返回MAP_FAILED

 -addr:一个指针,指定映射区域的起始地址。如果 addr 是 NULL,则系统选择映射区域的地址;否则,系统会尝试将映射区域放在 addr 指定的地址附近。


-length:映射区域的长度,单位为字节。


-prot:映射区域的保护方式,可以是以下标志的组合:
            -PROT_EXEC:允许执行映射区域的内容。
            -PROT_READ:允许读取映射区域的内容。
            -PROT_WRITE:允许写入映射区域的内容。
            -PROT_NONE:不允许任何访问。


-flags:控制映射区域的行为,可以是以下标志的组合(SHARED和PRIVATE二者必选其一):
            -MAP_SHARED:映射区域对写操作是共享的,即写入映射区域的内容会反映到文件中。
            -MAP_PRIVATE:映射区域是私有的,写入映射区域的内容不会反映到文件中,而是复制到一个新创建的文件中。
            -MAP_ANONYMOUS:创建一个匿名映射,不与任何文件关联。
            -MAP_FIXED:强制将映射区域放在 addr 指定的地址,否则失败。
            -MAP_GROWSDOWN:允许映射区域向下扩展。
            -MAP_DENYWRITE:禁止写入映射区域。
            -MAP_EXECUTABLE:允许执行映射区域的内容。
            -MAP_LOCKED:锁定映射区域,防止被交换到磁盘。
            -MAP_NORESERVE:不保留交换空间。
            -MAP_POPULATE:预读映射区域的页面。


-fd:文件描述符,指定要映射的文件。如果 flags 包含 MAP_ANONYMOUS,则此参数被忽略。


-offset:文件映射的起始偏移量,通常以字节为单位。如果 flags 有MAP_ANONYMOUS,则此参数被忽略。

 

int munmap(void addr[.length], size_t length);

        解除addr的内存映射, length是长度.成功返回0,失败返回-1或errno


文件锁

        为什么会有文件锁呢?因为文件的inode可能被多个文件描述符(file descriptors)共享。这意味着多个进程或线程可能通过不同的文件描述符访问同一个文件而这些文件描述符可能指向同一个inode。文件锁通常有两种类型:文件级锁(flock)记录级锁(lockf)。文件级锁锁定整个文件,而记录级锁则锁定文件的特定部分。如果锁的粒度不够细,可能会在不应该解锁的情况下意外解锁,假设A和B两个进程使用不同的fd,操控同一个inode的文件,A对文件加锁,B对文件解锁,在A进程看来就发生了意外解锁。

int flock(int fd, int op);

        用于对文件进行加锁或解锁操作,以实现进程间的同步


-fd:文件描述符,表示要加锁或解锁的文件。这个文件描述符必须有效,并且已经打开用于读取或写入。


-op:操作类型,定义了要执行的锁操作,可以是以下值之一:
            -LOCK_SH:共享锁(Shared lock)。如果其他进程已经持有这个文件的共享锁,当前进程可以获取共享锁,但是不能获取独占锁。
            -LOCK_EX:独占锁(Exclusive lock)。如果其他进程已经持有这个文件的任何类型的锁,当前进程不能获取独占锁。
            -LOCK_NB:非阻塞模式(Non-blocking)。这个标志可以与 LOCK_SH 或 LOCK_EX 结合使用,表示如果锁不能立即被获取,flock 将立即返回错误而不是等待。
            -LOCK_UN:解锁操作(Unlock)。释放当前进程持有的锁。

int lockf(int fd, int op, off_t len);

        是一个系统调用,用于在文件上执行加锁或解锁操作,与 flock 类似,但它提供了更细粒度的控制。lockf 允许你指定锁定文件的特定部分,而不是整个文件。


-fd:文件描述符,表示要加锁或解锁的文件。这个文件描述符必须有效,并且已经打开用于读取或写入。


-op:操作类型,定义了要执行的锁操作,可以是以下值之一:
            -F_LOCK:请求一个锁定。
            -F_TLOCK:请求一个测试并锁定。如果文件已经被锁定,调用将失败并返回错误。
            -F_ULOCK:释放一个锁定。


-len:锁定区域的长度。这个值指定了从当前文件位置开始的字节数。如果 len 是 0,锁定将从当前位置开始一直延伸到文件的末尾。


网络套接字

跨主机的传输要注意的问题
字节序
- 大端 低地址放高字节
- 小端 高地址放低字节(x86)

- 主机字节序 host
- 网络字节序 network
- _ to _ 长度()
    - htons()
    - htonl()
    - ntohs()
    - ntohl()

socket:
        一个中间层,连接网络协议与文件操作
        socket就是插座,与兴在计算机中两个从小通过socket建立起一个通道,数据在通道中传输
        socket把复杂的TCP/IP协议族隐藏了起来,对于程序元来说只要用好socket相关的函数接可以完成网络通信
        socket提供了`stream` `datagram` 两种通信机制,即流socket和数据包socket,流socket基于TCP协议,是一个有序、可靠、双向字节刘的通道,传输数据不会丢失、不会重复、顺序也不会错乱,数据包socket基于UDP协议,不需要建立和尉迟连接,可能会丢失或错乱。UDP不是一个可靠的协议,对数据的长度有限制,但是效率较高

1.socket

int socket(int domain, int type, int protocol);

        创建一个socket套接字

2.bind 

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

        将套接字绑定到特定的地址

3.listen 

int listen(int sockfd, int backlog);

        设置监听上限并监听传入的连接请求

4.accept 

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

        接受一个连接请求,返回一个新的套接字描述符

5.connect 

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

        连接到指定的服务器地址

6.send 

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

        向套接字发送数据

7.recv 

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

        从套接字接收数据

8.close 

int close(int sockfd);

        关闭套接字

9.sendto 

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

        向一个数据报套接字发送数据到指定地址

10.recvfrom 

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

        从数据报套接字接收数据


广播

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

        设置套接字选项

-sockfd:指定要操作的套接字的文件描述符。


-level:指定选项的协议层。常见的值包括SOL_SOCKET(套接字层),或者特定的协议层,如IPPROTO_TCP(TCP层)。


-optname:指定要获取或设置的选项的名称。不同的level值有不同的选项名称。


-optval:指向一个缓冲区,用于存放获取的选项值(对于getsockopt)或存放要设置的新值(对于setsockopt)。缓冲区的大小由optlen参数指定。


-optlen:一个socklen_t类型的值,表示optval缓冲区的大小。对于getsockopt,它应该在调用前被设置为缓冲区的大小,调用后,系统会更新这个值以反映实际的选项值大小。对于setsockopt,它应该被设置为要设置的选项值的大小。

多播/组播(IP_MUTICAST_IF)

        相较广播更灵活,`224.0.0.1` 这个地址表示所有支持多播的节点默认都存在于这个组中且无法离开,往这个地址发送相当于往255.255.255.255发消息

setsockopt(sfd, IPPROTO_IP, IP_MULTICAST_IF, &moptval, sizeof(optval));

端口复用

setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &valopt, sizeof(valopt));

多进程TCP通信实例

proto.h

#ifndef PROTO_H__
#define PROTO_H__#include <stdint.h>#define NAMEMAX 512-8-8//(UDP推荐长度-UDP报头长度-结构体的长度)
#define FMT_STAMP "%lld\n"
#define SERVERPORT "2333"#endif

server.c

#include <asm-generic/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdarg.h>
#include <time.h>#include "proto.h"#define IPSIZE 1024
#define BUFSIZE 1024
#define SERVERPORT "2333"static void debug(char *fmt,...){va_list ap;va_start(ap,fmt);printf("DEBUG ");printf(fmt,va_arg(ap,int));va_end(ap);
}static void server_job(int newsd){char buf[BUFSIZE];int pkglen = 0;pkglen = sprintf(buf,FMT_STAMP,(long long)time(NULL));if (send(newsd,buf,pkglen,0) < 0){perror("send()");exit(1);}
}int main()
{int sfd;struct sockaddr_in laddr;//local addrstruct sockaddr_in raddr;//remote addrchar ip[IPSIZE];sfd = socket(AF_INET,SOCK_STREAM,0/*IPPROTO_TCP*/);if (sfd < 0){perror("socket()");exit(1);}int val = 1;if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val)) < 0){perror("setsockopt()");exit(1);}laddr.sin_family = AF_INET;//指定协议laddr.sin_port = htons(atoi(SERVERPORT));//指定网络通信端口inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);//IPv4点分式转二进制数if(bind(sfd,(void *)&laddr,sizeof(laddr)) < 0){perror("bind()");exit(1);}if(listen(sfd,1024) < 0){//全连接数量perror("listen()");exit(1);}socklen_t raddr_len = sizeof(raddr);pid_t pid;while(1){int newsd;newsd = accept(sfd,(void *)&raddr,&raddr_len);//接收客户端连接if (newsd < 0){perror("accept()");exit(1);}pid = fork();if (pid < 0){perror("fork()");exit(1);}if (pid == 0){close(sfd);inet_ntop(AF_INET,&raddr.sin_addr,ip,IPSIZE);printf("client %s %d\n",ip,ntohs(raddr.sin_port));server_job(newsd);close(newsd);exit(0);}close(newsd);//父子进程必须都将打开的来自client的socket关闭,否则socket不会返回client}close(sfd);exit(0);
}

client.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <math.h>
#include <string.h>#include "proto.h"#define BUFSIZE 1024int main()
{int sfd;struct sockaddr_in raddr;//remote addrsfd = socket(AF_INET,SOCK_STREAM,0/*IPPROTO_TCP*/);raddr.sin_family = AF_INET;raddr.sin_port = htons(atoi(SERVERPORT));inet_pton(AF_INET,"127.0.0.1",&raddr.sin_addr);if(connect(sfd,(void *)&raddr,sizeof(raddr)) < 0){perror("connect()");exit(1);}FILE *fp;fp = fdopen(sfd,"r+");if (fp == NULL){perror("fopen()");exit(1);}long long stamp;if (fscanf(fp,FMT_STAMP,&stamp) < 1){fprintf(stderr,"Bad format\n");}else{fprintf(stdout,FMT_STAMP,stamp);}close(sfd);exit(0);
}

UDP多播实例

proto.h

#ifndef PROTO_H__
#define PROTO_H__#include <stdint.h>#define NAMEMAX 512-8-8//(UDP推荐长度-UDP报头长度-结构体的长度)
#define  MULTICASTADDR "224.2.2.2"struct msg_st{uint32_t math;uint32_t chinese;char name[0];
}__attribute__((packed));//不对齐#endif

server.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdarg.h>#include "proto.h"#define IPSIZE 1024
#define SERVERPORT "2333"static void debug(char *fmt,...){va_list ap;va_start(ap,fmt);printf("DEBUG ");printf(fmt,va_arg(ap,int));va_end(ap);
}int main()
{int sfd;struct sockaddr_in laddr;//local addrstruct sockaddr_in raddr;//remote addrstruct msg_st *rbuf;char ip[IPSIZE];int pkglen = sizeof(struct msg_st)+NAMEMAX;rbuf = malloc(pkglen);if (rbuf == NULL){perror("malloc()");exit(1);}sfd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);if (sfd < 0){perror("socket()");exit(1);}//设置socket属性struct ip_mreqn mreqn;inet_pton(AF_INET,"0.0.0.0",&mreqn.imr_address);//224.0.0.1 这个地址表示所有支持多播的节点默认都存在于这个组中且无法离开inet_pton(AF_INET,MULTICASTADDR,&mreqn.imr_multiaddr);mreqn.imr_ifindex = if_nametoindex("wlp7s0");if (setsockopt(sfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreqn,sizeof(mreqn)) < 0){perror("setsockopt()");exit(1);}laddr.sin_family = AF_INET;//指定协议laddr.sin_port = htons(atoi(SERVERPORT));//指定网络通信端口inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);//IPv4点分式转二进制数if(bind(sfd,(void *)&laddr,sizeof(laddr)) < 0){perror("bind()");exit(1);}socklen_t raddr_len = sizeof(raddr);while(1){recvfrom(sfd,rbuf,pkglen,0,(void *)&raddr,&raddr_len);//报式套接字每次通信都需要知道对方是谁inet_ntop(AF_INET,&raddr.sin_addr,ip,IPSIZE);printf("%s %d\n",ip,ntohs(raddr.sin_port));printf("%s %d %d\n",rbuf->name,ntohl(rbuf->math),ntohl(rbuf->chinese));fflush(NULL);}close(sfd);exit(0);
}

client.c

#include <asm-generic/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <math.h>
#include <string.h>#include "proto.h"#define SERVERPORT "2333"int main()
{int sfd;struct msg_st *sbuf;struct sockaddr_in raddr;//remote addrstruct ip_mreqn mreqn;sfd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);if(sfd < 0){perror("socket()");exit(1);}//设置socket的属性inet_pton(AF_INET,"0.0.0.0",&mreqn.imr_address);inet_pton(AF_INET,MULTICASTADDR,&mreqn.imr_multiaddr);mreqn.imr_ifindex = if_nametoindex("wlp7s0");if (setsockopt(sfd,IPPROTO_IP,IP_MULTICAST_IF,&mreqn,sizeof(mreqn)) < 0){perror("setsockopt()");exit(1);}//打开广播属性int pkglen = sizeof(struct msg_st)+strlen("Mike")+1;// 注意给'/0'留位置sbuf = malloc(pkglen);if (sbuf == NULL){perror("malloc()");exit(1);}char *name = "Mike";strcpy(sbuf->name,name);sbuf->math = htonl(rand()%100);//主机字节序转网络字节序sbuf->chinese = htonl(rand()%100);raddr.sin_family = AF_INET;raddr.sin_port = htons(atoi(SERVERPORT));inet_pton(AF_INET,MULTICASTADDR,&raddr.sin_addr);if(sendto(sfd,sbuf,pkglen,0,(void *)&raddr,sizeof(raddr)) < 0){perror("sendto()");exit(1);}puts("OK");close(sfd);exit(0);
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • GenAI 会消灭软件开发人员的工作吗?
  • Modbus通讯协议
  • linux top
  • 蓝牙网关北京厂家_蓝牙网关型号价格介绍
  • 软件安全测试内容和方法大揭秘,湖南软件测评公司推荐
  • ubuntu22安装k8s-1.24.17
  • 07 Redis List类型操作与使用场景
  • 【Vulnhub靶场AI-WEB-1.0打靶教程】
  • MySQL——日期与时间类型
  • 1000W长连接,如何建立和维护?千万用户IM 架构设计
  • PHP苹果 V X iPhone微商i o s多分开V X语音转发密友朋友圈一键跟圈软件
  • C#中读写INI配置文件
  • 前端入门知识分享:HTML 页面中 head 标签之间的代码详解
  • Laravel API资源收集器:打造高效数据响应的秘诀
  • springboot智能健康管理平台-计算机毕业设计源码57256
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • jQuery(一)
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • SOFAMosn配置模型
  • Webpack 4 学习01(基础配置)
  • 阿里研究院入选中国企业智库系统影响力榜
  • 聊聊flink的TableFactory
  • 入口文件开始,分析Vue源码实现
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 再次简单明了总结flex布局,一看就懂...
  • ​Java并发新构件之Exchanger
  • ​字​节​一​面​
  • # linux 中使用 visudo 命令,怎么保存退出?
  • #if和#ifdef区别
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (9)STL算法之逆转旋转
  • (LeetCode 49)Anagrams
  • (二开)Flink 修改源码拓展 SQL 语法
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (一) storm的集群安装与配置
  • (转)http-server应用
  • (转)Linux整合apache和tomcat构建Web服务器
  • (转)nsfocus-绿盟科技笔试题目
  • (转)关于多人操作数据的处理策略
  • ***利用Ms05002溢出找“肉鸡
  • .NET COER+CONSUL微服务项目在CENTOS环境下的部署实践
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法
  • .net core 依赖注入的基本用发
  • .NET Project Open Day(2011.11.13)
  • .NET 回调、接口回调、 委托
  • .NET/C# 的字符串暂存池
  • .NET国产化改造探索(一)、VMware安装银河麒麟
  • ?php echo $logosrc[0];?,如何在一行中显示logo和标题?
  • @EnableConfigurationProperties注解使用
  • @GlobalLock注解作用与原理解析
  • [ vulhub漏洞复现篇 ] JBOSS AS 5.x/6.x反序列化远程代码执行漏洞CVE-2017-12149
  • [ 手记 ] 关于tomcat开机启动设置问题
  • [1181]linux两台服务器之间传输文件和文件夹
  • [20171102]视图v$session中process字段含义