TCP/IP网络编程(2)——基于UDP的服务端和客户端的简单实现

目录

前言

一、UDP通信关键函数

1. 发送数据 sendto

2. 接收数据 recvfrom

二、UDP编程中的注意点

1. bind函数的使用

2. UDP套接字存在数据边界

3. UDP客户端调用connect函数

三、 完整代码

1. 无连接版服务端

2. 无连接版客户端

3. 有连接版服务端

4. 有连接版客户端



前言

本文主要介绍编写UDP服务端和客户端的一些关键函数,相比于TCP,UDP无需实现流控制,所以两端传输数据前无需建立连接。端点仅仅需要一个套接字就能与其他各个端点通信,而TCP每一个连接都需要一对套接字。UDP的客户端和服务端并没有太大的差别,所以本文不做细分。


一、UDP通信关键函数

1. 发送数据 sendto

ssize_t sendto(int sock, void* buff, size_t nbytes, int flags, 
               struct sockaddr* to, socklen_t addrlen);

成功则返回传输的字节数,失败则返回-1。

(1)sock:套接字文件描述符

(2)buf:要传输的数据

(3)nbytes:传输的数据长度

(4)flags:可选项参数,一般为0

(5)to:保存目标地址的sockaddr结构体变量地址

(6)addrlen:to参数的结构体变量长度

关于各参数的含义可以参考我的上一篇文章,这里不再赘述。

调用sendto:

sendto(server_socket,reply,sizeof(reply),0,
            (struct sockaddr*)&client_addr,client_addr_len);

2. 接收数据 recvfrom

ssize_t recvfrom(int sock, void* buff, size_t nbytes, int flags, 
               struct sockaddr* from, socklen_t* addrlen);

成功则返回接收的字节数,失败则返回-1。

(1)sock:套接字文件描述符

(2)buf:接收数据的缓存地址

(3)nbytes:可接收的最大字节数

(4)flags:可选项参数,一般为0

(5)to:sockaddr结构体变量地址,用于保存发送方地址信息

(6)addrlen:用于保存from参数的结构体变量长度的地址

调用recvfrom:

recvfrom(server_socket,buf,sizeof(buf),0,
            (struct sockaddr*)&client_addr,&client_addr_len);

二、UDP编程中的注意点

1. bind函数的使用

套接字在使用之前必须先给其分配地址,bind函数的作用就是手动给套接字分配地址,那么什么时候需要调用bind函数呢?

sendto函数和TCP编程中的connect函数一样,在调用过程中会自动给套接字分配地址,在这种情况下就不需要bind手动分配了。但是如果在调用这两个函数之前使用套接字,就要用bind先分配地址,比如先recvfrom,再sendto的情况。

2. UDP套接字存在数据边界

在TCP中,即使发送方调用多次send函数,接收方也可以通过一次recv函数接收所有数据。但是在UDP中,sendto和recvfrom函数的调用次数必须相同,不然接收不到完整的数据。在UDP中,recvfrom端依然有接收缓冲区。

3. UDP客户端调用connect函数

sendto函数会自动为套接字分配地址,函数调用完毕后会自动删除注册的地址,如果向同一目标多次发数据的情况下,重复的分配与删除地址会浪费时间,所以在客户端可以使用connect函数分配好地址,这样sendto就只需传输数据。客户端还可以像TCP一样使用send、recv函数进行通信。

三、 完整代码

1. 无连接版服务端

#include<iostream>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<unistd.h>
using namespace std;

int main(int argc, char* argv[])
{
    int server_socket;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    char* ip = "127.0.0.1";
    char* port = "9955";
    char buf[100];
    char *reply = "ok";

    server_socket = socket(PF_INET,SOCK_DGRAM,0);

    memset(&server_addr,0,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(ip);
    server_addr.sin_port = htons(atoi(port));

    bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr));   //bind分配地址

    while(1)
    {
        recvfrom(server_socket,buf,sizeof(buf),0,
            (struct sockaddr*)&client_addr,&client_addr_len);
        if(!strcmp(buf,"q"))    //收到q关闭
        break;
        else
        cout << buf << endl;
        sendto(server_socket,reply,sizeof(reply),0,
            (struct sockaddr*)&client_addr,client_addr_len);
    }
    close(server_socket);
    return 0;
}

2. 无连接版客户端

#include<iostream>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<unistd.h>
using namespace std;

int main(int argc, char* argv[])
{
    int client_socket;
    struct sockaddr_in server_addr;    
    socklen_t server_addr_len = sizeof(server_addr), from_len;
    char* ip = "127.0.0.1";
    char* port = "9955";
    char buf[100];

    client_socket = socket(PF_INET,SOCK_DGRAM,0);

    memset(&server_addr,0,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(ip);
    server_addr.sin_port = htons(atoi(port));

    while(1)
    {
        cout << "input: ";
        cin >> buf;
        sendto(client_socket,buf,sizeof(buf),0,
            (struct sockaddr*)&server_addr,sizeof(server_addr));
        if(!strcmp(buf,"q"))    //输入q关闭
        break;
        else
        recvfrom(client_socket,buf,sizeof(buf),0,
            NULL,NULL);    
        cout << buf << endl;
    }
    close(client_socket);
    return 0;
}

3. 有连接版服务端

#include<iostream>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<unistd.h>
using namespace std;

int main(int argc, char* argv[])
{
    int server_socket;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    char* ip = "127.0.0.1";
    char* port = "9955";
    char buf[100];
    char *reply = "ok";

    server_socket = socket(PF_INET,SOCK_DGRAM,0);

    memset(&server_addr,0,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(ip);
    server_addr.sin_port = htons(atoi(port));

    bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr));  

    for(int i=1;i<=3;i++)
    {
        recvfrom(server_socket,buf,sizeof(buf),0,
            (struct sockaddr*)&client_addr,&client_addr_len);
        cout << "i: " << i << "   msg: " << buf << endl;
        sendto(server_socket,reply,sizeof(reply),0,
            (struct sockaddr*)&client_addr,client_addr_len);
    }
    close(server_socket);
    return 0;
}

4. 有连接版客户端

#include<iostream>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<unistd.h>
using namespace std;

int main(int argc, char* argv[])
{
    int client_socket;
    struct sockaddr_in server_addr, from;    //用from存储发送方的地址
    socklen_t server_addr_len = sizeof(server_addr), from_len;
    char* ip = "127.0.0.1";
    char* port = "9955";
    char* buf[] = {"hello","world","!"};
    char reply[30];

    client_socket = socket(PF_INET,SOCK_DGRAM,0);

    memset(&server_addr,0,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(ip);
    server_addr.sin_port = htons(atoi(port));

    connect(client_socket,(struct sockaddr*)&server_addr,server_addr_len);

    for(int i=1;i<=3;i++)
    {
        send(client_socket,buf[i-1],sizeof(buf[i-1]),0);   
        recv(client_socket,reply,sizeof(reply),0);    
        cout << reply << endl;
    }
    close(client_socket);
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值