Linux中TCP/IP通信实践,FOR C/C++


一、客户机与服务机交互流程

在这里插入图片描述


二、函数详解

1. socket()

  • 作用:初始化,成功后返回服务器套接字文件描述符,失败返回-1
  • 函数原型:
    #include <sys/types.h>
    #include <sys/socket.h>
    
    int socket(int domain, int type, int protocol);
    
  • 参数:
    (1)domain:即协议域。又称为协议族(family),AF_INET
    (2)type:指定socket类型。常用的socket类型有:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。
    (3)protocol:指定协议。一般设置为0。

2. bind()

  • 作用:绑定地址端口,使文件描述符监听addr所描述的端口和地址
  • 函数原型:
    #include <sys/types.h>
    #include <sys/socket.h>
    
    int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
    
  • 参数:
    (1)sockfd:即需要绑定的服务器描述字
    (2)struct sockaddr* :通用指针类型。addr参数实际上可以接受多种协议的sockaddr结构体,该结构体存放协议族、端口和地址。
    (3)addrlen:用于指定结构体sockaddr的长度

3. listen()

  • 作用:声明socket处于监听状态。最多允许有backlog个客户端处与连接等待状态,如果接受到更多的连接请求就忽略。
  • 函数原型:
    #include <sys/types.h>
    #include <sys/socket.h>
    int listen(int sockfd,int backlog);
    
  • 参数
    (1)sockfd:服务器描述字
    (2)backlog:最多客户机等待的数目

4. accept()

  • 作用:使socket接受客户机网络请求。与客户机三次握手连接成功后,会返回一个新的已连接socket描述字。
  • 函数原型:
    #include <sys/types.h>
    #include <sys/socket.h>
    
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
  • 参数:
    (1)sockfd:服务器描述字
    (2)struct sockaddr* :指向struct sockaddr * ,返回客户端协议地址
    (3)addrlen:指向客户端协议地址长度

5. connect()

  • 作用:使客户端连接指定地址、端口的服务器
  • 函数原型:
    #include <sys/types.h>
    #include <sys/socket.h>
    
    int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
    
  • 参数:与bind()一致,bind()参数是自己的地址,connect()是对方(服务器)的地址。

6. write()

  • 功能:write()会把参数buf所指的内存写入count个字节到参数放到所指的文件fd内。
  • 返回值:如果顺利write()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中。
  • 函数原型:
    #include <stdio.h>
    #include <unistd.h> 
    
    ssize_t write(int fd, const void * buf, size_t count); 
    

7. read()

  • 功能:read()会把参数fd所指的文件传送count 个字节到buf 指针所指的内存中。
  • 返回值:返回实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取的数据。若参数count 为0, 则read()不会有作用并返回0。
  • 注意:read时fd中的数据如果小于要读取的数据,就会引起阻塞。
  • 函数原型:
    #include <stdio.h>
    #include <unistd.h> 
    
    ssize_t read(int fd, void * buf, size_t count);
    
  • read()、write()综合使用示例:
    char buf[1024];
    ssize_t s=read(new_sock,buf,sizeof(buf)-1); //从Client读取数据
    if(s>0){    //读取成功
        buf[s]=0;
        write(new_sock,buf,strlen(buf));
    }
    

8. close()

  • 功能:关闭当前描述字
  • 函数原型:
    #include <unistd.h>
    
    int close(int __fd);
    
  • 参数:
    (1) __fd:需要关闭的描述字

三、具体实现代码

1. 服务器端Server

(1)单进程

  • 只用于一台客户机对一台服务器使用,测试可在同一台电脑两个终端进行
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>  //C++中所需要的read()、write()

static void usage(const char* proc){
   printf("Usage:%s [local_ip] [local_port]\n",proc);
}

//初始化,返回服务器描述字
int startup(const char* _ip,int _port){ 
    int sock=socket(AF_INET,SOCK_STREAM,0); //socket():初始化
    if(sock<0){                             //成功后返回服务器描述字,一直存在
        perror("socket");
        exit(2);
    }
    printf("fd:%d\n",sock);
    struct sockaddr_in local;               //用sockaddr_in存储local(Server) addr和port信息
    local.sin_family=AF_INET;               //协议域
    local.sin_port=htons(_port);            //local port
    local.sin_addr.s_addr=inet_addr(_ip);   //local ip
    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0){    //bind():绑定local(Server) port和ip到sock
        perror("bind");
        exit(3);
    }
    if(listen(sock,10)<0){  //listen():接受一个网络请求,最多10个client等待
        perror("listen");
        exit(4);
    }
    return sock;
}
  
