imx6ull/linux应用编程学习(11)SOCKET

07-08 1071阅读

从上篇文章了解到了网络基础知识,那么socket与tcp/ip、udp等有什么关系呢?

imx6ull/linux应用编程学习(10)网络基础知识(基于正点原子)-CSDN博客

        Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后

       Socket是计算机网络中的一个通信端点,可以视为网络通信的基础设施,它为不同的网络通信协议提供了统一的接口。通过它,程序可以发送和接收数据。Socket的主要作用是建立网络连接,并进行数据传输。

Socket的类型

  1. 流式套接字(Stream Socket):基于TCP(Transmission Control Protocol)协议的Socket,提供面向连接的、可靠的数据传输服务。数据在传输过程中不会丢失,并且接收方会以发送方相同的顺序接收数据。

  2. 数据报套接字(Datagram Socket):基于UDP(User Datagram Protocol)协议的Socket,提供无连接的、尽力而为的数据传输服务。数据可能会在传输过程中丢失,接收顺序也可能不同。

        由上可知,socket根据作为udp和tcp的接口,可发为以上两类,在进行更详细对比之前,我们先回顾一下tcp与udp的区别。

TCP/IP与UDP的关系

TCP/IP和UDP是两种不同的传输层协议:

  1. TCP/IP协议:

    • 面向连接:在发送数据之前需要建立连接(三次握手)。
    • 可靠传输:通过确认机制和重传机制确保数据的可靠传输。
    • 流控制:可以调整发送数据的速率,以避免网络拥塞。
    • 数据顺序:确保数据按照发送顺序接收。
  2. UDP协议:

    • 无连接:发送数据时不需要建立连接,传输效率高。
    • 不保证可靠性:没有数据确认机制,数据可能丢失或重复。
    • 无序传输:数据报的接收顺序可能与发送顺序不同。
    • 适合实时应用:如视频流、语音通话等对数据传输速度要求高,但对数据丢失不敏感的应用。

(tips:tcp和udp都依赖IP,ip就相当于每台主机的身份证,tcp和udp需要根据ip去辨别不同的主机)

Socket与TCP/IP、UDP的关系

