`
Poechant
  • 浏览: 229670 次
博客专栏
Bebe66e7-3a30-3fc9-aeea-cfa3b474b591
Nginx高性能Web服务...
浏览量:24331
5738817b-23a1-3a32-86de-632d7da73b1e
Cumulus实时媒体服务...
浏览量:22105
社区版块
存档分类
最新评论

柳大的Linux讲义·基础篇(4)网络编程基础

 
阅读更多

柳大的Linux游记·基础篇(4)网络编程基础

  • Author: 柳大·Poechant
  • Blog:Blog.CSDN.net/Poechant
  • Email:zhongchao.usytc#gmail.com (#->@)
  • Date:March 11th, 2012
  • Copyright © 柳大·Poechant(钟超·Michael)

回顾

  1. 《柳大的Linux游记·基础篇(1)磁盘与文件系统》
  2. 《柳大的Linux游记·基础篇(2)Linux文件系统的inode》
  3. 《柳大的Linux游记·基础篇(3)权限、链接与权限管理》

闲话

最近很忙,博文写的少。感谢一些博友的理解。

有博友发邮件说《柳大的Linux游记》希望继续写下去,希望了解一些与 socket 入门有关的内容。对此深表惭愧,当时也是应一个博友的来信而开始写这个系列的,但仅写了三篇就没继续了。与 socket 相关的文章,在网络上非常多,socket 编程也是基本功。要我来写的话,写出心意很难,我只希望能写系统一些,所以我想先介绍 socket 的基础,然后说说 select,poll 和 epoll 等 IO 复用技术,可能这样会系统一些,也更实用。

W. Richard StevensUNIX Network Programming Volume 1中讲解例子的时候都使用了include "unp.h",这是Stevens先生在随书源码中的提供的一个包含了所有 UNIX 网络编程会用到的头文件的的一个头文件。但这样对于不了解 UNIX 网络编程以及 socket 的朋友来说,并不是一个好的学习途径。所以我想看完本文后读Stevens先生的杰出作品更好一些 :)

另外,《JVM深入笔记》的第四篇正在整理,最近确实空闲时间比较少,对此感到很抱歉。我会尽量抽时间多分享一些的。

言归正传,下面还是沿袭我的一贯风格,先以最简单的实例开始。

目录

  1. 快速开始
    • 1.1 TCP C/S
      • 1.1.1 TCP Server
      • 1.1.2 TCP Client
    • 1.2. UCP C/S
      • 1.2.1 UDP Server
      • 1.2.2 UDP Client
  2. TCP 和 UCP 的 Socket 编程对比
    • 2.1 Server
    • 2.2 Client
    • 2.3 所使用的 API 对比
  3. 裸用 socket 的性能很差

1 快速开始

1.1 TCP C/S

无论你是使用 Windows 还是 UNIX-like 的系统,操作系统提供给应用层的网络编程接口都是 Socket。在 5 层的 TCP/IP 网络结构或者 7 层的 OSI 网络结构中,都有传输层,TCP 和 UDP 协议就是为传输层服务的。而网络层的最常用协议就是 IP(IPv4 或 IPv6)在高层编写程序,就需要用到 TCP 协议和 UDP 协议。其直接使用,就是通过 Socket 来实现的。

先看一段简单的 TCP 通信的 Server 与 Client 例程。

1.1.1 TCP Server

下面是一个 TCP 连接的 Server 实例。

#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    // Get Port option
    if (argc < 2)
    {
        fprintf(stderr, "ERROR, no port provided\n");
        exit(1);
    }
    int port_no = atoi(argv[1]);

    // Get socket
    int socket_fd = socket(AF_INET, SOCK_STREAM, 0);

    // Bind
    struct sockaddr_in server_addr;
    bzero((char *) &server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(port_no);
    bind(socket_fd, (struct sockaddr *) &server_addr, sizeof(server_addr));

    // Listen
    listen(socket_fd, 5);

    while (1) {

        // Accept
        struct sockaddr_in client_addr;
        socklen_t client_addr_length = sizeof(client_addr);
        int socket_to_client = accept(socket_fd, (struct sockaddr *) &client_addr, &client_addr_length);

        // Read
        char buffer[1024];
        bzero(buffer, sizeof(buffer));
        read(socket_to_client, buffer, sizeof(buffer) - 1);
        printf("Here is the message: %s\n", buffer);

        // Write
        const char *data = "I got your message.";
        write(socket_to_client, data, sizeof(data));

        // Close
        close(socket_to_client);
    }

    close(socket_fd);

    return 0;
}

上面是 TCP 的 Client 的 Simplest Example。概括起来 Scoket Server 编程有如下几个步骤:

1.1.1.1 获取 Socket Descriptor:
    // socket function is included in sys/socket.h
    // AF_INET is included in sys/socket.h
    // SOCK_STREAM is included in sys/socket.h
    socket(AF_INET, SOCK_STREAM, 0);

通过sys/socket.h中的socket函数。第一个参数表示使用IPv4 Internet Protocol,如果是AF_INET6则表示IPv6 Internet Protocol,其中AF表示Address Family,另外还有PF表示Protocol Family。第二个参数表示流传输Socket Stream,流传输是序列化的、可靠的、双向的、面向连接的,Kernel.org 给出的解释是:“Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported.”

另外一个常用的是SOCK_DGRAM表示Socket Diagram,是无连接的、不可靠的传输方式,Kernel.org 给出的解释是“Supports datagrams (connectionless, unreliable messages of a fixed maximum length).”

第三个参数表示使用的协议族中的哪个协议。一般来说一个协议族经常只有一个协议,所以长使用“0”。具体参见Kernel.org 给出的解释

1.1.1.2 绑定地址与端口

首先要创建一个struct sockaddr_in,并设置地址族、监听的外来地址与本地端口号。如下:

// struct sockaddr_in is inclued in netinet/in.h
// bzero function is included in string.h
// atoi is include in stdlib.h
struct sockaddr_in server_addr;
bzero((char *) &server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(atoi(argv[1]))

然后将第 1 步创建的Socket与这里创建的地址绑定(实际上直接用的是Socket Descriptor)。

// struct sockaddr is included in sys/socket.h
// bind function is included in sys/socket.h
bind(socket_fd, (struct sockaddr *) &server_addr, sizeof(server_addr));
1.1.1.3 开始监听
// listen function is included in sys/socket.h
    listen(socket_fd, 5);

第一个参数不用赘述了,第二个参数是连接队列(connection queue)的最大长度。当Server进行了Accept后,在新来的请求就得进入队列等待,如果队列满了,再来的连接就会被拒绝。

1.1.1.4 接受连接
// struct sockaddr_in is included in netinet/in.h
// accept function is included in sys/socket.h
struct sockaddr_in client_addr;
socklen_t client_addr_length = sizeof(client_addr);
int socket_to_client = accept(socket_fd, (struct sockaddr *) &client_addr, &client_addr_length);

在开始监听socket_fd后,接收来自该Socket的连接,将获取到的客户端地址和地址长度写入client_addrclient_addr_length中。该accept在成功接受某连接后会得到该连接的Socket,并将其Socket Descriptor返回,就得到了socket_to_client

1.1.1.5 接收和发送数据
// bzero function is included in string.h
// read function is included in unistd.h
char buffer[1024];
bzero(buffer, sizeof(buffer));
read(socket_to_client, buffer, sizeof(buffer) - 1);

在接受连接后,就可以从该Socket读取客户端发送来的数据了,数据读取到char *的字符串中。发送过程也类似。

// write function is included in unistd.h
const char *data = "Server has received your message.";
write(socket_to_client, data, sizeof(data));
1.1.1.6 关闭Socket
// close function is included in unistd.h
close(socket_fd);

以上就简单解释了 TCP Server 的 Socket 通信过程。简单概括如下:

Create Socket - Bind socket with port - Listen socket - Accept connection - Read/Write - Close

1.1.2 TCP Client

再来看看 Client。以下是例程:

#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h> // bcopy
#include <stdlib.h> 

int main(int argc, char *argv[])
{
    // Get options
    if (argc < 3)
    {
        fprintf(stderr, "Usage: %s <hostname> <port>\n", argv[0]);
        exit(1);
    }
    struct hostent *server_host = gethostbyname(argv[1]);
    int server_port = atoi(argv[2]);

    // Get socket
    int socket_fd = socket(AF_INET, SOCK_STREAM, 0);

    // Connect
    struct sockaddr_in server_addr;
    bzero((char *) &server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    bcopy((char *) server_host->h_addr, (char *) &server_addr.sin_addr.s_addr, server_host->h_length);
    server_addr.sin_port = htons(server_port);
    connect(socket_fd, (struct sockaddr *) &server_addr, sizeof(server_addr));

    // Input
    printf("Please enter the message: ");
    char buffer[1024];
    bzero(buffer, sizeof(buffer));
    fgets(buffer, sizeof(buffer) - 1, stdin);

    // Write
    write(socket_fd, buffer, strlen(buffer));

    // Read
    bzero(buffer, sizeof(buffer));
    read(socket_fd, buffer, sizeof(buffer) - 1);
    printf("%s\n", buffer);

    // Close
    close(socket_fd);

    return 0;
}

上面是 TCP 的 Client 的 Simplest Example。概括起来 Scoket Client 编程有如下几个步骤:

1.1.2.1 获取 Socket Descriptor:

与 Server 一样。

1.1.2.2 连接服务器
// struct sockaddr_in is included in netinet/in.h
// struct sockaddr is included in sys/socket.h
// bzero is included in string.h
// bcopy is included in string.h
// AF_INET is included in sys/socket.h
// connect is included in sys/socket.h
connect(socket_fd, (struct sockaddr *) &server_addr, sizeof(server_addr));

第一个参数是int型的Socket Descriptor,通过socket函数得到。第二个参数是指向struct sockaddr地址的指针,第三个参数是该地址的大小。而这个地址是通过struct sockaddr_in得到的,然后用bzero位初始化,再赋初值,包括地址族为AF_INET,地址为struct sockaddr_in中的h_addr,这里用到了bcopy位拷贝函数,最后再赋上端口号htons(int server_port)connect的作用,就是将本地的Socket与服务器建立连接,而这个Socket则是通过Socket Descriptor来标示的。

1.1.2.3 发送或接收数据

首先看发送数据:

// write function is included in unistd.h
char buffer[1024]
...
write(socket_fd, buffer, strlen(buffer));

然后用write函数,向Socket所连接的服务器发送数据,数据是char *的字符串。再看下面的接收数据:

// read function is included in unistd.h
read(socket_fd, buffer, sizeof(buffer) - 1);

第一个参数是Socket Descriptor,第二个参数是char *的字符串,长度为第三个参数标示的sizeof(buffer)-1。功能就是从socket_fd标示的Socket所连接的服务器读取数据。

1.1.2.4 关闭Socket
// close function is included in unistd.h
close(socket_fd);

以上就简单解释了客户端的最基本的Socket通信。概括起来的过程就是:

Create Socket - Connect socket with server - Write/Read - Close

1.2 UDP C/S

刚才介绍了最简单的 TCP C/S 模型,下面看看 UDP C/S 模型。

1.2.1 UDP Server

下面是 UDP 连接的 Server 实例:

#include "sys/socket.h"
#include "netinet/in.h"
#include "string.h"

int main(int argc, char *argv[])
{
    // Create Socket
    int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);

    // Bind
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(atoi(argv[1]));
    bind(socket_fd, (struct sockaddr *) &server_addr, sizeof(server_addr));

    // As accept
    while (1)
    {
        // Receive
        char buffer[1024];
        struct sockaddr_in client_addr;
        socklen_t client_addr_length = sizeof(client_addr);
        int msg_recv_length = recvfrom(socket_fd, buffer, sizeof(buffer), 0, (struct sockaddr *) &client_addr, &client_addr_length);

        // Write
        const char *msg_send = "Received a datagram: ";
        write(1, msg_send, strlen(msg_send));
        write(1, buffer, msg_recv_length);

        // Send
        const char *msg_send_2 = "Got your message\n";
        sendto(socket_fd, msg_send_2, strlen(msg_send_2), 0, (struct sockaddr *) &client_addr, sizeof(struct sockaddr_in));
    }

    close(socket_fd);

    return 0;
}

UDP Server 的建立主要有以下几步:

1.2.1.1 获取 Socket Descriptor
sock=socket(AF_INET, SOCK_DGRAM, 0);

创建Socket,获取Socket Descriptor

1.2.1.2 绑定地址与端口
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(atoi(argv[1]));
bind(socket_fd, (struct sockaddr *) &server_addr, sizeof(server_addr));

将端口和允许的地址与Socket绑定。

1.2.1.3 接收和发送数据
char buffer[1024];
struct sockaddr_in client_addr;
socklen_t client_addr_length = sizeof(client_addr);
int msg_recv_length = recvfrom(socket_fd, buffer, sizeof(buffer), 0, (struct sockaddr *) &client_addr, &client_addr_length);

接收数据用的是sys/socket.h中的recvfrom函数,第一个参数是Socket Descriptor,第二个参数是用于存储数据的buffer,第三个参数是数据存储区域的长度(注,对于数组用sizeof取到的值是数组长度乘以长度;对于指针用sizeof取到的值是指针长度,对于32位机是4,对于64位机是8)。第四个参数是标志符,一般设置为0,具体可以查看info recvfrom。第五个参数用于存储客户端的地址,第六个参数是存储客户端地址的socklen_t型变量的长度。

用起来也很好记(也可以现查现用),先是套接字,然后是存储区及其大小,接着是标志符,最后是客户端地址及其大小。

const char *msg_send_2 = "Got your message\n";
sendto(socket_fd, msg_send_2, strlen(msg_send_2), 0, (struct sockaddr *) &client_addr, sizeof(struct sockaddr_in));

发送数据用更多是sys/socket.h中的sendto函数,第一个参数Socket Descriptor,第二和第三个参数是所发送的数据及其大小,然后是标示符(一般为0),最后是客户端地址及其大小。

1.2.1.4 关闭Socket
close(socket_fd);

简单概括一下 UDP Server 的 Socket 编程步骤:

Create Socket - Bind socket with port - Recv/Send - Close

1.2.2 UDP Client

#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>

int main(int argc, char *argv[])
{
    // Create socket
    int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);

    // Initialize server address
    struct hostent *server_host = gethostbyname(argv[1]);
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET; // socket internet family
    bcopy((char *)server_host->h_addr, (char *)&server_addr.sin_addr.s_addr, server_host->h_length); // socket internet address
    server_addr.sin_port = htons(atoi(argv[2])); // socket internet port

    // Send
    char buffer[1024];
    sendto(socket_fd, buffer, sizeof(buffer), 0, (const struct sockaddr *) &server_addr, sizeof(struct sockaddr_in));

    // Receive and write
    struct sockaddr_in client_addr;
    socklen_t client_addr_length = sizeof(client_addr);
    int msg_recv_length = recvfrom(socket_fd, buffer, sizeof(buffer), 0, (struct sockaddr *) &client_addr, &client_addr_length);
    const char *hint = "Got an ACK: ";
    write(1, hint, strlen(hint));
    write(1, buffer, msg_recv_length);

    // Close
    close(socket_fd);

    return 0;
}
1.2.2.1 创建Socket
int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
1.2.2.2 发送和接收数据
sendto(socket_fd, buffer, sizeof(buffer), 0, (const struct sockaddr *) &server_addr, sizeof(struct sockaddr_in));

与 UDP Server 的 sendto 一样。注意初始化。

1.2.2.3 关闭Socket
close(socket_fd);

总结一下 UDP Client 编程的几个步骤:

Create Socket - Send/Receive - Close

2 图解 TCP 和 UDP 原理

声明:图片来自此处

Resize icon

上图为 TCP 原理图示

Resize icon

上图为 UDP 原理图示

3 TCP 和 UCP 的 Socket 编程对比

3.1 Server

TCP 的过程是:

  1. Create server socket
  2. Bind the server socket with client addresses and a local server port
  3. Listen the server socket
  4. Accept with blocking, until the connection has established and then get a client socket
  5. Read and write data from the client socket
  6. Close the client socket as discontecting
  7. Close the local server socket

UDP 的过程是:

  1. Create server socket
  2. Bind the server socket with client addresses and a local server port
  3. Receive data and client address through the server socket
  4. Send data to the client address through the server socket
  5. Close the local server socket

通过对比,我们可以看到,相同点如下:

  1. 一开始都要创建 socket
  2. 接着都要绑定 socket 与本地端口和指定的客户端地址
  3. 最后都要关闭本地 socket

不过这些相似点似乎没什么价值,还是看看不同点吧。

  1. TCP 要监听端口,然后阻塞式地等待连接;UDP 则通过自身的循环来不断读取,不阻塞也不建立连接。
  2. TCP 建立连接后会有一个 client socket,然后通过向这个 socket 的读写实现数据传输;UDP 则直接向客户端地址发送和接收数据。
  3. 因为 TCP 方式有 client socket,所以完成一次传输后,可以关闭 client socket。当然也可以一直连着不关闭。

可以看到,TCP 和 UDP 的本质区别就是面向连接还是无连接的。因为面向连接,所以要监听到是否有 connection 到来,connection 一旦到来,就阻塞住,然后会有一个 socket 跳出来作为代言。通过对这个 socket 的读写就实现了对 connection 的另一端的客户端的读写。

3.2 Client

TCP 的过程是:

  1. Create client socket
  2. Connect the server address and port with the client socket
  3. After connection is established, read and write data to the client socket
  4. Close the local socket socket

UDP 的过程是:

  1. Create client socket
  2. Send data to the server address through the client socket
  3. Receive data and the server address throught the client socket
  4. Close the local client socket

可以看到如下区别:

  1. TCP 方式要 connect 服务器地址/端口与 socket;UDP 则不需要这个过程。
  2. TCP 方式在 connection 建立后,通过 client socket 读写数据;而 UDP 方式则直接通过 client socket 向服务器地址发送数据。

3.3 所使用的 API 对比

TCP 方式的 Server 用到:

  • socket
  • bindlisten
  • accept
  • readwrite
  • close

UDP 方式的 Server 用到:

  • sokect
  • bind
  • recvfromsendto
  • close

TCP 方式的 Client 用到:

  • socket
  • connect
  • writeread
  • close

UDP 方式的 Client 用到:

  • socket
  • sendtorecvfrom
  • close

4 裸用 socket 的性能很差

是的,这是最传统的网络编程方式:“One traditional way to write network servers is to have the main server block on accept(), waiting for a connection. Once a connection comes in, the server fork()s, the child process handles the connection and the main server is able to service new incoming requests.”

下一篇,我会介绍 IO 复用技术中在 Linux 下常用的 select、poll 和 epoll。

5 参考

  1. http://www.lowtek.com/sockets/select.html
  2. http://www.kernel.org/doc/man-pages/online/pages/man7/socket.7.html
  3. http://www.kernel.org/doc/man-pages/online/pages/man2/listen.2.html
  4. http://www.kernel.org/doc/man-pages/online/pages/man2/send.2.html
  5. http://www.kernel.org/doc/man-pages/online/pages/man2/sendto.2.html
  6. http://www.kernel.org/doc/man-pages/online/pages/man2/recv.2.html
  7. http://www.kernel.org/doc/man-pages/online/pages/man2/recvfrom.2.html
  8. http://www.linuxhowtos.org/C_C++/socket.htm

-

Happy Coding, enjoy sharing!

转载请注明来自“柳大的CSDN博客”:Blog.CSDN.net/Poechant

-

分享到:
评论

相关推荐

    Linux 讲义.rar

    这篇“Linux讲义”是一个针对初学者的技术手册,旨在帮助读者快速掌握Linux的基本操作和常用命令。以下将详细介绍这个讲义中可能包含的一些关键知识点。 1. **Linux简介**:Linux是一种自由和开放源码的操作系统,...

    黑马_Linux网络编程-网络基础-socket编程-高并发服务器

    本篇文章旨在深入解读“黑马_Linux网络编程-网络基础-socket编程-高并发服务器”相关的核心概念和技术要点,包括网络基础知识、常用网络协议、网络应用程序设计模式以及分层模型等内容。 #### 网络基础 网络基础...

    SHELL编程讲义

    这篇讲义将深入探讨SHELL编程的基础知识和高级技巧。 **一、SHELL基础** 1. **SHELL类型**:常见的SHELL类型有Bash(Bourne-Again SHell)、Sh、Csh、Ksh等,其中Bash是目前最常用的一种。 2. **SHELL环境**:每...

    嵌入式Linux开发培训入门讲义

    这篇培训讲义旨在为初学者提供一个全面的入门指导,帮助他们掌握在嵌入式系统中使用Linux的基本技能。 1. **嵌入式系统概述** 嵌入式系统是指集成在其他设备中的专用计算机系统,它们通常执行特定的任务,并且体积...

    清华Linux系列讲座之一 绝对经典

    本篇将深入探讨清华Linux系列讲座之一——绝对经典中的核心知识点,旨在为读者提供一份详尽的Linux学习指南。清华大学作为国内顶级学府,其计算机科学教育享有盛誉,其Linux系列讲座更是备受业界推崇。这次的讲座...

    Android驱动开发权威指南

    第二篇 勿于浮砂筑高台——Linux驱动基础篇 第3章Linux内核综述 3.1 OS基本概念 3.1.1多用户系统 3.1.2用户和组 3.1.3进程 3.1.4 Linux单核架构 3.2 Linux内核综述 3.2.1进程/内核模型综述 3.2.2内存管理综述 3.2.3...

    国防科技大学嵌入式教学讲义

    4. **嵌入式软件开发**:介绍C/C++编程语言在嵌入式系统中的应用,以及如何进行交叉编译。讲解驱动程序开发,包括设备驱动原理和接口设计。 5. **嵌入式硬件设计**:探讨嵌入式系统硬件设计,包括电路设计、PCB布局...

    若水 android开发从零开始讲义(源码)

    【Android开发从零开始:基础篇】 Android是一种基于Linux内核的开源操作系统,主要用于移动设备,如智能手机和平板电脑。若水老师的“Android开发从零开始”课程旨在帮助初学者全面掌握Android应用开发的基本技能...

    华为java培训讲义

    本篇讲义主要涵盖了Java环境配置、运行过程、垃圾回收机制、安全性以及源代码的基本规范,这些都是Java初学者需要掌握的基础知识。 首先,配置Java环境变量是使用Java的第一步。`JAVA_HOME`变量指向JDK的安装目录,...

    S3C2410开发流程及讲义

    本篇将深入解析S3C2410的开发流程,旨在为开发者提供详尽的实践指导。 一、S3C2410概述 S3C2410是一款高性能、低功耗的嵌入式处理器,内置ARM920T核心,工作频率最高可达400MHz,支持MMU(内存管理单元)和多种外设...

    Android开发完全讲义(第二版)第四文章之neonlight源代码

    《Android开发完全讲义(第二版)第四文章之neonlight源代码》是著名Android开发者李宁撰写的一篇深入解析Android应用开发的技术文章,重点围绕neonlight源代码进行讲解。在Android应用开发中,理解并掌握源码是提升...

    SpringBoot讲义.pdf

    #### 一、Spring Boot 快速入门及基础知识 - **Spring Boot 简介**: - **定义**:Spring Boot 是 Spring 家族中的一个新成员,旨在简化 Spring 应用的初始搭建以及开发过程。 - **优势**: - 自动配置:Spring ...

    自学android应用开发详细流程经验(第三版)

    1. 《Java语言程序设计基础篇》:这本书详细介绍了Java的基础语法,适合初学者。 2. 《Java编程思想 第四版》:这是一本权威的进阶教材,对于理解Java的高级概念非常有帮助。 3. 《疯狂Java讲义》:这是一本针对国内...

    Android底层开发实战

    在简要介绍系统底层开发流程的基础上,首先分析了主流的Zynq和pcDuino平台上开发环境的搭建、Linux内核以及Android系统的编译、下载;然后结合前文学习过的知识点,从零开始设计LED显示系统的Linux内核驱动、Android...

Global site tag (gtag.js) - Google Analytics