zoukankan      html  css  js  c++  java
  • 网络编程学习笔记:linux下的socket编程

    socket是进程通信的一种方式,通过调用一些API可以实现进程间通信,建立连接以及收发信息的过程如下图所示:

    这些函数的用法如下:

    1、int socket(int protocolFamily, int type, int protocol); 返回描述符sockfd

    • l  protocolFamily:协议族,AF_INET(IPV4)、AF_INET6(IPV6)、AF_LOCAL(或称AF_UNIX,unix域socket)、AF_ROUTE等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了用IPV4地址(32位)与端口号(16位),AF_UNIX决定了要用一个绝对路径名作为地址
    • l  type:指定socket类型。常用的类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等
    • l  protocol:协议名

    调用socket创建之后,返回的描述符存在于协议族空间中,但没有一个具体的地址,必须要通过bind才行

    2、int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

    • l  sockfd:socket描述字,socket的返回值
    • l  addr:一个const struct *addr指针,指向要绑定的sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同

    如, IPV4:

    struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
    };
    
    struct in_addr{
      uint32_t s_addr; // address in network byte order
    };
    • l  addrlen:对应的地址的长度

    3、int listen(int sockfd, int backlog);服务器监听函数

    • l  sockfd:socket描述字,socket的返回值
    • l  backlog: socket 可以排队的最大连接个数

      listen函数将socket变为被动类型的, 等待客户连接请求

    4、int connect(int sockfd, const struct *addr, socklen_t addrlen);

    • l  sockfd:socket描述字,socket的返回值
    • l  addr:服务器的socket地址
    • l  addrlen:socket地址的长度

    5、int accept(int sockfd, struct sockaddr* addr, socklen_t *addrlen);

    • l  sockfd:socket描述字,socket的返回值
    • l  addr:结果参数,接收一个返回值指向客户端的地址,如果对客户的地址不在乎,可以设置为NULL
    • l  addrlen:结果参数,接收上述addr结构大小,指明addr结构所占字节数,也可NULL

      accept()成功返回一个SOCKET描述符,表示接收到的套接字的描述符。否则返回值INVALID_SOCKET。

    6、ssize_t send(int sockfd, const void *buf, ssize_t len, int flags);

    • l  sockfd: socket描述字,socket的返回值
    • l  buf:要发送的数据buffer
    • l  len:要传送的数据大小
    • l  flag:一般取值为0,影响TCP首部的可选部分

      send将自己的数据copy到内核的send buffer,返回值为[-1,size]:

      • 返回-1说明发送数据失败,系统内部出问题了
      • 返回[0,size]:由于send是从内核的send buffer中写数据,那么send buffer中剩下的长度为m,返回min(m, size),返回0就是send buffer没有空间了

    7、ssize_t recv(int sockfd, void *buf, ssize_t len, int flags);

    各参数意义基本与send相同,recv操作将内核中的数据拷贝到应用程序内部

    返回值是[-1,size]:

      • 返回-1表明接收失败,socket失效、recv操作由于系统内部原因中断等原因
      • 返回0表明没有数据,在TCP中接收发送有一个timeout,当timeout的时候还没有数据返回0
      • 返回[1,size]:recv的操作时从内核拷贝数据,数据有多少拷贝多少,内核现在收到长度为m的数据,返回min(m, size);

    8、int close(int fd);

    一个echo小例子,客户端向服务器发送什么服务器就给客户端返回什么信息:

    1、服务器代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<errno.h>
    #include<sys/types.h>   // 基本系统数据类型,是系统的基本系统数据类型的头文件
    #include<sys/socket.h>  
    #include<netinet/in.h>  // 互联网地址族
    #include<arpa/inet.h>   //IP地址转换函数inet_pton
    #include<unistd.h>      //close
    
    #include<iostream>
    
    int main(int argc, char** argv){
        int socketfd, bindfd, connectfd;
        char buffer[4096];
        struct sockaddr_in serverAddress;
        int sendSize, recvSize;
    
        //printf("================== create socket ======================
    ");
        // create socket
        socketfd = socket(AF_INET, SOCK_STREAM, 0);
        if(socketfd == -1){
            printf("Create socket error:%s(errno:%d)
    ", strerror(errno),errno);
            exit(0);
        }
    
        //printf("================== set address ========================
    ");
        //set server address
        memset(&serverAddress, 0, sizeof(serverAddress));
        serverAddress.sin_family = AF_INET;
        serverAddress.sin_port = htons(800);
        //serverAddress.sin_addr.s_addr = htonl(127.0.0.1);
        inet_pton(AF_INET, "127.0.0.1", &serverAddress.sin_addr);
    
        std::cout << serverAddress.sin_addr.s_addr << '
    ';
    
        //printf("================== bind address ========================
    ");
        // bind address to the socket
        bindfd = bind(socketfd, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
        if(bindfd == -1){
            printf("bind socket error:%s(errno:%d)
    ", strerror(errno),errno);
            exit(0);        
        }
    
        //printf("================== listen ========================
    ");
        if(listen(socketfd, 10) == -1){
            printf("listen socket error:%s(errno:%d)
    ",strerror(errno),errno);
            exit(0);
        }
    
        printf("================== waiting connect ========================
    ");
        while(1){
            sleep(2);
            // recvive a connect and accept
            connectfd = accept(socketfd, (struct sockaddr*)NULL, NULL);
            if(connectfd == -1){
                printf("connet socket error:%s(errno:%d)
    ",strerror(errno),errno);
                continue;        
            }
    
            // receive data
            recvSize = recv(connectfd, buffer, 4096, 0);
            if(recvSize == -1){
                printf("recvive data error:%s(errno:%d)
    ",strerror(errno),errno);
                continue;            
            }
            
            printf("%s
    ", buffer);
    
            // send data
            sendSize = send(connectfd, buffer, 4096, 0);
            if(sendSize == -1){
                printf("send data error:%s(errno:%d)
    ",strerror(errno),errno);
                continue;            
            }
            close(connectfd);
    
        }
        close(socketfd);
    
    
    }

    2、客户端代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<errno.h>
    #include<sys/types.h>   // 基本系统数据类型,是系统的基本系统数据类型的头文件
    #include<sys/socket.h>  
    #include<netinet/in.h>  // 互联网地址族
    #include<arpa/inet.h>   //IP地址转换函数inet_pton
    #include<unistd.h>      //close
    
    #include<iostream>
    
    int main(){
        int socketfd, connectfd;
        int sendSize, recvSize;
    
        struct sockaddr_in serverAddress;
    
        char sendBuf[4096];
        char recvBuf[4096];
    
        printf("================== create socket ========================
    ");
        socketfd = socket(AF_INET, SOCK_STREAM, 0);
        if(socketfd == -1){
            printf("create socket error:%s(error%d)
    ", strerror(errno),errno);
            exit(0);
        }
    
        memset(&serverAddress, 0, sizeof(serverAddress));
        serverAddress.sin_family = AF_INET;
        serverAddress.sin_port = htons(800);
        //serverAddress.sin_addr.s_addr = htonl("127.0.0.1");    
        inet_pton(AF_INET, "127.0.0.1", &serverAddress.sin_addr);
    
        std::cout << serverAddress.sin_addr.s_addr << '
    ';
    
        connectfd = connect(socketfd, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
        if(connectfd == -1){
            printf("connect server error:%s(error(%d))
    ", strerror(errno), errno);
            exit(0);
        }
    
        printf("send message to server:
    ");
        //std::cin >> sendBuf;
        fgets(sendBuf, 4096, stdin);
        //printf("%s
    ", sendBuf);
        sendSize = send(socketfd, sendBuf, strlen(sendBuf), 0);
        if(sendSize == -1){
            printf("send data error:%s(error%d)
    ", strerror(errno), errno);
            exit(0);
        }
    
        printf("wait echo from server:
    ");
    
        sleep(2);
        recvSize = recv(socketfd, recvBuf, sizeof(recvBuf), 0);
        if(recvSize < 0){
            printf("recvive echo error:%s(error%d)
    ", strerror(errno), errno);
            exit(0);
        }
        printf("%s", recvBuf);
    
        close(socketfd);
    
    }

    在这过程中遇到的几个问题:

    1、(客户端)errno 111:connection refused

      这个问题说明客户端没有找到应该连接的端口,需要做到:

    • 确保服务端在相应的端口监听;
    • 关闭防火墙(ubuntu下面的命令:sudo ufw disable);
    • 而且server端要 sudo 运行;

      由于我客户端和服务器端口号不匹配,就出现了这个问题

    2、(服务器)errno 14:bad address

      accept()函数的第二个参数指的是一个接收返回结果的缓存区,表示的是本次连接的客户端的地址,如果无所谓客户端地址可以写为NULL,而一旦给这个参数赋了非NULL得值,但是这个缓存区又不可写的话,就会出现bad address报错

    3、(服务器)errno 107: transport endpoint is not connected.

      这个问题,是由于我服务器端的recv和send函数的第一个参数写的本地socket描述符造成的,事实上,这应该是已连接的socket描述符

  • 相关阅读:
    UVALive 7141 BombX
    CodeForces 722D Generating Sets
    CodeForces 722C Destroying Array
    CodeForces 721D Maxim and Array
    CodeForces 721C Journey
    CodeForces 415D Mashmokh and ACM
    CodeForces 718C Sasha and Array
    CodeForces 635C XOR Equation
    CodeForces 631D Messenger
    田忌赛马问题
  • 原文地址:https://www.cnblogs.com/Chilly2015/p/5790787.html
Copyright © 2011-2022 走看看