百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

网络编程-2、TCP&UDP编程 udp网络编程的一般步骤

yuyutoo 2024-12-22 21:48 6 浏览 0 评论

1、UDP编程

1.1、UDP编程-创建套接字

#include <sys/socket.h>
int socket(int family,int type,int protocol);

功能

创建一个用于网络通信的socket套接字(描述符)

参数

family:协议族(AF_INET、AF_INET6、PF_PACKET等)

type:套接字类(SOCK_STREAM、SOCK_DGRAM、SOCK_RAW等)

protocol:协议类别(0、IPPROTO_TCP、IPPROTO_UDP等

返回值:

套接字

特点

创建套接字时,系统不会分配端口

创建的套接字默认属性是主动的,即主动发起服务的请求;当作为服务器时,往往需要修改为被动的

1.2、UDP编程-发送数据

ssize_t sendto(int sockfd,const void *buf,size_t nbytes,int flags,const struct sockaddr *to,socklen_t addrlen);

功能:

向to结构体指针中指定的ip,发送UDP数据

参数:

sockfd:套接字

buf: 发送数据缓冲区

nbytes: 发送数据缓冲区的大小

flags:一般为0

to: 指向目的主机地址结构体的指针

addrlen:to所指向内容的长度

返回值:

成功:发送数据的字符数

失败: -1

注意:

通过to和addrlen确定目的地址

可以发送0长度的UDP数据包


1.3、UDP编程-绑定端口

int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);

功能:将本地协议地址与sockfd绑定

参数:

sockfd: socket套接字

myaddr: 指向特定协议的地址结构指针

addrlen:该地址结构的长度

返回值 :

成功:返回0

失败:其他

1.4、UDP编程-接收数据

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

功能 :

接收UDP数据,并将源地址信息保存在from指向的结构中

参数 :

sockfd:套接字

buf: 接收数据缓冲区

nbytes:接收数据缓冲区的大小

flags: 套接字标志(常为0)

from: 源地址结构体指针,用来保存数据的来源

addrlen: from所指内容的长度

返回值:

成功:接收到的字符数

失败: -1

注意:

通过from和addrlen参数存放数据来源信息

from和addrlen可以为NULL, 表示不保存数据来源


1.5、UDP编程-客户端示例


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_IP  "192.168.0.108"
#define SERVER_PORT   8080

int main(int argc, char *argv[])
{   
    int sockfd;
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);        //创建UDP套接字
    if(sockfd < 0)
    {
        perror("socket");
        exit(-1);
    }
    
    struct sockaddr_in dest_addr;
    bzero(&dest_addr, sizeof(dest_addr));
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port   = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &dest_addr.sin_addr);

    printf("UDP server %s:%d\n", SERVER_IP, SERVER_PORT);
    
    while(1)
    {
        char send_buf[512] = "";
        fgets(send_buf, sizeof(send_buf), stdin);//获取输入
        send_buf[strlen(send_buf)-1] = '\0';
        sendto(sockfd, send_buf, strlen(send_buf), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));//发送数据
    }
    
    close(sockfd);
    return 0;
}

1.6、UDP编程-服务端示例


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_IP  "192.168.0.104"
#define SERVER_PORT   8080

int main(int argc, char *argv[])
{
    int sockfd;
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0)
    {
        perror("socket");
        exit(-1);
    }
        
    struct sockaddr_in my_addr;
    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port   = htons(SERVER_PORT);
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    printf("server bind port: %d\n", SERVER_PORT);
    int ret = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
    if(ret != 0)
    {
        perror("bind");
        close(sockfd);      
        exit(-1);
    }
    printf("receive data:\n");
    while(1)
    {
        int recv_len;
        char recv_buf[512] = "";
        struct sockaddr_in client_addr;
        char client_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16
        socklen_t cliaddr_len = sizeof(client_addr);
        
        recv_len = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&client_addr, &cliaddr_len);
        inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
        //char *client_ip_pr=inet_ntoa(client_addr.sin_addr);
        printf("client_ip:%s ,client_port:%d\n",client_ip, ntohs(client_addr.sin_port));
        printf("data(%d):%s\n",recv_len,recv_buf);
    }
    close(sockfd);
    return 0;
}

1.7、UDP广播

广播:由一台主机向该主机所在局域网内的所有主机发送数据的方式 ,广播只能用UDP或原始IP实现,不能用TCP

广播的用途:

单个服务器与多个客户主机通信时减少分组流通

地址解析协议(ARP)

动态主机配置协议(DHCP)

网络时间协议(NTP)

广播的特点:

处于同一子网的所有主机都必须处理数据

UDP数据包会沿协议栈向上一直到UDP层

