20211406张顺扬

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

学习笔记11

网络编程是一种涉及计算机网络的软件开发技术,它允许不同计算机之间的通信和数据交换。在网络编程中,TCP/IP协议是基础,它定义了数据如何在网络上进行传输。

TCP/IP协议

TCP/IP(Transmission Control Protocol/Internet Protocol)是一组通信协议,它是互联网通信的基础。主要包括TCP和IP两个子协议。

IP地址和协议

  • IP地址: 每个主机都有一个唯一的32位IP地址,用于在网络中标识其位置。IP地址分为两部分,即网络部分和主机部分。

  • IP协议: 用于在IP主机之间发送和接收数据包。数据包由IP头、发送方IP地址、接收方IP地址以及数据组成。

路由器

  • TTL(Time To Live): IP包中的一个8位生存时间计数,防止数据包在网络中无限循环。每经过一个路由器,TTL减小1,如果减小到0,包会被丢弃。

  • 路由器: 用于接收和转发数据包,将数据包从源主机传输到目标主机。

UDP协议

UDP(User Datagram Protocol)是在IP上运行的协议,用于发送和接收数据报。相对于TCP,UDP不保证可靠性,但速度更快。

  • UDP特点: 快速高效,不保证数据可靠性。常用于实时应用,如视频流、音频等。

TCP协议

TCP(Transmission Control Protocol)是一种面向连接的协议,用于发送和接收数据流。相对于UDP,TCP提供了可靠的数据传输。

  • 端口编号: 多个应用程序可以同时使用TCP,每个应用程序由主机IP、协议(TCP/IP)、端口号唯一标识。

  • TCP连接: 类似于电话连接,通过建立连接来传输数据。通过socket()创建套接字,connect()建立连接,send()recv()进行数据传输。

网络字节序

计算机可以使用大端字节序或小端字节序,为了在网络中正确传输数据,需要进行字节序转换。

  • 大端/小端: 不同计算机体系结构对字节的存储顺序不同。网络字节序通常使用大端字节序。

数据流在TCP/IP网络中的传输

数据在网络中的传输涉及多个层级,从应用层到链路层,分别添加TCP或UDP报头、IP报头、以及网络链路层的帧。

套接字编程

套接字是实现网络编程的接口,使用一系列C语言库函数和系统调用来实现。在UNIX/Linux系统中,套接字API提供了socket()bind()等函数。

  • 套接字地址: 使用struct sockaddr_in定义,包括协议类型、端口号、IP地址等信息。

  • UDP套接字: 使用sendto()recvfrom()进行数据传输。

  • TCP套接字: 使用listen()监听连接请求,accept()接受连接,connect()建立连接,send()recv()进行数据传输。

UDP服务器-客户端程序

UDP服务器 (udp_server.c)

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

#define BUFLEN 256 // 缓冲区最大长度
#define PORT 1234 // 服务器端口号

char line[BUFLEN];
struct sockaddr_in me, client;
int sock, rlen, clen = sizeof(client);

