zoukankan      html  css  js  c++  java
  • 基于TCP的C/S初级网络编程1

    导读

    本篇实现C/S架构的“计算器”,与大家分享。

    看了会网络编程,便不自觉YY了下:实现一个简单的计算器,客户端给出简单的运算,服务端负责运算。这一小项目做起来很有意思,而且难度不大,所以推荐初学者试着去做做。下面分享在实现上述“计算器”的过程。

    简单的基于tcp协议的 C/S编程都离不开这几个函数:

    服务端:socket,bind,listen,accept,recv,send
    客户端:socket,connect,recv,send

    因为“计算器”还设计涉及客户端的阻塞(因为客户端提交了运算要求过后,服务端可能要等会才能回送计算结果,这时要求客户端阻塞等候),所以涉及select函数。select函数用途广泛,很容易实现阻塞功能。介绍一个文档,有兴趣可以参考一下:http://wenku.baidu.com/view/0ea86ffdc8d376eeaeaa3198.html

    客观测试环境

    可以在一个主机上同时进行服务端和客户端的测试,只要客户在connect的时候用回环地址(或者本地静态IP地址)连接服务端就可以。

    实现细节

    socket不成功怎么办,bind不成功怎么办,listen不成功怎么办...都有相应的出错处理,编程过程中养成这种“考虑周细”的习惯(考虑所有的情况,比如出错的时候打印错误信息),对调试很有帮助。

    http://www.gnu.org/software/libc/manual/html_node/Internet-Address-Formats.html

    — Data Type: struct sockaddr_in

    This is the data type used to represent socket addresses in the Internet namespace. It has the following members:

    sa_family_t sin_family
    This identifies the address family or format of the socket address. You should store the value AF_INET in this member. See Socket Addresses.
    struct in_addr sin_addr
    This is the Internet address of the host machine. See Host Addresses, and Host Names, for how to get a value to store here.
    unsigned short int sin_port
    This is the port number. See Ports.

    注:sockaddr_in此类型数据在使用之前请务必bzero

    其中sin_addr是结构体,

    http://www.gnu.org/software/libc/manual/html_node/Host-Address-Data-Type.html

    — Data Type: struct in_addr

    This data type is used in certain contexts to contain an IPv4 Internet host address. It has just one field, named s_addr, which records the host address number as an uint32_t.

    inet_pton和inet—_ntop方便点分十进制IP地址字符串和uint32_t(IP地址是4字节,应为网络字节序)的转换。

    select

    如上所述要求,“因为客户端提交了运算要求过后,服务端可能要等会才能回送计算结果,这时要求客户端阻塞等候”,select经常扮演阻塞的角色。
    http://www.gnu.org/software/libc/manual/html_node/Waiting-for-I_002fO.html(文档很详细)
    因此客户端提交运算要求之后,需要将其socket读功能阻塞,直到有数据(即服务端回送的结果)时才进行读取。如果用轮询的方法,很浪费CPU。

    上实验结果图片解解馋

    服务器启动

    1_thumb.png

    客户端启动,太快了,结果都出来了

    2_thumb.png

    服务器处理结束,退出

    3_thumb.png

    计算器要求:客户需要传递后缀表达式简单运算(如上图),服务器直接运行就即可。
    缺陷:此计算器只服务于一个客户,其他不给予处理;此计算器进一步改进可以实现接受不只一个客户的请求。

    client

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <arpa/inet.h>
    #include <string.h>
    #include <string.h>
    
    #define MAXSLEEP 1024
    
    int connect_retry(int sockfd,const struct sockaddr * addr,socklen_t alen)
    {
        int nsec;
    
        printf("connecting\n");
        for(nsec = 1; nsec <= MAXSLEEP; nsec<<=1)
        {
            if(connect(sockfd,addr,alen) == 0)
            {
                printf("connected\n");
                return 0;
            }// if
            if(nsec <= MAXSLEEP/2)//    delay
                sleep(nsec);
        }// for:
        return 0;
    }
    
    int main(int argc,char * argv[])
    {
        if(argc != 4)
        {
            printf("you must input 4 arg\n");
            return 1;
        }// if
    
        int fd;    
        struct sockaddr_in si,server;
        char addr[20],buf[20],bufrecv[20];
    
        bzero(bufrecv,sizeof(bufrecv));
        sprintf(addr,"127.0.0.1");
    
        fd = socket(AF_INET,SOCK_STREAM,0);//   create socker fd;
        printf("socket ok\n");
    
    //prepare server addr
        bzero(&server,sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(6000);
        inet_pton(AF_INET,addr,(void *)&server.sin_addr);
        printf("server ok\n");
    
    //prepare request data
        bzero(buf,sizeof(buf));
        sprintf(buf,"%c%c%c",argv[1][0],argv[2][0],argv[3][0]);
    
    //connect
        if(connect_retry(fd,(struct sockaddr *)&server,sizeof(server)) < 0)
        {
            printf("connect error\n");
            return 1;
        }// if
    
    //send
        if(send(fd,buf,20,0) < 0) 
        {
            printf("client send error\n");
            return 1;
        }// if
    
    //select
        fd_set readfd;
        FD_ZERO(&readfd);
        FD_SET(fd,&readfd);
        int t;
    
        if((t = select(FD_SETSIZE,&readfd,NULL,NULL,NULL)) < 0)
        {
            printf("select error\n");
            return 1;
        }// if
    
    //recv
        bzero(bufrecv,sizeof(bufrecv));
        recv(fd,bufrecv,20,0);
        printf("%s\n",bufrecv);
    
        close(fd);
        return 0;
    }

    server

    #include <stdio.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <errno.h>
    #include <ctype.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <string.h>
    
    char bufret[20];
    
    int initserver(int type,const struct sockaddr * addr,socklen_t alen,int qlen)
    {
        int fd;
        int err = 0;
    
        if((fd = socket(addr->sa_family,type,0)) < 0)
            return -1;
    
        printf("binding\n");
        if(bind(fd,addr,alen) < 0)
        {
            err = errno;
            goto errout;
        }// if
        printf("bind succeed \n");
    
        if(type == SOCK_STREAM || type == SOCK_SEQPACKET)
        {
            printf("listening\n");
            if(listen(fd,1) < 0)
            {
                err = errno;
                printf("listen error\n");
                goto errout;
            }// if
        }// if
        printf("listened \n");
        return (fd);
    
    errout:
        close(fd);
        errno = err;
        return -1;
    }
    
    int serve(int sockfd)
    {
        int a,b;
        char op,buf[25];
    
        int ret,addrlen = sizeof(struct sockaddr_in),clfd; 
        struct sockaddr_in client;
    
        bzero(&client,sizeof(client));
    
    //accept   
        printf("accepting\n");
        clfd = accept(sockfd,(struct sockaddr *)&client,&addrlen);
    
    //recv
        printf("accepted\n");
        bzero(buf,sizeof(buf));
        recv(clfd,buf,20,0);
        printf("recived\n");
    
    //calculate
        a = buf[0] - '0';
        b = buf[1] - '0';
        op = buf[2];
    
        switch(op)
        {
            case '+':ret = a + b;break;
            case '-':ret = a - b;break;
            case '*':ret = a * b;break;
            case '/':ret = a / b;break;
        }// switch
    
        sprintf(bufret,"the result:%d",ret);
    
    //send
        printf("sending\n");
        if(send(clfd,bufret,20,0) < 0)
        {
            printf("server send error\n");
            return -1;
        }// if
        printf("sended,server end\n");
        return 0;
    }
    
    int main(int argc,char * argv[])
    {
        int sockfd;
        char addr[20];
    
        bzero(addr,sizeof(addr));
        sprintf(addr,"127.0.0.1");
    
        struct sockaddr_in server;
        bzero(&server,sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(6000);
        //server.sin_addr.s_addr = htonl(INADDR_ANY);
        inet_pton(AF_INET,addr,(void *)&server.sin_addr);
    
    //prepare server
        if((sockfd = initserver(SOCK_STREAM,(struct sockaddr *)&server,sizeof(server),1)) < 0)
        {
            printf("initserver error\n");
            return 0;
        }// if
    
        printf("serving\n");
    
    //serve
        serve(sockfd);
        close(sockfd);
        return 0;
    }

    以上纯属笔者YY后的作品,还存在很多的缺陷与不足;抛砖引玉,与广大朋友分享。欢迎创意建议提议。另,如有错误,欢迎斧正。

    本文完 2012-08-02

    捣乱小子 http://www.daoluan.net/blog/

  • 相关阅读:
    UVALive 7509 Dome and Steles
    HDU 5884 Sort
    Gym 101194H Great Cells
    HDU 5451 Best Solver
    HDU 5883 The Best Path
    HDU 5875 Function
    卡特兰数
    UVa 11729 Commando War 突击战
    UVa 11292 The Dragon of Loowater 勇者斗恶龙
    Spark Scala Flink版本对应关系
  • 原文地址:https://www.cnblogs.com/daoluanxiaozi/p/2620686.html
Copyright © 2011-2022 走看看