Linux-网络编程

网络编程概述

本文将介绍网络编程中的一些基础知识,包括TCP和UDP的区别、端口号的作用、字节序的概念,以及通过socket编程实现客户端和服务器之间的通信。我们还会展示一些示例代码,帮助理解这些概念的实际应用。

1. TCP/UDP的区别,端口号的作用

1.1 TCP/UDP的区别

在TCP/IP 协议中, TCP 协议提供可靠的连接服务,采用三次握手建立一个连接。

第一次握手:建立连接时,客户端发送
syn 包 (tcp 协议中 syn 位置 1 ,序号为 J) 到服务器,并进
入 SYN_SEND 状态,等待服务器确认;

二次握手:服务器收到 syn 包,必须确认客户的 SYN ,同时自己也发送一个 SYN 包,即 SYN+ACK
包( tcp 协议中 syn 位置 1 ack 位置 1 ,序号 K ,确定序号为 J+1 ),此时服务器进入 SYN_RECV 状
态;
第三次握手:客户端收到服务器的
SYN ACK 包,向服务器发送确认包 ACK(tcp 协议中 ack 位置
1 ,确认序号 K+1) 1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。
通过这样的
三次握手 ,客户端与服务端建立起可靠的双工的连接,开始传送数据。三次握手的最主要目
的是保证连接是双工的,可靠更多的是通过重传机制来保证的。
但是为什么一定要进行三次握手来保证连接是双工的呢,一次不行么?两次不行么?
我们举一个现实生活中两个人进行语言沟通的例子来模拟
三次握手 。 同理对于 TCP 为什么需要进行三
次握手我们可以一样的理解:为了保证服务端能收接受到客户端的信息并能做出正确的应答而进行前两次
第一次和第二次 握手,为了保证客户端能够接收到服务端的信息并能做出正确的应答而进行后两次 第二
次和第三次 握手。
四次挥手
由于
TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这好比,我们打电话(全双工),正
常的情况下(出于礼貌),通话的双方都要说再见后才能挂电话,保证通信双方都把话说完了才挂电话。

TCP(传输控制协议)UDP(用户数据报协议) 是互联网协议套件中的两个核心传输层协议。它们之间的主要区别如下:

  • 连接方式

    • TCP是面向连接的(如打电话要先拨号建立连接),需要在传输数据之前建立连接。
    • UDP是无连接的,即发送数据之前,不需要建立连接。
  • 可靠性

    • TCP提供可靠的服务,保证数据无差错、不丢失、不重复且按序到达。
    • UDP尽最大努力交付,不保证可靠交付。
  • 数据传输

    • TCP面向字节流,将数据视为一连串无结构的字节流。
    • UDP面向报文,支持面向报文的传输方式。
  • 通信方式

    • 每一条TCP连接只能是点到点的。
    • UDP支持一对一、一对多、多对一和多对多的交互通信。
  • 首部开销

    • TCP首部开销较大,为20字节。
    • UDP首部开销小,只有8字节。
  • 信道类型

    • TCP的逻辑通信信道是全双工的可靠信道。
    • UDP则是不可靠信道。

1.2 端口的作用

IP地址标识网络中的主机,但一台主机可以提供多个网络服务(如Web服务、FTP服务等)。为了区分不同的服务,使用“IP地址+端口号”来标识具体的服务。

  • 端口号 提供了访问通道。服务器通常通过知名端口号来识别。例如,FTP服务器的TCP端口号为21,Telnet服务器的TCP端口号为23,TFTP服务器的UDP端口号为69。

2. 字节序

  • 字节序 指的是多字节数据在内存中的存储顺序。常见的字节序有两种:大端字节序(Big-endian)和小端字节序(Little-endian)。
    • x86系列CPU通常采用小端字节序(Little-endian)。
    • 网络字节序通常采用大端字节序(Big-endian)。

3. Socket编程实现双方消息发送

3.1 Socket服务器实现不断接收客户端信息

通过Socket编程实现一个简单的服务器不断接收客户端信息,并处理数据。以下是一个简单的Socket服务器和客户端示例代码。