int main() {
    printf("1. 创建一个UDP套接字\n");
    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    printf("2. 填充服务器地址和端口号\n");
    memset((char *)&me, 0, sizeof(me));
    me.sin_family = AF_INET;
    me.sin_port = htons(PORT);
    me.sin_addr.s_addr = htonl(INADDR_ANY); // 使用本地地址

    printf("3. 将套接字绑定到服务器IP和端口\n");
    bind(sock, (struct sockaddr*)&me, sizeof(me));

    printf("4. 等待数据报\n");
    while (1) {
        memset(line, 0, BUFLEN);
        printf("UDP服务器: 等待数据报\n");
        
        // recvfrom() 获取客户端IP和端口信息
        rlen = recvfrom(sock, line, BUFLEN, 0, (struct sockaddr *)&client, &clen);
        printf("从 [主机:端口] = [%s:%d] 收到一个数据报\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
        printf("rlen=%d: line=%s\n", rlen, line);

        printf("发送回复\n");
        sendto(sock, line, rlen, 0, (struct sockaddr*)&client, clen);
    }
}

UDP客户端 (udp_client.c)

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

#define SERVER_HOST "127.0.0.1" // 默认服务器IP: 本地主机
#define SERVER_PORT 1234 // 服务器端口号
#define BUFLEN 256 // 缓冲区最大长度

char line[BUFLEN];
struct sockaddr_in server;
int sock, rlen, slen = sizeof(server);

int main() {
    printf("1. 创建一个UDP套接字\n");
    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    printf("2. 填写服务器地址和端口号\n");
    memset((char *)&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(SERVER_PORT);
    inet_aton(SERVER_HOST, &server.sin_addr);

    while (1) {
        printf("输入一行:");
        fgets(line, BUFLEN, stdin);
        line[strlen(line) - 1] = 0; // 去除末尾的换行符

        printf("向服务器发送数据\n");
        sendto(sock, line, strlen(line), 0, (struct sockaddr *)&server, slen);

        memset(line, 0, BUFLEN);
        printf("尝试从服务器接收一行\n");
        rlen = recvfrom(sock, line, BUFLEN, 0, (struct sockaddr *)&server, &slen);
        printf("rlen=%d: line=%s\n", rlen, line);
    }
}


TCP回显服务器-客户端程序

TCP服务器 (tcp_server.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>

#define MAX 256
#define SERVER_PORT 1234

struct sockaddr_in server_addr, client_addr;
int mysock, csock; // socket描述符
int r, len, n; // 辅助变量

int server_init() {
    printf("================== 服务器初始化 ======================\n");

    // 创建TCP套接字
    printf("1: 创建一个TCP流套接字\n");
    mysock = socket(AF_INET, SOCK_STREAM, 0);
    if (mysock < 0) {
        printf("socket调用失败\n");
        exit(1);
    }

    // 填写服务器地址信息
    printf("2: 用主机IP和端口号信息填充server_addr\n");
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 本地主机
    server_addr.sin_port = htons(SERVER_PORT);

    // 将套接字绑定到服务器地址
    printf("3: 将套接字绑定到服务器地址\n");
    r = bind(mysock, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (r < 0) {
        printf("绑定失败\n");
        exit(3);
    }

    printf(" 主机名 = %s 端口 = %d\n", SERVER_HOST, SERVER_PORT);
    printf("4: 服务器正在监听....\n");
    listen(mysock, 5); // 队列长度为5

    printf("=================== 初始化完成 =======================\n");
}

int main() {
    char line[MAX];

    server_init();

    while (1) {
        // 尝试接受客户端请求
        printf("服务器: 正在接受新连接....\n");

        // 尝试接受客户端连接,将描述符保存在csock中
        len = sizeof(client_addr);
        csock = accept(mysock, (struct sockaddr *)&client_addr, &len);
        if (csock < 0) {
            printf("服务器: 接受错误\n");
            exit(1);
        }

        printf("服务器: 接受到来自\n");
        printf("-----------------------------------------------\n");
        printf("客户端: IP=%s 端口=%d\n",
               inet_ntoa(client_addr.sin_addr),
               ntohs(client_addr.sin_port));
        printf("-----------------------------------------------\n");

        // 处理循环:client_sock <== data ==> client
        while (1) {
            n = read(csock, line, MAX);
            if (n == 0) {
                printf("服务器: 客户端断开连接,服务器循环\n");
                close(csock);
                break;
            }

            // 显示收到的字符串
            printf("服务器: 读取 n=%d 字节; line=%s\n", n, line);

            // 将字符串回送给客户端
            n = write(csock, line, MAX);
            printf("服务器: 写入 n=%d 字节; ECHO=%s\n", n, line);
            printf("服务器: 准备处理下一个请求\n");
        }
    }
}

TCP客户端

(tcp_client.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>

#define MAX 256
#define SERVER_HOST "localhost"
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 1234

struct sockaddr_in server_addr;
int sock, r;

int client_init() {
    printf("======= 客户端初始化 ==========\n");

    printf("1 : 创建一个TCP套接字\n");
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        printf("socket调用失败\n");
        exit(1);
    }

    printf("2 : 用服务器的IP和端口号信息填充server_addr\n");
    // 初始化server_addr结构
    server_addr.sin_family = AF_INET; // 用于TCP/IP
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 本机主机
    server_addr.sin_port = htons(SERVER_PORT); // 端口号1234

    printf("3 : 连接到服务器....\n");
    r = connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (r < 0) {
        printf("连接失败\n");
        exit(3);
    }

    printf("4 : 连接成功\n");
    printf("-------------------------------------------------------\n");
    printf("服务器主机名=%s 端口号=%d\n", SERVER_HOST, SERVER_PORT);
    printf("-------------------------------------------------------\n");
    printf("========= 初始化完成 ==========\n");
}

int main() {
    int n;
    char line[MAX], ans[MAX];

    client_init();

    printf("******** 处理循环 *********\n");
    while (1) {
        printf("输入一行:");
        bzero(line, MAX); // 清零line[]
        fgets(line, MAX, stdin); // 从stdin中获取一行
        line[strlen(line) - 1] = 0; // 去掉末尾的\n

        if (line[0] == 0) // 如果是空行,退出
            exit(0);

        // 发送数据到服务器
        n = write(sock, line, MAX);
        printf("客户端: 写入 n=%d 字节; line=%s\n", n, line);

        // 从sock中读取一行并显示
        n = read(sock, ans, MAX);
        printf("客户端: 读取 n=%d 字节; 回显=%s\n", n, ans);
    }
}

上述程序包含了UDP服务器-客户端和TCP服务器-客户端的代码。这些代码通过套接字编程实现了基本的网络通信。请确保在运行这些程序之前,没有其他程序占用相同的端口号。

苏格拉底挑战




posted on   20211406张顺扬  阅读(8)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示