第14章 UDP编程(1)_UDP客户端服务器模型

1. UDP编程模型

(1)UDP客户端服务器模型

  ①客户端可以不调用bind()而直接与服务器通讯。

  ②UDP是无连接的,因此服务端不需要调用accept和listen客户端也无需调用connect函数

(2)数据传输

  ①发送数据


头文件


#include <sys/socket.h>


函数


ssize_t send(int sockfd, const void* buf, size_t nbytes, int flag);

ssize_t send(int sockfd, const void* buf, size_t nbytes, int flag,

const struct sockaddr* destaddr, socklen_t destlen);

ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flag);


参数

struct msghdr{
    void* msg_name; //optional address
    socklen_t msg_namelen; //address size in bytes
    struct iovec* msg_iov;    //array of I/O buffers
    int msg_iovlen;              //number of elements in array
    void* msg_control;         //ancillary data
    socklen_t msg_controllen; //number of ancillary bytes;
    int msg_flags;                //flags for received message
};


功能


发送数据


返回值


返回:成功返回发送字节数,出错返回-1。

  ②接收数据


头文件


#include <sys/socket.h>


函数


ssize_t recv(int sockfd, const void* buf, size_t nbytes, int flag);

ssize_t recvfrom(int sockfd, const void* buf, size_t nbytes, int flag, const struct sockaddr* addr, socklen_t addrlen);

ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flag);


参数


与send*函数类似


功能


接收数据


返回值


返回消息的字节数,无消息返回0,出错返回-1。

【编程实验】利用UDP获取服务器的当前时间

//time_udp_server.c

#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <memory.h>

int sockfd;

void sig_handler(int signo)
{
    if(signo == SIGINT){
        printf("server close\n");
        close(sockfd);
        exit(1);
    }
}

//输出客户端的信息
void out_addr(struct sockaddr_in* addr)
{
    char ip[16];
    int port = 0;
    memset(ip, 0, sizeof(ip));
    inet_ntop(AF_INET, &addr->sin_addr.s_addr, ip, sizeof(ip));
    port = ntohs(addr->sin_port);

    printf("client: %s(%d)\n", ip, port);
}

//与客户端进行通信
void do_service()
{
    struct sockaddr_in cliAddr;
    socklen_t len = sizeof(cliAddr);
    char buff[1024];
    memset(buff, 0, sizeof(buff));

    //接受客户端的数据报文
    //使用recvfrom而不使用recv函数的主要目的是为了获取客户端信息
    if(recvfrom(sockfd, buff, sizeof(buff), 0,
                (struct sockaddr*)&cliAddr, &len) < 0){
        perror("recvfrom error");
    }else{
        out_addr(&cliAddr);
        printf("client send info: %s\n", buff);

        //向客户端发送数据报文
        long int t = time(0);
        char* ptr = ctime(&t);
        size_t size = strlen(ptr) * sizeof(char);
        if(sendto(sockfd, ptr, size, 0,(struct sockaddr*)&cliAddr, len) < 0){
            perror("sendto error"); //cliAddr己经从recvfrom那里得到了客户端的信息
        }
    }
}

int main(int argc, char* argv[])
{
    if(argc < 2){
        printf("usage: %s port\n", argv[0]);
        exit(1);
    }

    //注册ctrl-c信号处理函数
    if(signal(SIGINT, sig_handler) == SIG_ERR){
        perror("signal sigint error");
        exit(1);
    }

    /*步骤1: 创建socket*/
    sockfd = socket(AF_INET, SOCK_DGRAM, 0); //SOCK_DGRAM为UDP协议
    if(sockfd < 0){
        perror("socket error");
        exit(1);
    }

    //设置socket的相关选项
    int ret;
    int opt = 1;//1表示启动该选项
    //设置为可重新使用端口,每次启动该端口时,原来对该端口使用将失效
    if((ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) < 0){
        perror("setsockopt error");
        exit(1);
    }

    /*步骤2: 调用bind函数将socket和地址进行绑定*/
    struct sockaddr_in servAddr;
    memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = AF_INET;  //IPv4
    servAddr.sin_port = htons(atoi(argv[1])); //port
    servAddr.sin_addr.s_addr = INADDR_ANY;  //ip
    if(bind(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) < 0){
        perror("bind error");
        exit(1);
    }

    /*步骤3:与客户端进行双向的数据通信*/
    while(1){
        do_service();
    }

    return 0;
}
/* 输出结果
 * [[email protected] 14.udp]# gcc -o bin/time_udp_client src/time_udp_client.c
 * [[email protected] 14.udp]# bin/time_udp_server 8888
 * client: 127.0.0.1(48929)
 * client send info: hello world!
 * client: 127.0.0.1(32953)
 * client send info: hello world!
 * ^Cserver close
 * [[email protected] 14.udp]#
 */

//time_udp_client.c

#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

