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;
    }
  • 相关阅读:
    【[SDOI2016]排列计数】
    newcoder NOIP提高组模拟赛C题——保护
    【[JLOI2014]松鼠的新家】
    【[USACO08NOV]奶牛混合起来Mixed Up Cows】
    【[USACO13NOV]没有找零No Change】
    【[SHOI2009]会场预约】
    【[USACO12FEB]附近的牛Nearby Cows】
    UVA11987 【Almost Union-Find】
    基于递归的BFS(Level-order)
    遍历二叉树
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8493809.html
Copyright © 2011-2022 走看看