zoukankan      html  css  js  c++  java
  • Linux编程基础——Socket编程

    Linux下的Socket编程大体上包括Tcp Socket、Udp Socket即Raw Socket这三种,其中TCP和UDP方式的Socket编程用于编写应用层的socket程序,是我们用得比较多的,而Raw Socket则用得相对较少,不在本文介绍范围之列。

    TCP Socket

    基于TCP协议的客户端/服务器程序的一般流程一般如下:

    它基本上可以分为三个部分:

    一、建立连接:

    1. 服务器调用socket()bind()listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态
    2. 客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答
    3. 服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回。

    二、传输数据:

    建立连接后,TCP协议提供全双工的通信管道,服务器端和客户端根据协议可以通过read和write的反复调用实现数据的传输

    三、关闭连接:

    当数据传输已经完成后,服务器和客户端可以调用Close关闭连接,一端关闭连接后,另一端read函数则会返回0,可以根据这个特征来感应另一端的退出。

    下面就以一个简单的EchoServer演示一下如何创建服务器端和客户端代码,其中和socket相关api都会高亮显示。

    服务器端示例:

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        #include <unistd.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>

        #define MAXLINE 80
        #define SERV_PORT 8000

        int main(void)
        {
            char buf[MAXLINE];

            int listenfd = 0;
            listenfd =
    socket(AF_INET, SOCK_STREAM, 0);

            sockaddr_in servaddr = {0};
            servaddr.sin_family = AF_INET;
            servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
            servaddr.sin_port = htons(SERV_PORT);

            
    bind(listenfd, (sockaddr *)&servaddr, sizeof(servaddr));
            
    listen(listenfd, 20);

            printf("Accepting connections ...\n");
            while (1)
            {
                sockaddr_in cliaddr = {0};
                socklen_t cliaddr_len = sizeof(cliaddr);
                int connfd =
    accept(listenfd, (sockaddr *)&cliaddr, &cliaddr_len);
        
                char str[INET_ADDRSTRLEN];
                printf("connected from %s at PORT %d\n",
                        inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
                        ntohs(cliaddr.sin_port));

                while(true)
                {
                    int count =
    read(connfd, buf, MAXLINE);
                    if (count == 0)
                        break;

                    
    write(connfd, buf, count);
                }

                
    close(connfd);
                printf("closed from %s at PORT %d\n",
                        inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
                        ntohs(cliaddr.sin_port));
            }
        }

    PS:这里需要注意的一下的是sock函数的第二个参数SOCK_STREAM,它表示是一个TCP连接,后面我们会介绍通过传入SOCK_DGRAM打开udp连接。

    服务器端主体流程就是一个死循环,它接受一个socket连接,然后将其原封不动的返回给客户端,待客户端退出后,关闭socket连接,再次接受下一个socket连接。

    客户端代码如下:

        #include <stdio.h>
        #include <arpa/inet.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <sys/socket.h>
        #include <netinet/in.h>

        #define MAXLINE 80
        #define SERV_PORT 8000
        #define MESSAGE "hello world"

        int main(int argc, char *argv[])
        {
            char buf[MAXLINE];

            int sockfd =
    socket(AF_INET, SOCK_STREAM, 0);

            sockaddr_in servaddr = {0};
            servaddr.sin_family = AF_INET;
            inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
            servaddr.sin_port = htons(SERV_PORT);

            if (0 !=
    connect(sockfd, (sockaddr *)&servaddr, sizeof(servaddr)))
            {
                printf("connected failed");
                return 1;
            }

            
    write(sockfd, MESSAGE, sizeof(MESSAGE));
            int count =
    read(sockfd, buf, MAXLINE);

            printf("Response from server: %s\n",buf);

            
    close(sockfd);
            return 0;
        }

    客户端代码比较简单,这里就不多介绍了。

    UDP Socket

    典型的UDP客户端/服务器通讯过程如下图所示:

    由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,实际上有很多保证通讯可靠性的机制需要在应用层实现,可能反而会需要更多代码。

    典型的示例如下:

        /* server.cpp */
        #include <stdio.h>
        #include <string.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>

        #define MAXLINE 80
        #define SERV_PORT 8000

        int main(void)
        {
            char buf[MAXLINE];
            char str[INET_ADDRSTRLEN];

            int sockfd =
    socket(AF_INET, SOCK_DGRAM, 0);

            sockaddr_in servaddr = {0};
            servaddr.sin_family = AF_INET;
            servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
            servaddr.sin_port = htons(SERV_PORT);

            
    bind(sockfd, (sockaddr *)&servaddr, sizeof(servaddr));

            printf("Accepting connections ...\n");
            while (1)
            {
                sockaddr_in cliaddr;
                socklen_t cliaddr_len = sizeof(cliaddr);

                int count =
    recvfrom(sockfd, buf, MAXLINE, 0, (sockaddr *)&cliaddr, &cliaddr_len);
                if (count < 0)
                {
                    printf("recvfrom error");
                    continue;
                }

                printf("received from %s at PORT %d\n",
                     inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
                     ntohs(cliaddr.sin_port));

                
    sendto(sockfd, buf, count, 0, (sockaddr *)&cliaddr, sizeof(cliaddr));
            }
        }

        /* client.cpp */
        #include <stdio.h>
        #include <string.h>
        #include <unistd.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>

        #define MAXLINE 80
        #define SERV_PORT 8000

        int main(int argc, char *argv[])
        {
            char buf[MAXLINE];
            char str[INET_ADDRSTRLEN];
        
            int sockfd =
    socket(AF_INET, SOCK_DGRAM, 0);

            sockaddr_in servaddr = {0};
            servaddr.sin_family = AF_INET;
            inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
            servaddr.sin_port = htons(SERV_PORT);

            while (fgets(buf, MAXLINE, stdin) != NULL)
            {
                int count =
    sendto(sockfd, buf, strlen(buf), 0, (sockaddr *)&servaddr, sizeof(servaddr));
                if (count == -1)
                {
                    printf("sendto error");
                    return 0;
                }

                count =
    recvfrom(sockfd, buf, MAXLINE, 0, NULL, 0);
                if (count == -1)
                {
                    printf("recvfrom error");
                    return 0;
                }
        
                write(STDOUT_FILENO, buf, count);
            }

            close(sockfd);
            return 0;
        }

     

     

  • 相关阅读:
    HTML转PDF
    观察者模式分析
    异常解决方案记录
    复杂结构数据提交
    在Spring Controller中将数据缓存到session
    Eclipse
    redis数据类型-散列类型
    redis数据类型-字符串类型
    redis键操作
    redis性能优化
  • 原文地址:https://www.cnblogs.com/TianFang/p/2874388.html
Copyright © 2011-2022 走看看