Socket是应用层与传输层协议(如TCP、UDP)之间的桥梁。通过Socket编程,开发者可以选择使用TCP或UDP协议来实现具体的网络通信功能。

  • 使用TCP的Socket:

    • 服务器端:创建一个流式Socket -> 绑定端口 -> 监听连接请求 -> 接受连接 -> 读写数据。
    • 客户端:创建一个流式Socket -> 连接服务器 -> 读写数据。
  • 使用UDP的Socket:

    • 服务器端:创建一个数据报Socket -> 绑定端口 -> 接收和发送数据报。
    • 客户端:创建一个数据报Socket -> 发送和接收数据报。

    总结

            Socket是实现网络通信的基本单元,它提供了统一的接口,方便程序员使用TCP或UDP进行数据传输。通过选择不同类型的Socket,程序可以在可靠性、速度和资源利用之间进行权衡,以满足不同应用场景的需求。

    引用网上的一张图:

    imx6ull/linux应用编程学习(11)SOCKET

    socket常用API:

    1. 创建Socket
    • socket()
      • 原型:socket(socket_family, socket_type, protocol=0)
      • 描述:创建一个新的Socket。
      • 参数:
        • socket_family:指定地址族,常用的有AF_INET(IPv4)、AF_INET6(IPv6)。
        • socket_type:指定Socket类型,常用的有SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)。
        • protocol:一般设为0,表示使用默认协议。
      2. 绑定地址
      • bind()
        • 原型:bind(address)
        • 描述:将Socket绑定到指定的地址(IP地址和端口)。
        • 参数:
          • address:一个元组,包含IP地址和端口号,如('192.168.1.1', 8080)。
        3. 监听连接
        • listen()
          • 原型:listen(backlog)
          • 描述:使Socket进入监听状态,准备接受连接请求。
          • 参数:
            • backlog:指定等待连接的最大数量。
          4. 接受连接
          • accept()
            • 原型:accept()
            • 描述:接受一个连接,返回一个新的Socket对象和客户端地址。
            • 返回值:一个元组(conn, address),conn是新的Socket对象,address是客户端地址。
            5. 连接到远程服务器
            • connect()
              • 原型:connect(address)
              • 描述:连接到远程服务器。
              • 参数:
                • address:远程服务器的地址和端口号。
              6. 发送数据
              • send()

                • 原型:send(bytes)
                • 描述:通过Socket发送数据。
                • 参数:
                  • bytes:要发送的数据,类型为字节串。
              • sendto()

                • 原型:sendto(bytes, address)
                • 描述:通过Socket发送数据到指定地址(用于UDP)。
                • 参数:
                  • bytes:要发送的数据,类型为字节串。
                  • address:目标地址和端口号。
                7. 接收数据
                • recv()

                  • 原型:recv(bufsize)
                  • 描述:接收数据。
                  • 参数:
                    • bufsize:指定一次接收的最大数据量。
                • recvfrom()

                  • 原型:recvfrom(bufsize)
                  • 描述:接收数据并获取发送方地址(用于UDP)。
                  • 参数:
                    • bufsize:指定一次接收的最大数据量。
                  • 返回值:一个元组(data, address),data是接收到的数据,address是发送方地址。
                  8. 关闭Socket
                  • close()
                    • 原型:close()
                    • 描述:关闭Socket,释放资源。

                    socket 编程实战:

                    任务:实现一个简单地服务器和一个简单地客户端应用程序,将ubuntu和开发板分别作为客户端和服务端,实现交互

                    1.服务器程序:

                    编写服务器应用程序的流程如下:

                    ①、调用 socket()函数打开套接字,得到套接字描述符;

                    ②、调用 bind()函数将套接字与 IP 地址、端口号进行绑定;

                    ③、调用 listen()函数让服务器进程进入监听状态;

                    ④、调用 accept()函数获取客户端的连接请求并建立连接;

                    ⑤、调用 read/recv、 write/send 与客户端进行通信;

                    ⑥、调用 close()关闭套接字。

                    #include 
                    #include 
                    #include 
                    #include 
                    #include 
                    #include 
                    #include 
                    #include 
                    #define SERVER_PORT     8888    //端口号不能发生冲突,不常用的端口号通常大于5000
                    int main(void)
                    {
                        struct sockaddr_in server_addr = {0};
                        struct sockaddr_in client_addr = {0};
                        char ip_str[20] = {0};
                        int sockfd, connfd;
                        int addrlen = sizeof(client_addr);
                        char recvbuf[512];
                        int ret;
                        /* 打开套接字,得到套接字描述符 */
                        sockfd = socket(AF_INET, SOCK_STREAM, 0);
                        if (0 > sockfd) {
                            perror("socket error");
                            exit(EXIT_FAILURE);
                        }
                        /* 将套接字与指定端口号进行绑定 */
                        server_addr.sin_family = AF_INET;
                        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
                        server_addr.sin_port = htons(SERVER_PORT);
                        ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
                        if (0 > ret) {
                            perror("bind error");
                            close(sockfd);
                            exit(EXIT_FAILURE);
                        }
                        /* 使服务器进入监听状态 */
                        ret = listen(sockfd, 50);
                        if (0 > ret) {
                            perror("listen error");
                            close(sockfd);
                            exit(EXIT_FAILURE);
                        }
                        /* 阻塞等待客户端连接 */
                        connfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);
                        if (0 > connfd) {
                            perror("accept error");
                            close(sockfd);
                            exit(EXIT_FAILURE);
                        }
                        printf("有客户端接入...\n");
                        inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_str, sizeof(ip_str));
                        printf("客户端主机的IP地址: %s\n", ip_str);
                        printf("客户端进程的端口号: %d\n", client_addr.sin_port);
                        /* 接收客户端发送过来的数据 */
                        for ( ; ; ) {
                            // 接收缓冲区清零
                            memset(recvbuf, 0x0, sizeof(recvbuf));
                            // 读数据
                            ret = recv(connfd, recvbuf, sizeof(recvbuf), 0);
                            if(0 >= ret) {
                                perror("recv error");
                                close(connfd);
                                break;
                            }
                            // 将读取到的数据以字符串形式打印出来
                            printf("from client: %s\n", recvbuf);
                            // 如果读取到"exit"则关闭套接字退出程序
                            if (0 == strncmp("exit", recvbuf, 4)) {
                                printf("server exit...\n");
                                close(connfd);
                                break;
                            }
                        }
                        /* 关闭套接字 */
                        close(sockfd);
                        exit(EXIT_SUCCESS);
                    }

                            客户端连接到服务器之后,客户端会向服务器(也就是本程序)发送数据,在我们服务器应用程序中会读取客户端发送的数据并将其打印出来,

                            SERVER_PORT 宏指定了本服务器绑定的端口号,这里我们将端口号设置为 8888,端口不能与其它服务器的端口号发生冲突,不常用的端口号通常大于 5000。

                    2.编写客户端程序

                            客户端的功能是连接上小节所实现的服务器,连接成功之后向服务器发送数据,发送的数据由用户输入

                    #include 
                    #include 
                    #include 
                    #include 
                    #include 
                    #include 
                    #include 
                    #include 
                    #define SERVER_PORT		8888          	//服务器的端口号
                    #define SERVER_IP   	"192.168.5.9"	//服务器的IP地址
                    int main(void)
                    {
                        struct sockaddr_in server_addr = {0};
                        char buf[512];
                        int sockfd;
                        int ret;
                        /* 打开套接字,得到套接字描述符 */
                        sockfd = socket(AF_INET, SOCK_STREAM, 0);
                        if (0 > sockfd) {
                            perror("socket error");
                            exit(EXIT_FAILURE);
                        }
                        /* 调用connect连接远端服务器 */
                        server_addr.sin_family = AF_INET;
                        server_addr.sin_port = htons(SERVER_PORT);  //端口号
                        inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);//IP地址
                        ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
                        if (0 > ret) {
                            perror("connect error");
                            close(sockfd);
                            exit(EXIT_FAILURE);
                        }
                        printf("服务器连接成功...\n\n");
                        /* 向服务器发送数据 */
                        for ( ; ; ) {
                            // 清理缓冲区
                            memset(buf, 0x0, sizeof(buf));
                            // 接收用户输入的字符串数据
                            printf("Please enter a string: ");
                            fgets(buf, sizeof(buf), stdin);
                            // 将用户输入的数据发送给服务器
                            ret = send(sockfd, buf, strlen(buf), 0);
                            if(0 > ret){
                                perror("send error");
                                break;
                            }
                            //输入了"exit",退出循环
                            if(0 == strncmp(buf, "exit", 4))
                                break;
                        }
                        close(sockfd);
                        exit(EXIT_SUCCESS);
                    }
                    

                            SERVER_IP 和 SERVER_PORT 指的是服务器的 IP 地址和端口号,服务器的 IP 地址根据实际情况进行设置,服务器应用程序示例代码 30.4.1 中我们绑定的端口号为 8888,所以在客户端应用程序中我们也需要指定 SERVER_PORT 为 8888。

                    注意:

                    客户端和服务端要确定可以互ping,我ubuntu的ip是192.168.5.12,子网掩码是255.255.255.0,那么程序里面的ip就应该是192.168.5.x。我这里设为了192.168.5.9。

                    然后再开发板界面,设置临时ip:

                    ifconfig eth0 192.168.5.9

                    就可以发现开发板的IP已经变成了192.168.5.9。

                    上机测试:

                    将客户端和服务端的程序编译:

                    imx6ull/linux应用编程学习(11)SOCKET

                    将ubuntu作为客户端,开发板作为服务端,

                    注意:客户端的程序文件最后在ubuntu上运行,所以不能用交叉编译工具,而是用gcc指令,因为交叉编译工具编译完,就代表你这个文件需要在其他环境进行运行了。

                    利用scp指令将服务端文件传至开发板

                    imx6ull/linux应用编程学习(11)SOCKET

                    先开启服务器,在开发板执行服务端文件:

                    imx6ull/linux应用编程学习(11)SOCKET

                    ubuntu执行客户端:

                    imx6ull/linux应用编程学习(11)SOCKET

                    连接成功

                    客户端发送字符串给服务端,服务端可以解收,成功!

                    imx6ull/linux应用编程学习(11)SOCKET

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]