int main(int argc,char *argv[]){    //用sockaddr_in存储Client addr和port信息
    if(argc!=3){    //输入参数不合法
        usage(argv[0]);
        return 1;
    }
    int listen_sock=startup(argv[1],atoi(argv[2])); //初始化,生成服务器描述字
    while(1){
        struct sockaddr_in client;
        socklen_t len=sizeof(client);
        int new_sock=accept(listen_sock,(struct sockaddr*)&client,&len);    //接受一个Client的网络请求,成功后返回已连接的Scoket描述字
        if(new_sock<0){
            perror("accept");
            continue;
        }

        printf("get a new client:[%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));

        char buf[1024];
        while(1){
            ssize_t s=read(new_sock,buf,sizeof(buf)-1); //从Client读取数据
            printf("Get_Size =  %d\n",s);
            if(s>0){    //读取成功
                buf[s]=0;
                printf("client# %s\n",buf);
                write(new_sock,buf,strlen(buf));
            }
            else if(s==0){  //Client离线
                printf("client quit!\n");
                break;
            }
            else{   //读取失败
                perror("read");
                break;
            }
        }
        close(new_sock);    //读取失败就退出
    }
}

(2)多进程

  • 用于多台客户机对应一台服务器,也可在多个终端进行测试
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/wait.h>   //waitpid()
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>

#include<unistd.h>  //C++中所需要的read()、write()
  
static void usage(const char* proc){
    printf("Usage:%s [local_ip] [local_port]\n",proc);
}

int startup(const char* _ip,int _port){
    int sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0){
        perror("socket");
        exit(2);
    }
    printf("fd:%d\n",sock);
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port=htons(_port);
    local.sin_addr.s_addr=inet_addr(_ip);
    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0){
        perror("bind");
        exit(3);
    }
    if(listen(sock,10)<0){
        perror("listen");
        exit(4);
    }
    return sock;
}

int main(int argc,char *argv[]){
    if(argc!=3){
        usage(argv[0]);
        return 1;
    }
    int listen_sock=startup(argv[1],atoi(argv[2]));
    while(1){
        struct sockaddr_in client;
        socklen_t len=sizeof(client);
        int new_sock=accept(listen_sock,(struct sockaddr*)&client,&len);
        if(new_sock<0){
            perror("accept");
            continue;
        }

        printf("get a new client:[%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
        
        //握手成功,创建子进程
        pid_t id=fork();    //fork调用一次,返回两次,子进程返回0,父进程返回子进程ID
        if(id<0){
            perror("fork");
            continue;
        }
        //子进程
        else if(id==0){ 
            if(fork()>0){
                exit(0);
            }
            char buf[1024];
            while(1){
                ssize_t s=read(new_sock,buf,sizeof(buf)-1);
                if(s>0){
                    buf[s]=0;
                    printf("client[%s:%d]# %s\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buf);
                    write(new_sock,buf,strlen(buf));
                }
                else if(s==0){
                    printf("client quit!\n");
                    break;
                }
                else{
                    perror("read");
                    break;
                }
            }
            close(new_sock);
            exit(0);
        }
        //父进程
        else{
            close(new_sock);
            waitpid(id,NULL,0);//暂时停止父进程执行,处理僵尸进程,但并不会阻塞
        }
    }
}
/*多进程tcp测试可以通过两个主机连接一个局域网完成。

切换到root用户,关闭自己和对方主机的防火墙—-service iptables stop

检查是否有sshd—-ps aux | grep ssh

使用命令查看IP地址是否在一个局域网内—-ifconfig

与对方主机ping一下看能不能通—-ping 192.xxx.xxx.xxx

将tcp_client执行文件发送到对方主机—-scp tcp_client 192.xxx.xxx.xxx:/home

运行tcp_server,对方主机在/home目录下运行tcp_client 
 */

(3)多线程

  • 同多进程,一天服务器连接多台客户机,但相对于多进程而言更稳定,因为线程相对于进程是各自私有资源,各自独立
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>

#include<unistd.h>  //C++中所需要的read()、write()
  
static void usage(const char* proc){
    printf("Usage:%s [local_ip] [local_port]\n",proc);
}

int startup(const char* _ip,int _port){
    //初始化
    int sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0){
        perror("socket");
        exit(2);
    }
    printf("fd:%d\n",sock);
    //绑定ip,port
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port=htons(_port);
    local.sin_addr.s_addr=inet_addr(_ip);
    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0){
        perror("bind");
        exit(3);
    }
    //监听
    if(listen(sock,10)<0){
        perror("listen");
        exit(4);
    }
    return sock;
}

