zoukankan      html  css  js  c++  java
  • 基于TCP的客户端和服务器端的代码设计

    实验平台

    linux

    实验内容

    编写TCP服务器和客户端程序,程序运行时服务器等待客户端连接。一旦连接成功,服务器显示客户端的IP地址和端口号,并向客户端发送字符串

    实验原理

    TCP是面向连接的通信,其主要实现过程如下:

    我们将服务器代码分为两部分。

    1. init_tcp_server() tcp服务器的初始化

    2. main() 实现读写数据

    这样做的好处是main函数不必写的特别冗长,利于维护。从框架上来说,服务器的初始化也与读、写无关。

    tcp服务器的初始化----init_tcp_server()

    1. 创建socket

    sockfd = socket(AF_INET, SOCK_STREAM, 0); //AF_INT:ipv4, SOCK_STREAM:tcp协议

    2. 设置socket  当然这一步可以省略

    int32_t opt = 1;
    ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    详细说明:

    3. 绑定(bind函数)

    将socket和地址(包括ip,port)绑定。需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序

    struct sockaddr_in serveraddr;    //地址结构体

    bind函数

    bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))

    4. listen监听,将接收到的客户端放入队列

     listen(sockfd,10)  //第二个参数是队列长度

    5. 调用accept函数,从队列获取请求,返回socket描述符,如果没有请求(没有客户端连接),将会阻塞,直到获取请求

    int fd=accept(sockfd, (struct sockaddr*)&clientaddr, &clientaddr_len);

    至此服务器初始化完成,返回成功连接的套接字fd。

    服务器端代码如下:tcpserver.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <string.h> 
    #include <sys/socket.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    #define PORT 1234
    #define BACKLOG 10
    #define BUFFER_SIZE 100
    
    /**
     * @brief 初始化tcp服务器
     * @param[in] listenfd 监听套接字
     * @return -1 - 失败, socket 文件句柄 - 成功
     */
    int32_t init_tcp_server(int32_t listenfd)
    {
        struct sockaddr_in server;
        struct sockaddr_in client;
        
        int32_t connectfd = 0;    
        int32_t addrlen;
        int32_t ret = 0;
        addrlen = sizeof(client);
    
        /**< 创建一个tcp套接字 */
        listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (listenfd == -1)
        {
            perror("create socket failed!
    ");
            exit(1);
        }
    
        /**< 设置一个tcp套接字 */
        int32_t opt = 1;
        ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        if (ret < 0)
        {
            perror("set socket failed!
    ");
            exit(1);
        }
    
        /**< 设置服务器监听所有的IP地址 */
        bzero(&server, sizeof(struct sockaddr_in));
        server.sin_family = AF_INET;
        server.sin_port = htons(PORT);              /**< 主机字节序转化成网络字节序 */
        server.sin_addr.s_addr = htonl(INADDR_ANY);
    
        /**< 与服务器进行绑定 */
        if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1)
        {
            perror("bind error");
            exit(1);
        }
        
        /**< 监听 */
        if (listen(listenfd, BACKLOG) == -1)
        {
            perror("listen error");
            exit(1);
        }
    
        /**< 等待客户端连接,如果没有,一直阻塞 */
        if ((connectfd = accept(listenfd, (struct sockaddr *)&client, &addrlen)) == -1)        
        {
            perror("accept error");
            close(listenfd);
            close(connectfd);
            exit(1);
        }
        printf("You got a connection from client's ip is %s, port is %d
    ", inet_ntoa(client.sin_addr), htons(client.sin_port));    
    
        return connectfd;
    }
    
    int main()
    {
        int32_t listenfd = 0;
        int32_t connectfd = 0;
        char buf[BUFFER_SIZE] = "Welcome to my server";
    
        connectfd = init_tcp_server(listenfd);
    
        send(connectfd, buf, BUFFER_SIZE, 0);    /**< 发送信息到客户端 */
        close(connectfd);
        close(listenfd);
    }


    客户端

    同样,将客户端代码分成两部分:

    1. init_tcp_client() tcp客户端的初始化

    2. main() 实现读写数据

    客户端的初始化较为简单,如上图,只要实现socket和connect函数即可。但是我们希望可以手动输入客户端连接的IP地址,便于以后扩展,因此需要给客户端初始化传入一个参数。例如,输入:

    ./tcpclient 127.0.0.1

    客户端代码如下:tcpclient.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <string.h> 
    #include <sys/socket.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    #define PORT 1234
    #define BUFFER_SIZE 100
    
    int32_t init_tcp_client(char *ipaddr)
    {
        int sockfd = 0;
        struct sockaddr_in server;
    
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        {
            perror("create socket failed!
    ");
            exit(1);
        }
        
        bzero(&server, sizeof(struct sockaddr_in));
        server.sin_family = AF_INET;
        server.sin_port = htons(PORT);
        inet_pton(AF_INET, ipaddr, &server.sin_addr.s_addr);       /**< 点分十进制转换成二进制的网络字节序 */
        
        if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1)
        {
            perror("connect error");
            exit(1);
        }
        
        return sockfd;
    }
    
    int32_t main(int argc, char*argv[])
    {
        int32_t sockfd, num;
        char buf[BUFFER_SIZE];
        if (argc != 2)
        {
            printf("Usage:%s <IP Address>
    ",argv[0]);
            exit(1);
        }
        sockfd = init_tcp_client(argv[1]);
    
        if ((num = recv(sockfd, buf, BUFFER_SIZE, 0)) == -1)
        {
            perror("recv error");
            exit(1);
        }
    
        buf[num - 1] = '';
        printf("Server Message: %s
    ", buf);
        close(sockfd);
    
        return 0;
    }

    Makefile文件如下:

    all:server client
    
    server:tcpserver.c
        gcc tcpserver.c -o server
        
    client:tcpclient.c
        gcc tcpclient.c -o client
        
    clean:
        rm -rf server client

    实验结果如下:

     

  • 相关阅读:
    《秒杀系统架构分析与实战 》
    《豆瓣的基础架构》
    转--《亿级用户下的新浪微博平台架构 》
    转-《蚂蚁金服11.11:支付宝和蚂蚁花呗的技术架构及实践 》
    hdu2029
    hdu2027
    hdu2026(water~~)
    PHP电影小爬虫(2)
    今天来做一个PHP电影小爬虫。
    PHP Simple HTML DOM解析器
  • 原文地址:https://www.cnblogs.com/gezhuangzhuang/p/12668194.html
Copyright © 2011-2022 走看看