server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
    int s_fd;
    int c_fd;
    int n_read;
    char readBuf[128];
    char *msg = "I got your message";
    struct sockaddr_in s_addr;
    struct sockaddr_in c_addr;

    memset(&s_addr, 0, sizeof(struct sockaddr_in));
    memset(&c_addr, 0, sizeof(struct sockaddr_in));

    if (argc != 3) {
        printf("param isn't right\n");
        return -1;
    }

    // 1. 创建socket
    s_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (s_fd == -1) {
        perror("socket");
        exit(-1);
    }

    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(atoi(argv[2]));
    inet_aton(argv[1], &s_addr.sin_addr);

    // 2. 绑定地址
    bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));

    // 3. 监听端口
    listen(s_fd, 10);

    // 4. 接受客户端连接
    int clen = sizeof(struct sockaddr_in);
    while (1) {
        c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
        if (c_fd == -1) {
            perror("accept");
            continue;
        }
        printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));

        if (fork() == 0) {
            // 5. 读取客户端数据
            n_read = read(c_fd, readBuf, 128);
            if (n_read == -1) {
                perror("read");
            } else {
                printf("get message: %d, %s\n", n_read, readBuf);
            }

            // 6. 发送响应
            write(c_fd, msg, strlen(msg));
            break; // 子进程完成一次后退出,父进程继续接收数据
        }
    }
    return 0;
}
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
    int c_fd;
    int n_read;
    char readBuf[128];
    char *msg = "from client msg";
    struct sockaddr_in c_addr;

    memset(&c_addr, 0, sizeof(struct sockaddr_in));

    if (argc != 3) {
        printf("param isn't right\n");
        return -1;
    }

    // 1. 创建socket
    c_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (c_fd == -1) {
        perror("socket");
        exit(-1);
    }

    c_addr.sin_family = AF_INET;
    c_addr.sin_port = htons(atoi(argv[2]));
    inet_aton(argv[1], &c_addr.sin_addr);

    // 2. 连接服务器
    if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1) {
        perror("connect");
        exit(-1);
    }

    // 3. 发送数据
    int n_write = write(c_fd, msg, strlen(msg));
    if (n_write == -1) {
        perror("write");
    }

    // 4. 读取响应
    n_read = read(c_fd, readBuf, 128);
    if (n_read == -1) {
        perror("read");
    } else {
        printf("get message from server: %d, %s\n", n_read, readBuf);
    }
    return 0;
}

4. 一个服务器对应一个客户端作双方交互信息

服务端创建子进程不断接收客户端数据,再创建一个子进程发送数据,做不同的事情;客户端创建子进程发送数据,父进程读取数据;一个进程一个while(1)

server.c

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

int main(int argc, char **argv) {
    int s_fd;
    int c_fd;
    int n_read;
    char readBuf[128];
    int mark = 0;
    char msg[128] = {0};
    struct sockaddr_in s_addr;
    struct sockaddr_in c_addr;

    memset(&s_addr, 0, sizeof(struct sockaddr_in));
    memset(&c_addr, 0, sizeof(struct sockaddr_in));

    if (argc != 3) {
        printf("param isn't right\n");
        return -1;
    }

    // 1. 创建socket
    s_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (s_fd == -1) {
        perror("socket");
        exit(-1);
    }

    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(atoi(argv[2]));
    inet_aton(argv[1], &s_addr.sin_addr);

    // 2. 绑定地址
    bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));

    // 3. 监听端口
    listen(s_fd, 10);

    // 4. 接受客户端连接
    int clen = sizeof(struct sockaddr_in);
    while (1) {
        c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
        if (c_fd == -1) {
            perror("accept");
            continue;
        }
        printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));
        mark++;

        if (fork() == 0) {
            if (fork() == 0) {
                while (1) {
                    // 6. 发送响应
                    memset(msg, 0, sizeof(msg));
                    sprintf(msg, "welcome %d client", mark);
                    write(c_fd, msg, strlen(msg));
                    sleep(3);
                }
            }

            // 5. 读取客户端数据
            while (1) {
                memset(readBuf, 0, sizeof(readBuf));
                n_read = read(c_fd, readBuf, 128);
                if (n_read == -1) {
                    perror("read");
                } else {
                    printf("\nget: %s\n", readBuf);
                }
            }
            break;
        }
    }
    return 0;
}