运行音视频等较高速率工作的应用,会带来大负

局限于局域网内使用


UDP广播地址

{网络ID,主机ID}

网络ID表示由子网掩码中1覆盖的连续位

主机ID表示由子网掩码中0覆盖的连续位

定向广播地址:主机ID全1

例:对于192.168.220.0/24,其定向广播地址为 192.168.220.255

通常路由器不会转发该广播

受限广播地址:255.255.255.255

路由器从不转发该广播


套接字选项

int setsockopt(int sockfd, int level,int optname,const void *optval,socklen_t optlen);

功能:

设置套接字选项值

参数:

sockfd:套接字

level: 被设置的选项的级别,如果想要在套接字级别上设置选项,就必须把level设置为 SOL_SOCKET

optname:准备设置的选项,option_name可以有哪些取值,这取决于level

optval:类型

optlen:

返回值:

成功:0

失败: -1

示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_IP  "192.168.0.104"
#define SERVER_PORT   8080

int main(int argc, char *argv[])
{
    int sockfd=0;
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0)
    {
        perror("socket");
        exit(-1);
    }
        
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port   = htons(SERVER_PORT);

    
    printf("server bind port: %d\n", SERVER_PORT);
    
    int opt=1;
    setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&opt,sizeof(opt));
    
    char send_buf[1024]={0};
    strcpy(send_buf,"this is broadbast msg\n");

    int len = sendto(sockfd,send_buf,strlen(send_buf),0,(struct sockaddr *)&server_addr,sizeof(server_addr));
    if(len<0)
    {
        printf("send error\n");
        close(sockfd);
        return -1;
    }
    printf("send success\n");

    close(sockfd);
    return 0;
}

1.8、UDP多播(组播)

多播: 数据的收发仅仅在同一分组中进行多播的特点:

多播地址标示一组接口

多播可以用于广域网使用

在IPv4中,多播是可选的

UDP多播地址

IPv4的D类地址是多播地址

十进制:224.0.0.1 - 239.255.255.254

十六进制:E0.00.00.01 - EF.FF.FF.FE 特殊的IP地址多播地址:224.0.0.1:是所有主机组,子网上所有具有多播能力的节点(主机、路由器、打印机)必须在所有具有多播能力的接口上加入该组。224.0.0.2:是所有路由器组,子网上所有多播路由器必须在所有具有多播能力的接口上加入该组。

多播地址分类

224.0.0.0 -- 224.0.255 链路局部的多播地址,是低级拓扑和维护协议保留的,多播路由器不转发以这些地址为目的地址的数据报

224.0.1.0 -- 224.0.1.255: 为用户可用的组播地址(临时组地址),可以用于 Internet 上的。224.0.2.0 -- 238.255.255.255: 用户可用的组播地址(临时组地址),全网范围内有效239.0.0.0 -- 239.255.255.255: 为本地管理组播地址,仅在特定的本地范围内有效


  • 在IPv4因特网域(AF_INET)中,多播地址结构体用如下结构体ip_mreq表示

struct in_addr{
    in_addr_t    s_addr;                              
};
 
struct ip_mreq{
    struct in_addr imr_multiaddr;//多播组ip
    struct in_addr imr_interface;//将要添加到的多播组ip
};

套接口选项

int setsockopt(int sockfd, int level,int optname,const void *optval, socklen_t optlen);

功能:

设置套接字选项值

参数:

sockfd:套接字

level: 被设置的选项的级别,IPPROTO_IP

optname: IP_ADD_MEMBERSHIP :加入多播组 ,IP_DROP_MEMBERSHIP :离开多播组

optval:类型 ip_mreq{}

optlen:

返回值:

成功:0

失败: -1


为什么要用组播

单播和组播是寻址方案的两个极端(要么单个、要么全部),多播则是两者之间的一种折中的方案,多播数据报只应该由对它感兴趣的接口接收。另外,广播一般局限于局域网内使用,多播则既可用于局域网,也可跨广域网使用。

2、TCP编程

2.1、TCP介绍

  • 作为客户端需要具备的条件

(1) 知道服务器的ip、port
(2) 主动连接 服务器

  • 需要用到的函数

socket : 创建TCP套接字(主动)

connect:连接服务器

send:发送数据到服务器

recv: 接受服务器的响应

close:关闭连接

2.2、TCP客户端相关函数

2.2.1 创建TCP套接字函数

	int sockfd;
	sockfd = socket(AF_INET, SOCK_STREAM, 0);// 创建通信端点:套接字

2.2.2 连接服务器函数

int connect(int sockfd,const struct sockaddr *addr,socklen_t len);

功能:主动跟服务器建立链接
参数:

sockfd:socket套接字
addr: 连接的服务器地址结构
len: 地址结构体长度

返回值:

成功:0 失败:其他

注意:

? connect建立连接之后不会产生新的套接字
? 连接成功后才可以开始传输TCP数据

2.2.3 TCP发送数据

#include <sys/socket.h>
ssize_t send(int sockfd, const void* buf,size_t nbytes, int flags);

功能:用于发送数据
参数:

sockfd: 已建立连接的套接字
buf: 发送数据的地址
nbytes: 发送缓数据的大小(以字节为单位)
flags: 套接字标志(常为0)

注意:不能用TCP协议发送0长度的数据包

2.2.4 TCP接收数据

#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf,size_t nbytes, int flags);

功能:用于接收网络数据
参数:

? sockfd:套接字
? buf: 接收网络数据的缓冲区的地址
? nbytes:接收缓冲区的大小(以字节为单位)
? flags: 套接字标志(常为0)

返回值:成功接收到字节数

2.2.5 客户端代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_IP  "192.168.0.107"
#define SERVER_PORT   8080

int main(int argc, char *argv[])
{
    int sockfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);// 创建通信端点:套接字
    if(sockfd < 0)
    {
        perror("socket");
        exit(-1);
    }
    
    struct sockaddr_in server_addr;
    bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
    
    int err_log = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));      // 主动连接服务器
    if(err_log != 0)
    {
        perror("connect");
        close(sockfd);
        exit(-1);
    }
    
    char send_buf[1024] = "";
    printf("send data to [%s:%d]\n",SERVER_IP,SERVER_PORT);
    while(1)
    {
        printf("send:");
        fgets(send_buf,sizeof(send_buf),stdin);
        send_buf[strlen(send_buf)-1]='\0';
        send(sockfd, send_buf, strlen(send_buf), 0);   // 向服务器发送信息
    }


    close(sockfd);
    return 0;
}

2.3、TCP服务端相关函数

做为TCP服务器需要具备的条件

? 具备一个可以确知的地址
? 让操作系统知道是一个服务器,而不是客户端
? 等待连接的到来

对于面向连接的TCP协议来说,连接的建立才真正意味着数据通信的开始

2.3.1、bind函数

bind用法和UDP服务端使用一样,

	struct sockaddr_in my_addr;
	bzero(&my_addr, sizeof(my_addr));	     
	my_addr.sin_family = AF_INET;
	my_addr.sin_port   = htons(port);
	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
	if( err_log != 0)
	{
		perror("binding");
		close(sockfd);		
		exit(-1);
	}

2.3.2、listen函数

#include <sys/socket.h>
int listen(int sockfd, int backlog);

功能:

? 将套接字由主动修改为被动
? 使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接

参数:

? sockfd: socket监听套接字
? backlog:连接队列的长度

返回值:

? 成功:返回0
? 失败:其他

2.3.3、accept 函数

#include <sys/socket.h>
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);

功能:从已连接队列中取出一个已经建立的连接,如果没有任何连接可用,则进入睡眠等待(阻塞)

参数:

? sockfd: socket监听套接字

? cliaddr: 用于存放客户端套接字地址结构

? addrlen:套接字地址结构体长度的地址

返回值:已连接套接字

注意:返回的是一个已连接套接字,这个套接字代表当前这个连接

2.3.4、服务端的代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_PORT   8080

int main(int argc, char *argv[])
{   //创建套接字 
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);   
    if(sockfd < 0)
    {
        perror("socket");
        exit(-1);
    }
    
    struct sockaddr_in my_addr;
    bzero(&my_addr, sizeof(my_addr));        
    my_addr.sin_family = AF_INET;
    my_addr.sin_port   = htons(SERVER_PORT);
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
    if( err_log != 0)
    {
        perror("binding");
        close(sockfd);      
        exit(-1);
    }
    
    err_log = listen(sockfd, 10);
    if(err_log != 0)
    {
        perror("listen");
        close(sockfd);      
        exit(-1);
    }   
    
    printf("listen client port=%d...\n",SERVER_PORT);

    while(1)
    {   
    
        struct sockaddr_in client_addr;        
        char cli_ip[INET_ADDRSTRLEN] = "";     
        socklen_t cliaddr_len = sizeof(client_addr);    
        
        int connfd;
        connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);       
        if(connfd < 0)
        {
            perror("accept");
            continue;
        }

        inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
        printf("----------------------------------------------\n");
        printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));
        
        char recv_buf[2048] = "";
        while( recv(connfd, recv_buf, sizeof(recv_buf), 0) > 0 )
        {
            printf("\nrecv data:\n");
            printf("%s\n",recv_buf);
        }
        
        close(connfd);     //关闭已连接套接字
        printf("client closed!\n");
    }
    close(sockfd);         //关闭监听套接字
    return 0;
}

