20242817-李臻-课上测试:网络编程

发布于:2025-05-01 ⋅ 阅读:(33) ⋅ 点赞:(0)

课上测试:网络编程

一、实验要求

  1. 在 Ubuntu 或 openEuler 中完成任务(推荐openEuler)

  2. 参考《head first C》实现knock knock服务器,提交代码knock.c,编译运行过程(3分)

  3. 使用多线程实现knock knock服务器,提交代码knockmt.c,编译运行过程,至少两个客户端测试,服务器运行结果中要打印线程id(10分)

  4. 提交git log结果(1分)

二、实验过程

任务一:单线程Knock-Knock服务器实现

knock.c

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

#define PORT 30000
#define BUFFER_SIZE 255

int listener_d;

void error(const char *msg) {
    perror(msg);
    exit(1);
}

void handle_shutdown(int sig) {
    if (listener_d) close(listener_d);
    fprintf(stderr, "\nServer shutdown.\n");
    exit(0);
}

int read_in(int socket, char *buf, int len) {
    char *s = buf;
    int slen = len;
    int c = recv(socket, s, slen, 0);
    while ((c > 0) && (s[c-1] != '\n')) {
        s += c;
        slen -= c;
        c = recv(socket, s, slen, 0);
    }
    if (c < 0) return c;
    else if (c == 0) buf[0] = '\0';
    else s[c-1] = '\0';
    return len - slen;
}

int main() {
    signal(SIGINT, handle_shutdown);

    // 创建socket
    listener_d = socket(PF_INET, SOCK_STREAM, 0);
    if (listener_d == -1) error("Can't open socket");

    // 绑定端口
    struct sockaddr_in name;
    name.sin_family = PF_INET;
    name.sin_port = htons(PORT);
    name.sin_addr.s_addr = htonl(INADDR_ANY);
    int reuse = 1;
    if (setsockopt(listener_d, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) 
        error("Can't set reuse");

    if (bind(listener_d, (struct sockaddr *)&name, sizeof(name)) == -1)
        error("Can't bind");

    // 监听
    if (listen(listener_d, 10) == -1) error("Can't listen");

    printf("Server running on port %d...\n", PORT);

    while (1) {
        struct sockaddr_storage client_addr;
        unsigned int address_size = sizeof(client_addr);
        int connect_d = accept(listener_d, (struct sockaddr *)&client_addr, &address_size);
        if (connect_d == -1) error("Can't accept connection");

        // 处理客户端请求
        char buf[BUFFER_SIZE];
        send(connect_d, "Knock! Knock!\n> ", 16, 0);
        read_in(connect_d, buf, BUFFER_SIZE);
        if (strncasecmp("Who's there?", buf, 12)) {
            send(connect_d, "Protocol error: Expected 'Who's there?'\n", 40, 0);
        } else {
            send(connect_d, "Oscar\n> ", 8, 0);
            read_in(connect_d, buf, BUFFER_SIZE);
            if (strncasecmp("Oscar who?", buf, 10)) {
                send(connect_d, "Protocol error: Expected 'Oscar who?'\n", 38, 0);
            } else {
                send(connect_d, "Oscar silly question, you get a silly answer!\n", 46, 0);
            }
        }
        close(connect_d);
    }
    return 0;
}
编译运行结果:

服务器运行:
在这里插入图片描述
客户端输入:
在这里插入图片描述


任务二:多线程Knock-Knock服务器实现

knockmt.c

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

#define PORT 30000
#define BUFFER_SIZE 255

int listener_d;

void error(const char *msg) {
    perror(msg);
    exit(1);
}

void handle_shutdown(int sig) {
    if (listener_d) close(listener_d);
    fprintf(stderr, "\nServer shutdown.\n");
    exit(0);
}

int read_in(int socket, char *buf, int len) {
    char *s = buf;
    int slen = len;
    int c = recv(socket, s, slen, 0);
    while ((c > 0) && (s[c-1] != '\n')) {
        s += c;
        slen -= c;
        c = recv(socket, s, slen, 0);
    }
    if (c < 0) return c;
    else if (c == 0) buf[0] = '\0';
    else s[c-1] = '\0';
    return len - slen;
}

void* handle_client(void *arg) {
    int connect_d = *(int *)arg;
    char buf[BUFFER_SIZE];
    pthread_t tid = pthread_self();
    printf("Thread %lu handling client\n", tid);

    send(connect_d, "Knock! Knock!\n> ", 16, 0);
    read_in(connect_d, buf, BUFFER_SIZE);
    if (strncasecmp("Who's there?", buf, 12)) {
        send(connect_d, "Protocol error: Expected 'Who's there?'\n", 40, 0);
    } else {
        send(connect_d, "Oscar\n> ", 8, 0);
        read_in(connect_d, buf, BUFFER_SIZE);
        if (strncasecmp("Oscar who?", buf, 10)) {
            send(connect_d, "Protocol error: Expected 'Oscar who?'\n", 38, 0);
        } else {
            send(connect_d, "Oscar silly question, you get a silly answer!\n", 46, 0);
        }
    }
    close(connect_d);
    free(arg);
    return NULL;
}

int main() {
    signal(SIGINT, handle_shutdown);

    // 创建socket
    listener_d = socket(PF_INET, SOCK_STREAM, 0);
    if (listener_d == -1) error("Can't open socket");

    // 绑定端口
    struct sockaddr_in name;
    name.sin_family = PF_INET;
    name.sin_port = htons(PORT);
    name.sin_addr.s_addr = htonl(INADDR_ANY);
    int reuse = 1;
    if (setsockopt(listener_d, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)))
        error("Can't set reuse");

    if (bind(listener_d, (struct sockaddr *)&name, sizeof(name)) == -1)
        error("Can't bind");

    // 监听
    if (listen(listener_d, 10) == -1) error("Can't listen");

    printf("Multithreaded server running on port %d...\n", PORT);

    while (1) {
        struct sockaddr_storage client_addr;
        unsigned int address_size = sizeof(client_addr);
        int *connect_d = malloc(sizeof(int));
        *connect_d = accept(listener_d, (struct sockaddr *)&client_addr, &address_size);
        if (*connect_d == -1) error("Can't accept connection");

        pthread_t thread;
        if (pthread_create(&thread, NULL, handle_client, connect_d) != 0) {
            perror("Failed to create thread");
            close(*connect_d);
            free(connect_d);
        }
    }
    return 0;
}
编译运行结果

运行knockmt,打开新的terminal去访问server,进行如下命令及对话:

telnet 127.0.0.1 30000
> Knock! Knock!
> Who's there?
> Oscar
> Oscar who?
> Oscar silly question, you get a silly answer!

客户端得到服务器应答
在这里插入图片描述
多运行几个客户端之后,查看服务器界面,发现服务器端打印了线程id。每个客户端连接由独立线程处理,通过 pthread_self() 打印线程ID,支持并发访问。

在这里插入图片描述

任务三:Gitee仓库管理

仓库链接:https://gitee.com/li-zhen1215/homework/tree/master/Week8/keshang4

在这里插入图片描述
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到