Linux下 面向连接的 UDP通信

UDP Server程序

1、编写UDP Server程序的步骤
 (1)使用socket()来建立一个UDP socket,第二个参数为SOCK_DGRAM。  
 (2)初始化sockaddr_in结构的变量,并赋值

 这里使用“2030”作为服务程序的端口,使用“INADDR_ANY”作为绑定的IP地址即任何主机上的地址。  
 (3)使用bind()把上面的socket和定义的IP地址和端口绑定。这里检查bind()是否执行成功,如果有错误就退出。这样可以防止服务程序重复运行的问题。  
 (4)进入无限循环程序,使用recvfrom()进入等待状态,直到接收到客户程序发送的数据,就处理收到的数据,并向客户程序发送反馈。这里是直接把收到的数据发回给客户程序。
2、srv.cpp程序内容

#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>

int main()
{
    int srvSock=socket(AF_INET, SOCK_DGRAM, 0);
    
    if(srvSock==-1)
    {
        perror("创建套接字出错\n");
        return 1;
    }
    struct sockaddr_in srvAddr, clientAddr;
    srvAddr.sin_family=AF_INET;
    srvAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    srvAddr.sin_port=htons(2030);

    if(bind(srvSock, (struct sockaddr *)&srvAddr, sizeof(srvAddr))==-1)
    {
        perror("绑定失败\n");
        return 1;
    }

    socklen_t len=sizeof(srvAddr);
    while(1)
    {
        char recvMsg[128]={0};
        char sendMsg[128]={0};
        memcpy(sendMsg, "hello, client", 128);
        recvfrom(srvSock, recvMsg, 128, 0, (struct sockaddr *)&clientAddr, &len);
        printf("接收到数据%s\n", recvMsg);
        sendto(srvSock, sendMsg, 128, 0, (struct sockaddr *)&clientAddr, len);
        printf("发送数据%s\n", sendMsg);
    }
    return 0;
}

UDP Client程序   

1、编写UDP Client程序的步骤   

(1)初始化sockaddr_in结构的变量,并赋值。

这里使用“2030”作为连接的服务程序的端口,从命令行参数读取IP地址,并且判断IP地址是否符合要求。   

(2)使用socket()来建立一个UDP socket,第二个参数为SOCK_DGRAM。  

(3)使用connect()来建立与服务程序的连接

与TCP协议不同,UDP的connect()并没有与服务程序三次握手。上面说了UDP是非连接的,实际上也可以是连接的。使用连接的UDP,kernel可以直接返回错误信息给用户程序,从而避免由于没有接收到数据而导致调用recvfrom()一直等待下去,看上去好像客户程序没有反应一样。

(4)向服务程序发送数据,因为使用连接的UDP,所以使用write()来替代sendto()。这里的数据直接从标准输入读取用户输入。

(5)接收服务程序发回的数据,同样使用read()来替代recvfrom()。  

(6)处理接收到的数据,这里是直接输出到标准输出上。  

注意:

1. 在linux操作系统中,套接字可视为文件,在调用connect()函数之后就可以调用read()和write()直接在其上进行接收和发送。

2. 当用recvfrom()函数接收的时候并没有错误提示,而当用read()函数读取的时候有错误提示。原因尚未知。

 client.cpp程序内容:  
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>

int main()
{
    int clientSock=socket(AF_INET, SOCK_DGRAM, 0);
    
    if(clientSock==-1)
    {
        perror("建立套接字出错\n");
        return 1;
    }
    
    struct sockaddr_in destAddr;
    destAddr.sin_family=AF_INET;
    destAddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    destAddr.sin_port=htons(2030);
    
    socklen_t len=sizeof(destAddr);
    
    connect(clientSock, (struct sockaddr *)&destAddr, len);
    
    /*即便链接出错,也不会返回任何信息
    if(connect(clientSock,(struct sockaddr *)&destAddr, len)==-1)
    {
        perror("与服务器连接失败\n");
        return 1;
    }
    */

    char sendMsg[128]={0};
    memcpy(sendMsg, "hello, server", 128);
    char recvMsg[128]={0};
 
    /* 用sendto()和recvfrom()没有链接错误提示
    sendto(clientSock, sendMsg, 128, 0, (struct sockaddr *)&destAddr, len);

    while(1)
    {
        char recvMsg[128]={0};
        recvfrom(clientSock, recvMsg, 128, 0, (struct sockaddr *)&destAddr, &len);
        printf("接收到数据%s\n", recvMsg);
        break;
    }
    */
   
    write(clientSock, sendMsg, 128);
    while(1)
     {      
         int n=read(clientSock, recvMsg, 128);
         if(n==-1)
         {
             perror("read error");
             return 1;
         }
         recvMsg[n]=0;
         printf("接收到数据%s\n", recvMsg);
         break;
     } 
}
运行例子程序

1、编译例子程序   
使用如下命令来编译例子程序:   

gcc -Wall -o srv srv.cpp   

gcc -Wall -o client client.cpp   

编译完成生成了udpserv和udpclient两个可执行程序。   

2、运行UDP Server程序
执行./srv &命令来启动服务程序。我们可以使用netstat -ln命令来观察服务程序绑定的IP地址和端口,部分输出信息如下:  
 Active Internet connections (only servers)  

   Proto Recv-Q Send-Q Local Address Foreign Address State  

   udp        0      0 0.0.0.0:2030            0.0.0.0:*

可以看到udp处有“0.0.0.0:2030”的内容,说明服务程序已经正常运行,可以接收主机上任何IP地址且端口为2030的数据。  
如果这时再执行./srv &命令,就会看到如下信息:   

绑定失败:

Address already in use  

说明已经有一个服务程序在运行了。

出现这种情况可以用命令kill结束正在运行的程序,并重新打开。

3、运行UDP Client程序
执行./client 命令来启动客户程序,使用127.0.0.1来连接服务程序,执行效果如下:

接收到数据hello, client 

输入的数据都正确从服务程序返回了,按ctrl+c可以退出程序。  

如果服务程序没有启动,而执行客户程序,就会看到如下信息:

read error: Connection refused  

说明指定的IP地址和端口没有服务程序绑定,客户程序就退出了。
这就是使用connect()的好处,注意,这里错误信息是在向服务程序发送数据后收到的,而不是在调用connect()时
如果使用tcpdump程序来抓包,会发现收到的是ICMP的错误信息。

你可能感兴趣的:(linux,server,struct,socket,gcc,internet)