匠心精神 - 良心品质腾讯认可的专业机构-IT人的高薪实战学院

咨询电话:4000806560

Linux下的网络编程技术详解,让你成为网络编程高手

Linux下的网络编程技术详解,让你成为网络编程高手

网络编程是软件开发中非常重要的一环,它涉及到了网络通信、协议、数据序列化、IO多路复用等方面的知识,尤其在Linux环境下,这些知识点尤为重要。本文将会深入探讨Linux下的网络编程技术,带你从初级程序员逐渐成为网络编程高手。

一、Socket编程基础

网络编程的核心就是Socket编程,Socket是应用程序与网络之间的一个抽象接口,它能够提供TCP/IP协议栈的操作接口。在Linux中,Socket编程分为两种:基于TCP协议的Socket编程和基于UDP协议的Socket编程。其中,TCP是面向连接的协议,UDP是无连接的协议。

1.1 TCP Socket编程

TCP通信是基于连接的,客户端和服务端建立连接后,可以进行双向通信。下面是一个简单的TCP Socket编程示例:

Client端:

```c
#include 
#include 
#include 
#include 
#include 
#include 

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080

int main(int argc, char **argv) {
    int sock_fd;
    struct sockaddr_in server_addr;
    char send_buf[1024] = "hello, server!";
    char recv_buf[1024] = {0};
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_fd < 0) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
    if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect error");
        exit(EXIT_FAILURE);
    }
    write(sock_fd, send_buf, strlen(send_buf));
    read(sock_fd, recv_buf, sizeof(recv_buf));
    printf("%s\n", recv_buf);
    close(sock_fd);
    return 0;
}
```

Server端:

```c
#include 
#include 
#include 
#include 
#include 
#include 

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080

int main(int argc, char **argv) {
    int sock_fd, client_fd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len;
    char recv_buf[1024] = {0};
    char send_buf[1024] = "hello, client!";
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_fd < 0) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind error");
        exit(EXIT_FAILURE);
    }
    if (listen(sock_fd, 20) < 0) {
        perror("listen error");
        exit(EXIT_FAILURE);
    }
    printf("server start...\n");
    client_len = sizeof(client_addr);
    client_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &client_len);
    if (client_fd < 0) {
        perror("accept error");
        exit(EXIT_FAILURE);
    }
    read(client_fd, recv_buf, sizeof(recv_buf));
    printf("recv from client: %s\n", recv_buf);
    write(client_fd, send_buf, strlen(send_buf));
    close(client_fd);
    close(sock_fd);
    return 0;
}
```

1.2 UDP Socket编程

UDP通信是无连接的,每次发送前都需要指定目的地址和端口。下面是一个简单的UDP Socket编程示例:

Client端:

```c
#include 
#include 
#include 
#include 
#include 
#include 

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080

int main(int argc, char **argv) {
    int sock_fd;
    struct sockaddr_in server_addr;
    char send_buf[1024] = "hello, server!";
    char recv_buf[1024] = {0};
    sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock_fd < 0) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
    sendto(sock_fd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
    recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, NULL, NULL);
    printf("%s\n", recv_buf);
    close(sock_fd);
    return 0;
}
```

Server端:

```c
#include 
#include 
#include 
#include 
#include 
#include 

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080

int main(int argc, char **argv) {
    int sock_fd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len;
    char recv_buf[1024] = {0};
    char send_buf[1024] = "hello, client!";
    sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock_fd < 0) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind error");
        exit(EXIT_FAILURE);
    }
    printf("server start...\n");
    client_len = sizeof(client_addr);
    recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&client_addr, &client_len);
    printf("recv from client: %s\n", recv_buf);
    sendto(sock_fd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&client_addr, client_len);
    close(sock_fd);
    return 0;
}
```

二、IO多路复用

在网络编程中,为了提高程序的性能和响应速度,需要使用IO多路复用技术。在Linux中,IO多路复用主要有三种方式:select、poll和epoll。其中,epoll是效率最高、稳定性也最好的一种方式。

2.1 select

select是最早的一种IO多路复用技术,它是通过轮询的方式来查询准备就绪的文件描述符。下面是一个简单的select示例:

```c
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080

int main(int argc, char **argv) {
    int sock_fd;
    struct sockaddr_in server_addr;
    fd_set rfds, wfds;
    char send_buf[1024] = "hello, server!";
    char recv_buf[1024] = {0};
    struct timeval tv = {5, 0};
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_fd < 0) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
    if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect error");
        exit(EXIT_FAILURE);
    }
    FD_ZERO(&rfds);
    FD_ZERO(&wfds);
    FD_SET(sock_fd, &rfds);
    FD_SET(sock_fd, &wfds);
    select(sock_fd + 1, &rfds, &wfds, NULL, &tv);
    if (FD_ISSET(sock_fd, &rfds)) {
        read(sock_fd, recv_buf, sizeof(recv_buf));
        printf("%s\n", recv_buf);
    }
    if (FD_ISSET(sock_fd, &wfds)) {
        write(sock_fd, send_buf, strlen(send_buf));
    }
    close(sock_fd);
    return 0;
}
```

2.2 poll

poll是一种比select更高效的IO多路复用技术,它是通过轮询内核中的文件描述符来查询准备就绪的文件描述符。下面是一个简单的poll示例:

```c
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080

int main(int argc, char **argv) {
    int sock_fd;
    struct sockaddr_in server_addr;
    struct pollfd fds[1];
    char send_buf[1024] = "hello, server!";
    char recv_buf[1024] = {0};
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_fd < 0) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
    if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect error");
        exit(EXIT_FAILURE);
    }
    fds[0].fd = sock_fd;
    fds[0].events = POLLIN | POLLOUT;
    poll(fds, 1, 5000);
    if (fds[0].revents & POLLIN) {
        read(sock_fd, recv_buf, sizeof(recv_buf));
        printf("%s\n", recv_buf);
    }
    if (fds[0].revents & POLLOUT) {
        write(sock_fd, send_buf, strlen(send_buf));
    }
    close(sock_fd);
    return 0;
}
```

2.3 epoll

epoll是一种效率最高、稳定性最好的IO多路复用技术,它是通过回调机制来查询准备就绪的文件描述符。下面是一个简单的epoll示例:

```c
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080

int main(int argc, char **argv) {
    int sock_fd, epfd;
    struct sockaddr_in server_addr;
    char send_buf[1024] = "hello, server!";
    char recv_buf[1024] = {0};
    struct epoll_event event, events[1];
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_fd < 0) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
    if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect error");
        exit(EXIT_FAILURE);
    }
    epfd = epoll_create(1);
    if (epfd < 0) {
        perror("epoll_create error");
        exit(EXIT_FAILURE);
    }
    event.events = EPOLLIN | EPOLLOUT;
    event.data.fd = sock_fd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, sock_fd, &event);
    epoll_wait(epfd, events, 1, 5000);
    if (events[0].events & EPOLLIN) {
        read(sock_fd, recv_buf, sizeof(recv_buf));
        printf("%s\n", recv_buf);
    }
    if (events[0].events & EPOLLOUT) {
        write(sock_fd, send_buf, strlen(send_buf));
    }
    close(epfd);
    close(sock_fd);
    return 0;
}
```

三、总结

本文简要介绍了Linux下的网络编程技术,包括TCP/UDP Socket编程、IO多路复用等方面的知识点。网络编程是一项很重要的技能,希望大家多多实践,不断提高自己的编程水平。