client.c

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

int main(int argc, char **argv) {
    int c_fd;
    int n_read;
    char readBuf[128];
    char msg[128] = {0};
    struct sockaddr_in c_addr;

    memset(&c_addr, 0, sizeof(struct sockaddr_in));

    if (argc != 3) {
        printf("param isn't right\n");
        return -1;
    }

    // 1. 创建socket
    c_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (c_fd == -1) {
        perror("socket");
        exit(-1);
    }

    c_addr.sin_family = AF_INET;
    c_addr.sin_port = htons(atoi(argv[2]));
    inet_aton(argv[1], &c_addr.sin_addr);

    // 2. 连接服务器
    if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1) {
        perror("connect");
        exit(-1);
    }

    while (1) {
        if (fork() == 0) {
            while (1) {
                // 3. 发送数据
                memset(msg, 0, sizeof(msg));
                printf("input: ");
                gets(msg);
                write(c_fd, msg, strlen(msg));
            }
        }

        while (1) {
            // 4. 读取响应
            memset(readBuf, 0, sizeof(readBuf));
            n_read = read(c_fd, readBuf, 128);
            if (n_read == -1) {
                perror("read");
            } else {
                printf("\nget: %s\n", readBuf);
            }
        }
    }
    return 0;
}

5. 服务器与多个客户端聊天,服务器自动回复

server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv) {
    int s_fd;
    int c_fd;
    int n_read;
    int mark = 0;
    char buf[128] = {0};
    char msg[128] = {0};
    struct sockaddr_in s_addr;
    struct sockaddr_in c_addr;

    memset(&s_addr, 0, sizeof(struct sockaddr_in));
    memset(&c_addr, 0, sizeof(struct sockaddr_in));

    if (argc != 3) {
        printf("params no ok\n");
        exit(-1);
    }

    // 1. 创建socket
    s_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (s_fd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2. 绑定地址
    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(atoi(argv[2]));
    inet_aton(argv[1], &s_addr.sin_addr);

    bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));

    // 3. 监听端口
    listen(s_fd, 10);

    int len = sizeof(struct sockaddr_in);
    while (1) {
        // 4. 接受客户端连接
        c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &len);
        if (c_fd == -1) {
            perror("accept");
            continue;
        }

        mark++;
        printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));

        if (fork() == 0) {
            if (fork() == 0) {
                // 5. 发送自动回复消息
                while (1) {
                    memset(msg, 0, sizeof(msg));
                    sprintf(msg, "welcome No %d client", mark);
                    write(c_fd, msg, strlen(msg));
                    sleep(2);
                }
            }

            // 6. 读取客户端消息
            while (1) {
                memset(buf, 0, sizeof(buf));
                n_read = read(c_fd, buf, sizeof(buf));
                if (n_read == -1) {
                    perror("read");
                } else {
                    printf("read client: %s\n", buf);
                }
            }
        }
    }
    close(s_fd);
    return 0;
}

client.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv) {
    int c_fd;
    int n_read;
    char buf[128] = {0};
    char msg[128] = {0};
    struct sockaddr_in c_addr;

    memset(&c_addr, 0, sizeof(struct sockaddr_in));

    if (argc != 3) {
        printf("params is no ok\n");
        exit(-1);
    }

    // 1. 创建socket
    c_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (c_fd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2. 绑定地址
    c_addr.sin_family = AF_INET;
    c_addr.sin_port = htons(atoi(argv[2]));
    inet_aton(argv[1], &c_addr.sin_addr);

    // 3. 连接服务器
    if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1) {
        perror("connect");
        exit(-1);
    }

    while (1) {
        if (fork() == 0) {
            while (1) {
                // 4. 发送消息
                memset(msg, 0, sizeof(msg));
                printf("input: ");
                gets(msg);
                write(c_fd, msg, strlen(msg));
            }
        }

        while (1) {
            // 5. 接收消息
            memset(buf, 0, sizeof(buf));
            n_read = read(c_fd, buf, sizeof(buf));
            if (n_read == -1) {
                perror("read");
            } else {
                printf("read server: %s\n", buf);
            }
        }
    }
    close(c_fd);
    return 0;
}