void *request(void *arg){
    int new_sock=(int)arg;
    char buf[1024];
    while(1){
        ssize_t s=read(new_sock,buf,sizeof(buf)-1);
        if(s>0){
            buf[s]=0;
            printf("client# %s\n",buf);
            write(new_sock,buf,strlen(buf));
        }
        else if(s==0){
            printf("client quit!\n");
            break;
        }
        else{
            perror("read");
            break;
        }
    }
    close(new_sock);
    return  (void*)0;
}

int main(int argc,char *argv[]){
    if(argc!=3){
        usage(argv[0]);
        return 1;
    }

    int listen_sock=startup(argv[1],atoi(argv[2]));
    while(1){
        struct sockaddr_in client;
        socklen_t len=sizeof(client);
        int new_sock=accept(listen_sock,(struct sockaddr*)&client,&len);
        if(new_sock<0){
            perror("accept");
            continue;
        }

        printf("get a new client:[%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
        //一旦三次握手成功就开启新线程
        pthread_t id;
        pthread_create(&id,NULL,request,(void*)new_sock);
        pthread_detach(id);

    }
}
/*多线程tcp测试可以通过两个主机连接一个局域网完成。

切换到root用户,关闭自己和对方主机的防火墙—-service iptables stop

检查是否有sshd—-ps aux | grep ssh

使用命令查看IP地址是否在一个局域网内—-ifconfig

与对方主机ping一下看能不能通—-ping 192.xxx.xxx.xxx

将tcp_client执行文件发送到对方主机—-scp tcp_client 192.xxx.xxx.xxx:/home

运行tcp_server,对方主机在/home目录下运行tcp_client 
 */

2. 客户端Client

单进程、多进程、多线程客户端均如下所示:

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>
 
#include<unistd.h>  //C++中所需要的read()、write()

static void usage(const char* proc){
    printf("Usage:%s [local_ip] [local_port]\n",proc);
}

int main(int argc,char* argv[]){
    if(argc!=3){
        usage(argv[0]);
        return 1;
    }

    int sock=socket(AF_INET,SOCK_STREAM,0);
        if(sock<0){
            perror("socket");
            return(2);
    }

    struct sockaddr_in peer;
    peer.sin_family=AF_INET;
    peer.sin_port=htons(atoi(argv[2]));
    peer.sin_addr.s_addr=inet_addr(argv[1]);

    if(connect(sock,(struct sockaddr*)&peer,sizeof(peer))<0){
        perror("connect");
        return 3;
    }
    char buf[1024];
    while(1){
        printf("Please Enter# ");
        fflush(stdout);     //清空输出缓存区
        fflush(stdin);
        ssize_t s = read(0,buf,sizeof(buf)-1);  //读取用户输入
        printf("Read_Size = %d\n",s);
        if(s>0){        
            buf[s-1]=0;
            if(strlen(buf) == 0)
                continue;

            write(sock,buf,strlen(buf));            //向服务器写数据
            
            //读取服务器回调数据并显示
            ssize_t _s=read(sock,buf,sizeof(buf)-1);
            if(_s>0){
                buf[_s]=0;
                printf("server echo# %s\n",buf);

            }
            else if(s==0){  
                printf("server quit!\n");
                break;
            }
        }
    }
    close(sock);
    return 0;
}

– 感谢https://blog.csdn.net/akiqiu/article/details/75098177中提供的代码,为自己学习提供了很大帮助,上述代码基于此修改调试至更优。

四、多进程、多线程知识补充

1. 多线程与多进程

  • 进程:操作系统中资源分配的最小单位
  • 线程:CPU调度的最小单位

2. 进程标识符

  • 每个进程都有一个非负整数表示唯一进程ID

3. 多进程基本函数

(1)fork():

  • 函数原型:
    #include<unistd.h>
    
    pid_t fork(void);
    
  • fork()调用一次,返回两次
    • 子进程:return 0
    • 父进程:return 新子进程ID
    • 返回负数:错误
      在这里插入图片描述

(2)waitpid():

  • 功能:暂时停止目前进程的执行,直到有信号来或者子进程结束,用于处理僵尸进程,但可以调节参数使之不阻塞父进程。
  • 示例:
    waitpid(id,NULL,0);//暂时停止父进程执行,处理僵尸进程,但并不会阻塞
    
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux C/C++后台架构开发是一门非常具有前景的技能,在互联网和各种电子设备都得到广泛应用。因此,这门课程的成长体系很重要,能够立足于现有技术发展趋势,不断完善内容,使学员能够跟随市场需求进行技术升级。 首先,课程应该注重基础知识的讲解,包括Linux操作系统的基础知识、C/C++编程语言的基础知识等,这是后续学习的基础。其次,应该注重实战训练,通过项目的实践来加深对知识的理解,并促进学员的技能提升。同时,要合理结合课程的理论知识和实践操作,培养学员的动手能力和实际应用技能。 除此之外,课程还应该关注行业技术变化的趋势,不断更新课程内容,讲解新技术的应用,使学员能够跟随技术的发展趋势提升自己。同时,要注重培养学员的团队合作能力和创新能力,帮助他们更好地适应团队工作和市场需求。 总之,Linux C/C++后台架构开发成长体系课程需要注重基础知识的讲解、实战训练、行业技术变化的跟进和团队合作与创新能力的培养。通过这些方面的努力,才能使学员掌握实用的技术,具备市场竞争力,并有能力适应未来技术的发展趋势。 ### 回答2: 作为一种开源的操作系统,Linux的应用广泛,尤其在服务器端,被广泛应用于Web服务器、数据库服务器等。因此,当今各大企业都需要专业的Linux后台架构开发人员来维护服务器的安全、稳定和高效运行。 而C/C++作为一种高效、可靠的编程语言,被广泛应用于Linux系统编程,尤其是在高性能、实时应用和底层驱动方面。因此,具备C/C++编程能力的Linux后台架构开发人员具有较高的市场竞争力。 在成长体系课程方面,专业的培训机构可以提供基础和高级的C/C++编程语言学习,以及Linux系统编程相关知识扎实的培训。而在课程设置和教学方式上,应该采取理论与实践相结合的方式,让学员在编程实践逐渐掌握并理解相关知识。同时,在应用开发的过程,特别是在搭建后台架构时,需要学会合理设计系统架构,选择合适的开发工具和技术,并能有效管理和维护系统。 此外,针对行业发展趋势和技术更新,培训机构应当不断更新课程内容,结合最新的技术趋势,为学员提供更具有竞争力的技术挑战,并开展多种实战项目实践。让学员在实践提升自己的技能和实践经验,不断提升自身的职业竞争力。 ### 回答3: Linux C/C++后台架构开发是一个广受欢迎的领域,无论是大型互联网企业还是小型企业都需要有相应的开发团队和技术人才。开发人员需要具备扎实的C/C++编程基础,熟悉Linux操作系统的运行机制和性能优化,了解分布式系统架构和网络通信协议等知识,以及掌握一定的数据库开发和管理经验。 针对此领域的开发人员,成长体系课程可以提供以下培训内容: 一、Linux操作系统原理:Linux系统的运行机制,常用命令和工具的使用方法,文件系统和进程管理等。 二、C/C++编程:C/C++基础语法和编程规范,数据结构和算法,内存管理和锁机制等。 三、分布式系统架构:分布式系统的概念和架构,通信协议和数据传输方式,分布式存储和计算等。 四、网络通信TCP/IP协议栈和网络编程,HTTP、Websocket等常用协议的使用和封装。 五、数据库开发和管理:常见数据库的概念和使用方法,SQL语言编写,数据库的设计和优化等。 通过以上培训内容的学习和实践,开发人员可以逐渐掌握Linux C/C++后台架构开发的技能和经验,不断提升能力和水平。同时,课程还将涉及团队协作和项目管理等方面的知识,培养学员的软技能和团队意识。最终,学员能够独立完成复杂的后台开发任务,为企业创造更大的价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值