zoukankan      html  css  js  c++  java
  • Linux 下socket通信终极指南(附TCP、UDP完整代码)

    linux下用socket通信,有TCP、UDP两种协议,网上的很多教程把两个混在了一起,或者只讲其中一种。现在我把自己这两天研究的成果汇总下来,写了一个完整的,适合初学者参考,也方便自己以后查阅。

    首先讲什么是socket,不喜欢理论的可以略过。

    Berkeley套接字应用程序接口(API)包括了一个用C语言写成的应用程序开发库,主要用于实现进程间通讯,在计算机网络通讯方面被广泛使用。(来自 wikipedia socket )

    下面介绍一下常用的socket API(也来自 wikipedia socket

    这个列表是一个Berkeley套接字API库提供的函数或者方法的概要:

    • socket() 创建一个新的确定类型的套接字,类型用一个整型数值标识,并为它分配系统资源。
    • bind() 一般用于服务器端,将一个套接字与一个套接字地址结构相关联,比如,一个指定的本地端口和IP地址。
    • listen() 用于服务器端,使一个绑定的TCP套接字进入监听状态。
    • connect() 用于客户端,为一个套接字分配一个自由的本地端口号。 如果是TCP套接字的话,它会试图获得一个新的TCP连接。
    • accept() 用于服务器端。 它接受一个从远端客户端发出的创建一个新的TCP连接的接入请求,创建一个新的套接字,与该连接相应的套接字地址相关联。
    • send()recv(),或者write()read(),或者recvfrom()sendto(), 用于往/从远程套接字发送和接受数据。
    • close() 用于系统释放分配给一个套接字的资源。 如果是TCP,连接会被中断。
    • gethostbyname()gethostbyaddr() 用于解析主机名和地址。
    • select() 用于修整有如下情况的套接字列表: 准备读,准备写或者是有错误。
    • poll() 用于检查套接字的状态。 套接字可以被测试,看是否可以写入、读取或是有错误。
    • getsockopt() 用于查询指定的套接字一个特定的套接字选项的当前值。
    • setsockopt() 用于为指定的套接字设定一个特定的套接字选项。

    更多的细节如下给出。

    [编辑]socket()

    socket() 为通讯创建一个端点,为套接字返回一个文件描述符。 socket() 有三个参数:

    • domain 为创建的套接字指定协议集。 例如:
      • PF_INET 表示IPv4网络协议
      • PF_INET6 表示IPv6
      • PF_UNIX 表示本地套接字(使用一个文件)
    • type 如下:
      • SOCK_STREAM (可靠的面向流服务或流套接字)
      • SOCK_DGRAM (数据报文服务或者数据报文套接字)
      • SOCK_SEQPACKET (可靠的连续数据包服务)
      • SOCK_RAW (在网络层之上的原始协议)。
    • protocol 指定实际使用的传输协议。 最常见的就是IPPROTO_TCPIPPROTO_SCTPIPPROTO_UDPIPPROTO_DCCP。这些协议都在<netinet/in.h>中有详细说明。 如果该项为“0”的话,即根据选定的domain和type选择使用缺省协议。

    如果发生错误,函数返回值为-1。 否则,函数会返回一个代表新分配的描述符的整数。

    原型:
    int socket(int domain, int type, int protocol)。
    

    [编辑]bind()

    bind() 为一个套接字分配地址。当使用socket()创建套接字后,只赋予其所使用的协议,并未分配地址。在接受其它主机的连接前,必须先调用bind()为套接字分配一个地址。bind()有三个参数:

    • sockfd, 表示使用bind函数的套接字描述符
    • my_addr, 指向sockaddr结构(用于表示所分配地址)的指针
    • addrlen, 用socklen_t字段指定了sockaddr结构的长度

    如果发生错误,函数返回值为-1,否则为0。

    原型
    int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
    

    [编辑]listen()

    当socket和一个地址绑定之后,listen()函数会开始监听可能的连接请求。然而,这只能在有可靠数据流保证的时候使用,例如:数据类型(SOCK_STREAM,SOCK_SEQPACKET)。

    listen()函数需要两个参数:

    • sockfd, 一个socket的描述符.
    • backlog, 一个决定监听队列大小的整数,当有一个连接请求到来,就会进入此监听队列,当队列满后,新的连接请求会返回错误。当请求被接受,返回 0。反之,错误返回 -1。

    原型:

    int listen(int sockfd, int backlog);
    

    [编辑]accept()

    当应用程序监听来自其他主机的面对数据流的连接时,通过事件(比如Unix select()系统调用)通知它。必须用 accept()函数初始化连接。 Accept() 为每个连接创立新的套接字并从监听队列中移除这个连接。它使用如下参数:

    • sockfd,监听的套接字描述符
    • cliaddr, 指向sockaddr 结构体的指针,客户机地址信息。
    • addrlen,指向 socklen_t的指针,确定客户机地址结构体的大小 。

    返回新的套接字描述符,出错返回-1。进一步的通信必须通过这个套接字。

    Datagram 套接字不要求用accept()处理,因为接收方可能用监听套接字立即处理这个请求。

    函数原型:
    int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
    

    [编辑]connect()

    connect()系统调用为一个套接字设置连接,参数有文件描述符和主机地址。

    某些类型的套接字是无连接的,大多数是UDP协议。对于这些套接字,连接时这样的:默认发送和接收数据的主机由给定的地址确定,可以使用 send()和 recv()。 返回-1表示出错,0表示成功。

    函数原型:
    int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);

    TCP socket通信

    服务器端流程如下:

    1.创建serverSocket

    2.初始化 serverAddr(服务器地址)

    3.将socket和serverAddr 绑定 bind

    4.开始监听 listen

    5.进入while循环,不断的accept接入的客户端socket,进行读写操作write和read

    6.关闭serverSocket

    客户端流程:

    1.创建clientSocket

    2.初始化 serverAddr

    3.链接到服务器 connect

    4.利用write和read 进行读写操作

    5.关闭clientSocket

    具体实现代码如下

    #server.c(TCP)
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <errno.h>
    #define SRVPORT 10005
    #define CONNECT_NUM 5
    #define MAX_NUM 80
    int main()
    {
        int serverSock=-1,clientSock=-1;
        struct sockaddr_in serverAddr;
    
        serverSock=socket(AF_INET,SOCK_STREAM,0);
        if(serverSock<0)
        {
            printf("socket creation failed
    ");
            exit(-1);
        }
        printf("socket create successfully.
    ");
    
        memset(&serverAddr,0,sizeof(serverAddr));
        serverAddr.sin_family=AF_INET;
        serverAddr.sin_port=htons((u_short) SRVPORT);
        serverAddr.sin_addr.s_addr=htons(INADDR_ANY);
        if(bind(serverSock,&serverAddr,sizeof(struct sockaddr_in))==-1)
        {
            printf("Bind error.
    ");
            exit(-1);
        }
        printf("Bind successful.
    ");
    
        if(listen(serverSock,10)==-1)
        {
            printf("Listen error!
    ");
        }
        printf("Start to listen!
    ");
    
        char revBuf[MAX_NUM]={0};
        char sedBuf[MAX_NUM]={0};
        while(1)
        {
            clientSock=accept(serverSock,NULL,NULL);
            while(1)
            {
                if(read(clientSock,revBuf,MAX_NUM)==-1)
                {
                    printf("read error.
    ");
                }
                else
                {
                    printf("Client:%s
    ",revBuf);
                }
                if(strcmp(revBuf,"Quit")==0||strcmp(revBuf,"quit")==0)
                {
                    strcpy(sedBuf,"Goodbye,my dear client!");
                }
                else
                {
                    strcpy(sedBuf,"Hello Client.");
                }
                if(write(clientSock,sedBuf,sizeof(sedBuf))==-1)
                {
                    printf("Send error!
    ");
                }
                printf("Me(Server):%s
    ",sedBuf);
                if(strcmp(revBuf,"Quit")==0||strcmp(revBuf,"quit")==0)
                {
                    break;
                }
                bzero(revBuf,sizeof(revBuf));
                bzero(sedBuf,sizeof(sedBuf));
            }
            close(clientSock);
        }
        close(serverSock);
        return 0;
    }
    
    #client.c(TCP)
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <errno.h>
    #include <string.h>
    #define SRVPORT 10005
    #define CONNECT_NUM 5
    #define MAX_NUM 80
    int main()
    {
        int clientSock=-1;
        struct sockaddr_in serverAddr;
    
        clientSock=socket(AF_INET,SOCK_STREAM,0);
        if(clientSock<0)
        {
            printf("socket creation failed
    ");
            exit(-1);
        }
        printf("socket create successfully.
    ");
    
        memset(&serverAddr,0,sizeof(serverAddr));
        serverAddr.sin_family=AF_INET;
        serverAddr.sin_port=htons((u_short) SRVPORT);
        serverAddr.sin_addr.s_addr=htons(INADDR_ANY);
        if(connect(clientSock,&serverAddr,sizeof(struct sockaddr_in))<0)
        {
            printf("Connect error.
    ");
            exit(-1);
        }
        printf("Connect successful.
    ");
    
        char sedBuf[MAX_NUM]={0};
        char revBuf[MAX_NUM]={0};
        while(gets(sedBuf)!=-1)
        {
            if(write(clientSock,sedBuf,strlen(sedBuf))==-1)
            {
                printf("send error!
    ");
            }
            printf("Me(Client):%s
    ",sedBuf);
            bzero(sedBuf,sizeof(sedBuf));
            if(read(clientSock,revBuf,MAX_NUM)==-1)
            {
                printf("rev error!
    ");
            }
            printf("Sever:%s
    ",revBuf);
            if(strcmp(revBuf,"Goodbye,my dear client!")==0)
                break;
            bzero(revBuf,sizeof(revBuf));
        }
        close(clientSock);
        return 0;
    }
    


    UDP协议不能保证数据通信的可靠性,但是开销更低,编起来也更加简单

    服务器流程:

    1.创建serverSocket

    2.设置服务器地址 serverAddr

    3.将serverSocket和serverAddr绑定 bind

    4.开始进行读写 sendto和recvfrom

    5.关闭serverSocket

    客户端流程

    1.创建clientSocket

    2.设置服务器地址 serverAddr

    3.可选 设置clientAddr并和clientSocket(一般不用绑定)

    4.进行发送操作 sendto

    5.关闭clientSocket

    具体代码如下:

    #server.c(UDP)
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <netinet/in.h>//for sockaddr_in
    #include <arpa/inet.h>//for socket
    int main()
    {
     int fd=socket(AF_INET,SOCK_DGRAM,0);
     if(fd==-1)
     {
     perror("socket create error!
    ");
     exit(-1);
     }
     printf("socket fd=%d
    ",fd);
    
    
     struct sockaddr_in addr;
     addr.sin_family=AF_INET;
     addr.sin_port=htons(6666);
     addr.sin_addr.s_addr=inet_addr("127.0.0.1");
    
    
     int r;
     r=bind(fd,(struct sockaddr*)&addr,sizeof(addr));
     if(r==-1)
     {
     printf("Bind error!
    ");
     close(fd);
     exit(-1);
     }
     printf("Bind successfully.
    ");
     
     char buf[255];
     struct sockaddr_in from;
     socklen_t len;
     len=sizeof(from);
     while(1)
     {
     r=recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr*)&from,&len);
     if(r>0)
     {
     buf[r]=0;
     printf("The message received for %s is :%s
    ",inet_ntoa(from.sin_addr),buf);
     }
     else
     {
     break;
     }
     }
     close(fd);
     return 0;
    }
    #client.c(UDP)
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <netinet/in.h>//for sockaddr_in
    #include <arpa/inet.h>//for socket 
    int main()
    {
     int fd=socket(AF_INET,SOCK_DGRAM,0);
     if(fd==-1)
     {
     perror("socket create error!
    ");
     exit(-1);
     }
     printf("socket fd=%d
    ",fd);
    
    
     struct sockaddr_in addr_to;//目标服务器地址
     addr_to.sin_family=AF_INET;
     addr_to.sin_port=htons(6666);
     addr_to.sin_addr.s_addr=inet_addr("127.0.0.1");
     
     struct sockaddr_in addr_from;
     addr_from.sin_family=AF_INET;
     addr_from.sin_port=htons(0);//获得任意空闲端口
     addr_from.sin_addr.s_addr=htons(INADDR_ANY);//获得本机地址
     r=bind(fd,(struct sockaddr*)&addr_from,sizeof(addr_from));
    
    int r; 
     if(r==-1)
     {
     printf("Bind error!
    ");
     close(fd);
     exit(-1);
     }
     printf("Bind successfully.
    ");
    
    
     char buf[255];
     int len;
     while(1)
     {
     r=read(0,buf,sizeof(buf)); 
     if(r<0)
     {
     break;
     }
     len=sendto(fd,buf,r,0,(struct sockaddr*)&addr_to,sizeof(addr_to)); 
     if(len==-1)
     {
     printf("send falure!
    ");
     }
     else
     {
     printf("%d bytes have been sended successfully!
    ",len);
     }
     }
     close(fd);
     return 0;
    }
  • 相关阅读:
    Truck History(poj 1789)
    Highways poj 2485
    117. Populating Next Right Pointers in Each Node II
    116. Populating Next Right Pointers in Each Node
    115. Distinct Subsequences
    114. Flatten Binary Tree to Linked List
    113. Path Sum II
    109. Convert Sorted List to Binary Search Tree
    106. Construct Binary Tree from Inorder and Postorder Traversal
    105. Construct Binary Tree from Preorder and Inorder Traversal
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8493809.html
Copyright © 2011-2022 走看看