zoukankan      html  css  js  c++  java
  • linux网络编程之socket编程(二)

    今天继续对socket编程进行研究,这里会真正开如用socket写一个小例子,进入正题:

    TCP客户/服务器模型:
     
    关于这个模型的流程这里就不多说了,比较容易理解,下面则利用这种模型来编写一个实际的例子。
    回射客户/服务器:
    这个例子的效果就是:客户端从命令行获取一行命令,然后发送给服务器端,当服务端接收到这行命令之后,不做任何操作,将其又回送给客户端,然后客户端进行回显,下面则开始一步步来实现这样的效果,来初步感受下Socket编程:
    首先编写服务端:echosrv.c
    第一步:创建套接字:
    关于第一个参数domain,man帮助中也有说明:
    但是,AF_INET等价于PF_INET,这里推荐用后者,因为刚好代表protocol family含义,下面代码如下:
     
    第二步:绑定一个地址到套接字上:
     首先准备一下第二个参数,也就是要绑定的地址:
    其中绑定地址还有其它两种方式:
    另外,其实"servaddr.sin_addr.s_addr = htonl(INADDR_ANY);"这种写法是可以省略掉的,因为它是全0,但这里为了显示说明所以保留。
    【提示】:关于上面的写法,可以参考博文:http://www.cnblogs.com/webor2006/p/3905799.html 中的地址转换函数
    下面开始进行绑定:
    第三步:则开始进行监听:
     
    具体代码如下:
    其中SOMAXCONN可以从man帮助中查看到:
    它代表了socket的并发最大连接个数。
    另外还得注意,套接字有被动套接字和主动套接字之分,当调用listen之后,该socket就变动被动套接字了,需要由主动套接字来发起连接,主动套接字是用connect函数来发起连接的。
     
    第四步:从已完成连接队列中返回第一个连接:
     
    接下来,则进行数据的接收,并将数据回显给客户端:
    accept函数会返回一个新的套接字,注意:此时的套接字不再是被动套接字,而变为了主动:
    可以通过accept的man手册来得知:
    下面,则开始从该套接字中读取客户端发过来的数据:
    至此,服务端的代码都已经编写完了,下面则先编译一下:
    查看man帮助:
    于是在代码中加入头:
    再次编译:
    还是出错,那IPPPOTO_TCP是在哪定义的呢?
    可以通过以下命令进行查找:
    于是乎,加上该头文件后再编译:
    用同样的办法来进行查找:
    于是加入它:
    再次编译:
    还是报错,对于这里面对应的头文件这里就不具体一个个查找了,不然有点充数的嫌疑,将所有头文件加上再次编译:
     
    接下来,开始编写客户端的代码:echocli.c
    首先创建一个socket:
    第二步开始与服务器进行连接:
     
    【说明】:用connect发起连接的套接字是主动套接字。
    连接成功之后,就可以向服务器发送数据了:
    另外,服务端在使用资源之后,最后也得关闭掉,所以修改服务端程序如下:
    这时,客户端程序也已经编写完成,下面编译运行看一下效果:
    也就是第一次客户端输入很长的字符串,而第二次输入很短的字符串时,这时就会输出有问题,照理应该是客户端输入什么,服务端就会回显给客户端,也就是打印两条一模一样的语句,产生这样的问题原因是什么呢?
    下面来用图来分析一下:
     
    所以,解决该问题的思路就是每次循环时将值初始化一下既可,修改代码如下:
     再次编译运行:
    关于这个原因,之后会来解决,先占且不关心,等一会就会正常了,正常运行的效果如下:
    这样,就实现了客户端与服务器端的socket通讯了,最终的代码如下:
    echosrv.c【服务端】:
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    #define ERR_EXIT(m) 
            do 
            { 
                    perror(m); 
                    exit(EXIT_FAILURE); 
            } while(0)
    
    int main(void)
    {
        int listenfd;
        if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    /*    if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
            ERR_EXIT("socket");
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5188);
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
        /*inet_aton("127.0.0.1", &servaddr.sin_addr);*/
    
        if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("bind");
        if (listen(listenfd, SOMAXCONN) < 0)
            ERR_EXIT("listen");
    
        struct sockaddr_in peeraddr;
        socklen_t peerlen = sizeof(peeraddr);
        int conn;
        if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
            ERR_EXIT("accept");
    
        char recvbuf[1024];//用来存储客户端发来的数据
        while (1)
        {
            memset(recvbuf, 0, sizeof(recvbuf));
            int ret = read(conn, recvbuf, sizeof(recvbuf));//从套接字中读取数据
            fputs(recvbuf, stdout);//打印到屏幕上
            write(conn, recvbuf, ret);//并且将其又回显给客户端,其第三个参数的长度正好是我们接收到的长度
        }
        close(conn);
        close(listenfd);
        
        return 0;
    }

    echocli.c【客户端】:

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    #define ERR_EXIT(m) 
            do 
            { 
                    perror(m); 
                    exit(EXIT_FAILURE); 
            } while(0)
    
    int main(void)
    {
        int sock;
        if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
            ERR_EXIT("socket");
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5188);
        servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
        if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("connect");
    
        char sendbuf[1024] = {0};
        char recvbuf[1024] ={0};
        while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
        {
            write(sock, sendbuf, strlen(sendbuf));
            read(sock, recvbuf, sizeof(recvbuf));
    
            fputs(recvbuf, stdout);
            memset(sendbuf, 0, sizeof(sendbuf));
            memset(recvbuf, 0, sizeof(recvbuf));
        }
    
        close(sock);
        
        return 0;
    }

    好了,今天的内容学到这,下回见~

  • 相关阅读:
    设计规范理解
    JVM读书笔记
    springboot整合RabbitMQ
    springboot 整合Redis
    tomcat原理
    配置嵌入式Servlet容器
    Springboot自动配置原理
    Springboot启动原理
    Springboot配置文件加载顺序
    修改VisualSVN Server地址为ip地址,修改svn服务端地址为ip或者域名地址的方法
  • 原文地址:https://www.cnblogs.com/webor2006/p/3923254.html
Copyright © 2011-2022 走看看