相关推荐

Mysql和Oracle实现序列自增(oracle创建序列的sql)

Mysql和Oracle实现序列自增/*ORACLE设置自增序列oracle本身不支持如mysql的AUTO_INCREMENT自增方式,我们可以用序列加触发器的形式实现,假如有一个表T_WORKM...

关于Oracle数据库12c 新特性总结(oracle数据库19c与12c)

概述今天主要简单介绍一下Oracle12c的一些新特性,仅供参考。参考:http://docs.oracle.com/database/121/NEWFT/chapter12102.htm#NEWFT...

MySQL CREATE TABLE 简单设计模板交流

推荐用MySQL8.0(2018/4/19发布,开发者说同比5.7快2倍)或同类型以上版本....

mysql学习9:创建数据库(mysql5.5创建数据库)

前言:我也是在学习过程中,不对的地方请谅解showdatabases;#查看数据库表createdatabasename...

MySQL面试题-CREATE TABLE AS 与CREATE TABLE LIKE的区别

执行"CREATETABLE新表ASSELECT*FROM原表;"后,新表与原表的字段一致,但主键、索引不会复制到新表,会把原表的表记录复制到新表。...

Nike Dunk High Volt 和 Bright Spruce 预计将于 12 月推出

在街上看到的PandaDunk的超载可能让一些球鞋迷们望而却步,但Dunk的浪潮仍然强劲,看不到尽头。我们看到的很多版本都是为女性和儿童制作的,这种新配色为后者引入了一种令人耳目一新的新选择,而...

美国多功能舰载雷达及美国海军舰载多功能雷达系统技术介绍

多功能雷达AN/SPY-1的特性和技术能力,该雷达已经在美国海军服役了30多年,其修改-AN/SPY-1A、AN/SPY-1B(V)、AN/SPY-1D、AN/SPY-1D(V),以及雷神...

汽车音响怎么玩,安装技术知识(汽车音响怎么玩,安装技术知识视频)

全面分析汽车音响使用或安装技术常识一:主机是大多数人最熟习的音响器材,有关主机的各种性能及规格,也是耳熟能详的事,以下是一些在使用或安装时,比较需要注意的事项:LOUDNESS:几年前的主机,此按...

【推荐】ProAc Response系列扬声器逐个看

有考牌(公认好声音)扬声器之称ProAcTablette小音箱,相信不少音响发烧友都曾经,或者现在依然持有,正当大家逐渐掌握Tablette的摆位设定与器材配搭之后,下一步就会考虑升级至表现更全...

#本站首晒# 漂洋过海来看你 — BLACK&amp;DECKER 百得 BDH2000L无绳吸尘器 开箱

作者:初吻给了烟sco混迹张大妈时日不短了,手没少剁。家里有了汪星人,吸尘器使用频率相当高,偶尔零星打扫用卧式的实在麻烦(汪星人:你这分明是找借口,我掉毛是满屋子都有,铲屎君都是用卧式满屋子吸的,你...

专题|一个品牌一件产品(英国篇)之Quested(罗杰之声)

Quested(罗杰之声)代表产品:Q212FS品牌介绍Quested(罗杰之声)是录音监听领域的传奇品牌,由英国录音师RogerQuested于1985年创立。在成立Quested之前,Roger...

常用半导体中英对照表(建议收藏)(半导体英文术语)

作为一个源自国外的技术,半导体产业涉及许多英文术语。加之从业者很多都有海外经历或习惯于用英文表达相关技术和工艺节点,这就导致许多英文术语翻译成中文后,仍有不少人照应不上或不知如何翻译。为此,我们整理了...

Fyne Audio F502SP 2.5音路低音反射式落地音箱评测

FyneAudio的F500系列,有新成员了!不过,新成员不是新的款式,却是根据原有款式提出特别版。特别版产品在原有型号后标注了SP字样,意思是SpecialProduction。Fyne一共推出...

有哪些免费的内存数据库(In-Memory Database)

以下是一些常见的免费的内存数据库:1.Redis:Redis是一个开源的内存数据库,它支持多种数据结构,如字符串、哈希表、列表、集合和有序集合。Redis提供了快速的读写操作,并且支持持久化数据到磁...

RazorSQL Mac版(SQL数据库查询工具)

RazorSQLMac特别版是一款看似简单实则功能非常出色的SQL数据库查询、编辑、浏览和管理工具。RazorSQLformac特别版可以帮你管理多个数据库,支持主流的30多种数据库,包括Ca...

取消回复欢迎 发表评论: