C语言实现简单的客户端和服务器模型

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38253837/article/details/80397391

最近刚刚开始学习网络编程,今天看了童永清的《Linux C编程实战》一书,在看到这本书网络编程后面的部分,有一个实现面向连接的Client/Server实例,在此将它整理了一下,并加上一些自己的备注,希望对大家有所帮助。
先简要介绍一下这个实例:客户端通过IP和端口号向服务器发起连接,服务器收到连接后,打印客户端的IP,并请求客户端用户名和密码,客户端输入用户名和密码,服务器检查用户名和密码是否匹配,若匹配成功,则服务器在终端打印该用户已登录,客户端退成程序。
一、服务器端源代码

//my_server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include "my_recv.h"

#define SERV_PORT 6666  //port
#define LISTENQ 12  //the max request deque

#define VALID_USERINFO "y"  //the flag of valid user
#define INVALID_USERINFO "n"    //the flag of invalid user

#define USERNAME 0  //user name
#define PASSWORD 1  //password

//userinfo
typedef struct userinfo {
    char username[32];
    char password[32];
}userInfo;

//users
userInfo users[] = {
    {"zhangsan", "123456"},
    {"linux", "unix"}
};

/**
 * Function:find_name
 * Description:find the num of the name in the list of users
 * Input: user name
 * Output:the num of the user name
 */
int find_name(const char *name) {
    int i = 0;

    if (NULL == name) {
        printf("name is NULL");
        return -2;
    }
    while (users[i].username[0] != ' ') {
        if (strcmp(users[i].username, name) == 0) {
            return i;
        }
        ++i;
    }
    return -1;
}
//send data to client
void send_data(int conn_fd, const char *string) {
    if (send(conn_fd, string, strlen(string), 0) < 0) {
        my_err("send", __LINE__);
    }
}

int main(int args, char *argv[]) {

    int sock_fd, conn_fd;   //the descriptor of socket
    int optval; //option value
    int flag_recv = USERNAME;   //the flag of name or password
    int ret;    //
    int name_num;   //the num of user
    pid_t pid;  //the id of the process
    socklen_t cli_len;  
    struct sockaddr_in cli_addr, serv_addr;
    char recv_buf[128]; //the buffer of the receive data

/**
 * int socket(int domain, int type, int protocol);
 * Input:
 *      domain: this selects the protocol family
 *      type: the type of socket
 *      0:the protocol is decided by the first and the second argument
*Output::On success, a file descriptor for the new socket,
         On error, -1 is returned, and errno is set appropriately.
*/
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);  //create a new socket 
    if (sock_fd < 0) {
        my_err("socket", __LINE__);
    }
    optval = 1;
    //set the socket so that we can use it again
    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int) ) < 0) {
        my_err("setsockopt", __LINE__);
    }

    memset(&serv_addr, 0, sizeof(struct sockaddr_in));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    //bind the socket to the local port
    if (bind(sock_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in)) < 0) {
        my_err("bind", __LINE__);
    }
    //set the socket to listen status
    if (listen(sock_fd, LISTENQ) < 0) {
        my_err("listen", __LINE__);
    }

    cli_len = sizeof(struct sockaddr_in);

    while (1) {
        //wait a request
        conn_fd = accept(sock_fd, (struct sockaddr *)&cli_addr, &cli_len);
        if (conn_fd < 0) {
            my_err("accept", __LINE__);
        }

        printf("accept a new client, ip is %s\n",inet_ntoa(cli_addr.sin_addr));

        if ((pid =fork()) == 0) {
            while (1) {
                if ((ret = recv(conn_fd, recv_buf, sizeof(recv_buf), 0)) < 0) {
                    perror("recv");
                }
                //the flag of end
                recv_buf[ret - 1] = '\0';
                if (flag_recv == USERNAME) {
                    name_num = find_name(recv_buf);
                    switch(name_num) {
                        case -1:
                            send_data(conn_fd, "n\n");
                            break;
                        case -2: 
                            exit(1);
                            break;
                        default:
                            send_data(conn_fd, "y\n");
                            flag_recv = PASSWORD;
                    }
                }
                else if (flag_recv == PASSWORD) {
                    if (strcmp(users[name_num].password, recv_buf) == 0) {
                        //send data to client
                        send_data(conn_fd, "y\n");
                        send_data(conn_fd, "Welcome to my server\n");
                        printf("%s login\n", users[name_num].username);
                    }
                    else {
                        send_data(conn_fd, "n\n");
                    }
                }
            }
            close(sock_fd);
            close(conn_fd);
            exit(0);
        }
        else {
            close(conn_fd);
        }
    }

    return 0;
}

二、客户端源代码

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

#define INVALID_USERINFO 'n'
#define VALID_USERINFO 'y'

//get user's information from user's input
int get_userinfo(char *buf, int len) {
    int i;
    int c;

    if (buf == NULL) {
        return -1;
    }

    i = 0;
    while (((c = getchar()) != '\n') && (c != EOF) && (i < len-2)) {
        buf[i++] = c;
    }
    buf[i++] = '\n';
    buf[i++] = '\0';

    return 0;
}

//get information of user from server
void input_userinfo(int conn_fd, const char *string) {
    char input_buf[32];
    char recv_buf[BUFSIZE];
    int flag_userinfo;

    do {
        printf("%s:", string);
        if (get_userinfo(input_buf, 32) < 0) {
            printf("error return from get_userinfo\n");
            exit(1);
        }
        //send user's information to server
        if (send(conn_fd, input_buf, strlen(input_buf), 0) < 0) {
            my_err("send", __LINE__);
        }
        //receive information from server
        if (my_recv(conn_fd, recv_buf, sizeof(recv_buf)) < 0) {
            printf("data is to long\n");
            exit(1);
        }
        if (recv_buf[0] == VALID_USERINFO) {
            flag_userinfo = VALID_USERINFO;
        }
        else {
            printf("%s error, input again\n", string);
            flag_userinfo = INVALID_USERINFO;
        }
    } while (flag_userinfo == INVALID_USERINFO);

}

int main(int argc, char *argv[]) {
    int                 i;
    int                 ret;
    int                 conn_fd;
    int                 serv_port;
    struct sockaddr_in  serv_addr;
    char                recv_buf[BUFSIZE];

    if (argc != 5) {
        printf("Usage: [-p] [serv_port] [-a] [serv_address]\n");
        exit(1);
    }

    memset(&serv_addr, 0, sizeof(struct sockaddr_in));
    serv_addr.sin_family = AF_INET;

    for (i = 1; i < argc; ++i) {
        //is the argument a flag of port
        if (strcmp("-p", argv[i]) == 0) {
            //atoi():convert a string into an integer
            serv_port = atoi(argv[i+1]);
            if (serv_port < 0 || serv_port > 65535) {
                printf("invalid ser_addr.sinport\n");
                exit(1);
            }
            else {
                //htons(): host to network short
                serv_addr.sin_port = htons(serv_port);
            }
            continue;
        }
        //is the argument a flag of ip
        if (strcmp("-a", argv[i]) == 0) {
            if (inet_aton(argv[i+1], &serv_addr.sin_addr) == 0) {
                printf("invalid server ip address\n");
                exit(1);
            }
            continue;
        }
    }
    if (serv_addr.sin_port == 0 || serv_addr.sin_addr.s_addr == 0) {
        printf("Usage: [-p] [serv_addr.sin_port] [-a] [ser_address]\n");
        exit(1);
    }
    //create a socket
    conn_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (conn_fd < 0) {
        my_err("connect", __LINE__);
    }
    //connect server
    if (connect(conn_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) < 0) {
        my_err("connect", __LINE__);
    }

    input_userinfo(conn_fd, "username");
    input_userinfo(conn_fd, "password");

    //receive data from server
    if ((ret = my_recv(conn_fd, recv_buf, sizeof(recv_buf))) < 0) {
        printf("data is too long\n");
        exit(1);
    }

    for (i = 0; i < ret; ++i) {
        printf("%c", recv_buf[i]);
    }
    printf("\n");

    close(conn_fd);

    return 0;
}

三、读取数据以及错误处理
头文件的定义:

//my_recv.h
#ifndef _MY_RECV_H
#define _MY_RECV_H

#define BUFSIZE 1024

void my_err(const char *err_string, int line);
int my_recv(int conn_fd, char *data_buf, int len);

#endif

源代码:

//my_recv.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "my_recv.h"

void my_err(const char *err_string, int line) {
    fprintf(stderr, "line:%d ", line);
    perror(err_string);
    exit(1);
}

int my_recv(int conn_fd, char *data_buf, int len) {
    static char recv_buf[BUFSIZE];
    static char *pread;
    static int len_remain = 0;
    int i;
    if (len_remain <= 0) {
        if ((len_remain = recv(conn_fd, recv_buf, sizeof(recv_buf), 0)) < 0) {
            my_err("recv", __LINE__);
        }
        else if (len_remain == 0) {
            return 0;
        }
        pread = recv_buf;
    }

    for (i = 0; *pread != '\n'; ++i) {
        if (i > len) {
            return -1;
        }
        data_buf[i] = *pread++;
        len_remain--;
    }
    len_remain--;
    pread++;

    return i;
}

五、运行并测试程序
首先运行服务端程序:

./my_server

运行结果:
这里写图片描述
再运行客户端程序:

./my_client -a 127.0.0.1 -p 6666

输入用户名和密码后结果:
这里写图片描述
此时服务器端收到客户端的连接:
这里写图片描述
整个程序的运行结果就是这样啦~

猜你喜欢

转载自blog.csdn.net/qq_38253837/article/details/80397391