6. FTP云盘

FTP云盘功能实现,包含以下功能:

  • lls: 列出本地的文件
  • lcd: 进入本地的文件路径
  • cd: 进入服务端的路径
  • ls: 列出服务端的文件
  • quit: 退出客户端
  • get: 获取服务端的文件
  • put: 上传客户端的文件

server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"

int get_cmd_type(char *cmd) {
    if (strcmp("ls", cmd) == 0) return LS;
    if (strcmp("pwd", cmd) == 0) return PWD;
    if (strcmp("quit", cmd) == 0) return QUIT;
    if (strstr(cmd, "cd")) return CD;
    if (strstr(cmd, "get")) return GET;
    if (strstr(cmd, "put")) return PUT;
    return -1;
}

char* getDir(char *cmd) {
    char *p = NULL;
    p = strtok(cmd, " ");
    p = strtok(NULL, " ");
    return p;
}

void cmdHandler(struct MSG msg, int c_fd) {
    int ret;
    int fd;
    int p_fd;
    FILE *fp;
    char *filename = NULL;
    char *file = NULL;
    char *filename2 = NULL;
    char dataBuf[1024] = {0};

    printf("%s\n", msg.cmd);
    ret = get_cmd_type(msg.cmd);
    printf("ret=%d\n", ret);

    switch (ret) {
        case LS:
        case PWD:
            fp = popen(msg.cmd, "r");
            fread(msg.cmd, sizeof(msg.cmd), 1, fp);
            write(c_fd, &msg, sizeof(msg));
            break;
        case CD:
            filename = getDir(msg.cmd);
            printf("filename: %s\n", filename);
            chdir(filename);
            break;
        case GET:
            file = getDir(msg.cmd);
            if (access(file, F_OK) == -1) {
                strcpy(msg.cmd, "NO this file");
                write(c_fd, &msg, sizeof(msg));
            } else {
                msg.type = 8;
                fd = open(file, O_RDWR);
                read(fd, dataBuf, sizeof(dataBuf));
                close(fd);
                strcpy(msg.cmd, dataBuf);
                write(c_fd, &msg, sizeof(msg));
                printf("success\n");
            }
            break;
        case PUT:
            filename2 = getDir(msg.cmd);
            printf("filename: %s\n", filename2);
            p_fd = open(filename2, O_RDWR | O_CREAT, 0666);
            write(p_fd, msg.dataBuf, strlen(msg.dataBuf));
            printf("write success\n");
            close(p_fd);
            break;
        case QUIT:
            printf("client out\n");
            exit(-1);
            break;
    }
}

int main(int argc, char **argv) {
    int s_fd;
    int c_fd;
    int n_read;
    struct sockaddr_in s_addr;
    struct sockaddr_in c_addr;
    struct MSG msg;

    memset(&s_addr, 0, sizeof(struct sockaddr_in));
    memset(&c_addr, 0, sizeof(struct sockaddr_in));

    if (argc != 3) {
        printf("params is no ok\n");
        exit(-1);
    }

    // 1. 创建socket
    s_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (s_fd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2. 绑定地址
    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(atoi(argv[2]));
    inet_aton(argv[1], &s_addr.sin_addr);

    bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));

    // 3. 监听端口
    listen(s_fd, 10);

    int len = sizeof(struct sockaddr_in);
    while (1) {
        // 4. 接受客户端连接
        c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &len);
        if (c_fd == -1) {
            perror("accept");
            continue;
        }

        printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));

        if (fork() == 0) {
            while (1) {
                memset(&msg.cmd, 0, sizeof(msg));
                n_read = read(c_fd, &msg, sizeof(msg));
                if (n_read == 0) {
                    printf("client out");
                    break;
                } else if (n_read > 0) {
                    cmdHandler(msg, c_fd);
                }
            }
        }
    }
    close(s_fd);
    close(c_fd);
    return 0;
}

client.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"