int main(int argc, char* argv[])
{
    if(argc < 3){
        printf("usage: %s ip port\n", argv[0]);
        exit(1);
    }

    /*步骤1:创建socket*/
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0){
        perror("socket error");
        exit(1);
    }

    /*步骤2: 调用recvfrom和sendto等函数和服务端双向通信*/
    struct sockaddr_in servAddr; //封装服务器的地址信息
    memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = AF_INET; //IPv4
    servAddr.sin_port = htons(atoi(argv[2]));//端口
    inet_pton(AF_INET, argv[1], &servAddr.sin_addr.s_addr);

    //在UDP能否调用connect来与服务端连接?
    //TCP中调用connect会经过三次握手,建立起双方的连接。
    //但UDP中调用connect并没有建立真正的连接,而是在内核中记录了通讯双方的地址信息(如IP、por)
    //当UDP中调用了connect后,以后可以直接使用send而不必使用sendto来发送消息给对方。
    //此外,还有一个好处就是因为sock记录了客户端的IP,使用该sockfd可以只接收指定服务器发来的消息,而不会
    //接收除服务器以外其他地方发来的消息。
    if(connect(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) < 0){
        perror("connect error");
        exit(1);
    }

    char buff[1024] = "hello world!";
    //向服务器发送数据报文
    if((sendto(sockfd, buff, sizeof(buff), 0, (struct sockaddr*)&servAddr, sizeof(servAddr))) < 0){
        perror("sendto error");
        exit(1);
    }else{
        //接受服务器端的数据报文
        memset(buff, 0, sizeof(buff));
        size_t size;

        //1.为什么recv里没有指定服务器地址,却可以发送成功?
        //因为如果之前的sendto发送成功,则sockfd(是个结构体)里将保存通讯双方的信息,这里就可以直接使用这个sockfd来通讯
        //2.为什么不需要判断recv的返回值为0?(0表示对方己关闭连接)
        //因为UDP是无连接的通信,通信双方是没有建立连接的,数据被传到链路层以后发送方就可以关闭,因此这里不需判断是否为0.
        if((size = recv(sockfd, buff, sizeof(buff), 0)) < 0){
            perror("recv error");
            exit(1);
        }else{
            printf("%s", buff);
        }
    }

    close(sockfd);

    return 0;
}
/*输出结果
 * [[email protected] 14.udp]# bin/time_udp_client 127.0.0.1 8888
 * Sat Mar 18 10:01:09 2017
 * [[email protected]alhost 14.udp]# bin/time_udp_client 127.0.0.1 8888
 * Sat Mar 18 10:01:12 2017
 * [[email protected] 14.udp]#
 */
时间: 2024-12-21 13:40:14

第14章 UDP编程(1)_UDP客户端服务器模型的相关文章

&lt;&lt;Python基础教程&gt;&gt;学习笔记 | 第14章 | 网络编程

Python是个很强大的网络编程工具,原因有二: 1. Python内有很多针对常见网络协议的库 2. Python在处理字节流方面的优势 本章主要内容: 探讨Python标准库中的一些网络模块,探讨SocketServer类,最后是Twisted框架. ------ 相关模块 Socket模块 基本组件,用于两个程序之间的信息通道.套接字包括两个: 服务器套接字和客户端套接字.创建一个服务器套接字后,让它等待连接,这样它就在某个网络地址处监听.客户端套接字负责:简单的连接,完成事务,断开连接.

第14章 网络编程

参考: https://blog.csdn.net/u014162133/article/details/46573873 原文地址:https://www.cnblogs.com/happykoukou/p/9336637.html

python 14章,网络编程

一,socket 模块 套接字包括两类:服务器,客户机, 创建一个套接字后让它等待连接 服务器: import socket s = socket.socket() port = 1234 host = socket.gethostname() print host s.bind((host,port)) s.listen(5) while True: get,addr = s.accept() print 'Got connection from',addr get.send('Thank y

Windows核心编程:第14章 探索虚拟内存

Github https://github.com/gongluck/Windows-Core-Program.git //第14章 探索虚拟内存.cpp: 定义应用程序的入口点. // #include "stdafx.h" #include "第14章 探索虚拟内存.h" int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lp

第三章 网络编程

终于学到网络编程了! 先上图和程序: 这是今天写的TCP的实现 服务器和客户端分别在两台电脑 这是服务器图: 这是服务器程序: 1 #-*- coding:utf-8 -*- 2 from socket import * #导入socket所有属性 3 from time import ctime #导入ctime() 4 5 6 host = '' #HOST 变量为空,表示bind()函数可以绑定在所有有效的地址上. 7 port = 21000 #设置端口 8 bufsize = 1024

第13章 网络编程

1 /***************** 2 ***第13章 网络编程 3 *******知识点: 4 **************1.基本概念 5 ******************1.1 网络OSI模型 6 ******************1.2 IP地址 7 ******************1.3 端口地址 8 ******************1.4 通讯协议 9 **************2.Java网络相关类 10 ******************2.1 URLDe

winsocket之TCP/UDP编程

一.概述: 本次练习的是TCP/UDP套接字编程,使用的是winsocket,对主要的库函数进行简绍,并实现了一个程序:实现服务器与客户端之间的通信,在服务器端实现记录用户名和密码,客服端可以实现用户名和密码的输入和查找,并且检查是否匹配.(参考  <<Visual C++网络编程>>) PS: 127.0.0.1是回路地址,用于在同一台机器上测试代码.端口号要大于1024. 二.基于TCP/UDP协议的套接字编程详解: 基于 TCP 的套接字编程的所有客户端和服务器端都是从调用s

TCP/UDP编程中的问题汇总

TCP/UDP编程中的问题汇总 TCP和UDP发送大文件的问题. 答: 发送端: 发送时,先发送文件的名称及大小等信息. 然后,设置一个缓冲区的大小,假设为4K. 再循环读4K的文件内容,并发送,直到发送完成. 最后,再发送完成标记. 接收端: 接收到第一个包时,得到文件的大小等信息. 计算出要接收多少个包. 然后,循环接收包,并将接收到的数据写入到文件中. 直到,接收到的数据长度等于文件的大小. struct package { 文件标识 //GUID 偏移量 //001- 数据段 //Byt

UDP编程中的connect

标准的udp客户端开了套接口后,一般使用sendto和recvfrom函数来发数据,最近看到ntpclient的代码里面是使用send函数直接法的,就分析了一下,原来udp发送数据有两种方法供大家选用的,顺便把udp的connect用法也就解释清楚了.方法一: socket----->sendto()或recvfrom() 方法二: socket----->connect()----->send()或recv() 首先从这里看出udp中也是可以使用connect的,但是这两种方法到底有什