int get_cmd_type(char *cmd) {
    if (strcmp(cmd, "ls") == 0) return LS;
    if (strcmp(cmd, "pwd") == 0) return PWD;
    if (strcmp(cmd, "quit") == 0) return QUIT;
    if (strstr(cmd, "lcd")) return LCD;
    if (strstr(cmd, "cd")) return CD;
    if (strstr(cmd, "lls")) return LLS;
    if (strstr(cmd, "get")) return GET;
    if (strstr(cmd, "put")) return PUT;
    return -1;
}

char* getDir(char *cmd) {
    char *p = NULL;
    p = strtok(cmd, " ");
    p = strtok(NULL, " ");
    return p;
}

int cmd_handler(struct MSG msg, int c_fd) {
    int ret;
    int fd;
    char *filename = NULL;
    char *dir = NULL;
    char buf[32] = {0};

    ret = get_cmd_type(msg.cmd);
    switch (ret) {
        case LS:
        case PWD:
        case CD:
        case GET:
            write(c_fd, &msg, sizeof(msg));
            break;
        case LLS:
            system("ls");
            break;
        case LCD:
            filename = getDir(msg.cmd);
            printf("filename: %s\n", filename);
            chdir(filename);
            break;
        case PUT:
            strcpy(buf, msg.cmd);
            dir = getDir(buf);
            printf("dir: %s\n", dir);
            if (access(dir, F_OK) == -1) {
                printf("%s no exsit\n", dir);
            } else {
                fd = open(dir, O_RDWR);
                read(fd, msg.dataBuf, strlen(msg.dataBuf));
                close(fd);
                write(c_fd, &msg, sizeof(msg));
                printf("ok\n");
            }
            break;
        case QUIT:
            write(c_fd, &msg, sizeof(msg));
            close(c_fd);
            exit(-1);
            break;
    }
    return ret;
}

void cmd_serverMsg_handler(struct MSG msg, int c_fd) {
    int n_read;
    int fd;
    char *file = NULL;
    struct MSG getmsg;

    n_read = read(c_fd, &getmsg, sizeof(getmsg));
    if (n_read == 0) {
        printf("server quit\n");
    }

    if (getmsg.type == 8) {
        file = getDir(msg.cmd);
        fd = open(file, O_RDWR | O_CREAT, 0600);
        printf("open success\n");
        write(fd, getmsg.cmd, strlen(getmsg.cmd));
    } else {
        printf("-------------------------------------\n");
        printf("%s\n", getmsg.cmd);
        printf("-------------------------------------\n");
    }
}

int main(int argc, char **argv) {
    int c_fd;
    int ret;
    struct sockaddr_in c_addr;
    struct MSG msg;

    memset(&c_addr, 0, sizeof(struct sockaddr_in));

    if (argc != 3) {
        printf("params is no good\n");
        exit(-1);
    }

    // 1. 创建socket
    c_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (c_fd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2. 连接服务器
    c_addr.sin_family = AF_INET;
    c_addr.sin_port = htons(atoi(argv[2]));
    inet_aton(argv[1], &c_addr.sin_addr);

    if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1) {
        perror("connect");
        exit(-1);
    }

    printf("connect....\n");

    // 3. 处理命令
    while (1) {
        memset(msg.cmd, 0, sizeof(msg));
        gets(msg.cmd);
        ret = cmd_handler(msg, c_fd);
        if (ret == CD || ret == LLS || ret == LCD || ret == QUIT || ret == PUT) {
            continue;
        }
        if (ret == -1) {
            printf("this is command no exist\n");
            continue;
        }
        cmd_serverMsg_handler(msg, c_fd);
    }
    close(c_fd);
    return 0;
}

config.h

#define LS   0
#define PWD  1
#define CD   2
#define GET  3
#define PUT  4
#define LLS  5
#define LCD  6
#define QUIT 7

struct MSG {
    int type;
    char cmd[1024];
    char dataBuf[1024];
};

通过上述代码示例,展示了如何通过socket编程实现客户端和服务器之间的通信,包括简单的消息传递、多客户端处理以及文件传输等功能。希望这些示例代码能帮助理解和应用网络编程中的一些基本概念和技术。

  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值