江门广告网站推广技巧,博客网站设计方案,中山权威发布,男女做那个的的视频网站写在前边
本文是B站up主韦东山的4_8-3.UDP编程示例_哔哩哔哩_bilibili视频的笔记#xff0c;其中有些部分博主也没有理解#xff0c;希望各位辩证的看。
UDP协议简介
UDP 是一个简单的面向数据报的运输层协议#xff0c;在网络中用于处理数据包#xff0c;是一种无连接的…写在前边
本文是B站up主韦东山的4_8-3.UDP编程示例_哔哩哔哩_bilibili视频的笔记其中有些部分博主也没有理解希望各位辩证的看。
UDP协议简介
UDP 是一个简单的面向数据报的运输层协议在网络中用于处理数据包是一种无连接的协议。UDP 不提供可靠性的传输它只是把应用程序传给 IP 层的数据报发送出去但是并不能保证它们能到达目的地。由于 UDP 在传输数据报前不用在客户和服务器之间建立一个连接且没有超时重发等机制故而传输速度很快。
对于UDP网络编程步骤这里借用韦山东老师的图 图 3UDP 用户数据包模式
UDP相对于TCP编程来说简单了很多因为UDP没有TCP那些可靠连接的东西所以编程相对来说也简单了一些。
这里对于函数只有发送和接受函数和之前有点区别
sendto()
函数结构
#include sys/types.h#include sys/socket.hssize_t sendto ( socket s , const void * msg, int len, unsigned int flags, conststruct sockaddr * to , int tolen ) ;
描述
sendto() 用来将数据由指定的socket传给对方主机。
参数
- s
用于通信的通信描述符对于服务器就是指accept函数返回的通信描述符
- msg
指向一片应用缓存用于存放要发送的数据存放数据一般使用结构体变量。
- len
存放发送数据的缓存的大小。
- flags
一般设置为0此时是阻塞发送的阻塞发送是指发送数据不成功会一直阻塞直到被某信号中断或发送成功为止不过发送数据一般不阻塞。
- to
存放指定欲传送的网络地址结构sockaddr请参考bind()。
- tolen
sockaddr的结构长度。
- 返回值
成功返回发送的字节数失败返回-1
recvfrom()
函数结构
#include sys/types.h#include sys/socket.hssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
描述
它是一个系统调用用于从套接字接收数据。该函数通常与无连接的数据报服务如 UDP一起使用但也可以与其他类型的套接字使用。与简单的 recv() 函数不同recvfrom() 可以返回数据来源的地址信息。
参数
- sockfd
一个已打开的套接字的描述符
- buf
指明一个缓冲区该缓冲区用来存放recvfrom函数接收到的数据
- len
指明buf的长度。
- flags
传0 表示使用默认协议。
- src_addr
一个指针指向一个 sockaddr 结构用于保存发送数据的源地址结构sockaddr请参考bind()。
- addrlen
src_addr的结构长度。当 recvfrom() 返回时该值会被修改为实际地址的长度以字节为单位。
- 返回值
成功成功执行时返回接收到的字节数。失败返回-1。
剩下的函数请参考TCP中的函数linux网络编程——TCP编程-CSDN博客
现在分别实现server 程序和client 程序。
server程序
在这个函数中参照server图进行编程将图中所有函数挨个实现即可。 图 4server
具体实现看代码
#include sys/types.h /* See NOTES */#include sys/socket.h#include stdio.h#include stdlib.h#include errno.h#include string.h#include netinet/in.h#include sys/wait.h#include unistd.h#include arpa/inet.h#include pthread.h/** 服务器端程序* socket* bind* sendto/recvfrom* close*/#define PORT 8888struct Param {int sockfd;char* buff;struct sockaddr* src_addr;};// 接收数据在子线程中处理void* Receive_data(void* Param1){struct Param Param2 *(struct Param*)Param1;while (1){// 接收数据int addr_len sizeof(Param2.src_addr);int iRecvLen recvfrom(Param2.sockfd, Param2.buff, sizeof(Param2.buff), 0, Param2.src_addr, addr_len);if (iRecvLen 0){Param2.buff[iRecvLen] \0;// inet_ntoa(tSocketClientAddr.sin_addr)是将IP地址转换为字符串的函数printf(Get Msg From Client: %s: %s\n, inet_ntoa(((struct sockaddr_in*)Param2.src_addr)-sin_addr), Param2.buff);}}}int main(int argc, char** argv){int isocketfd;int Client_socketfd;int ret;struct sockaddr_in my_addr;struct sockaddr_in Client_addr;char send_buf[1024];char buff[1000];struct Param Param1;pthread_t ntid;// SOCK_DGRAM是UDP协议AF_INET表示IPv4协议isocketfd socket(AF_INET, SOCK_DGRAM, 0);if (-1 isocketfd){printf(create socket failed!\n);return -1;}// 配置bind函数的地址信息my_addr.sin_family AF_INET; //指定协议族为IPV4版本的TCP/IP协议族my_addr.sin_port htons(PORT); //指定端口号和客户端通信的端口号两者必须一致my_addr.sin_addr.s_addr htonl(INADDR_ANY); //指定IP地址这里设置为INADDR_ANY表示可以接收任何来源的连接请求ret bind(isocketfd, (const struct sockaddr*)my_addr,sizeof(struct sockaddr));if (-1 ret){printf(bind socket failed!\n);return -1;}Param1.sockfd isocketfd;Param1.buff buff;Param1.src_addr (struct sockaddr*)Client_addr;ret pthread_create(ntid, NULL, Receive_data, Param1);if (ret){printf(create pthread failed!\n);return -1;}while (1){// 发送数据if (fgets(send_buf, sizeof(send_buf), stdin)){sendto(isocketfd, send_buf, strlen(send_buf), 0, (struct sockaddr*)Client_addr, sizeof(Client_addr));}}close(isocketfd);return 0;} 这里使用了多线程将发送和接收数据分开实现发送和接收数据互不干涉其中pthread_create()函数是创建一个线程具体函数分析见pthread_create()章节这里将接收数据放入了创建的子线程中主函数中实现发送函数。 图 5服务器端测试结果
这里获得的数据其中192.168.147.132的IP是客户端的数据。
client 程序
在客户端的程序中基本思路还是和之前服务器的程序相同都是使用多线程将接收数据放入了创建的子线程中还是依照韦老师的图 图 6client
依次实现如上函数即可具体实现如下代码
#include sys/types.h /* See NOTES */#include sys/socket.h#include stdio.h#include stdlib.h#include errno.h#include string.h#include netinet/in.h#include sys/wait.h#include unistd.h#include arpa/inet.h#include pthread.h/** UDP客户端程序* socket* send* close*/#define PORT 8888// 线程参数结构体struct Param {int sockfd;char* buff;struct sockaddr* src_addr;};// 接收数据在子线程中处理void* Receive_data(void* Param1){struct Param Param2 *(struct Param*)Param1;while (1){// 接收数据int addr_len sizeof(Param2.src_addr);int iRecvLen recvfrom(Param2.sockfd, Param2.buff, sizeof(Param2.buff), 0, Param2.src_addr, addr_len);if (iRecvLen 0){Param2.buff[iRecvLen] \0;// inet_ntoa(tSocketClientAddr.sin_addr)是将IP地址转换为字符串的函数printf(Get Msg From Client: %s: %s\n, inet_ntoa(((struct sockaddr_in*)Param2.src_addr)-sin_addr), Param2.buff);}else if (iRecvLen -1){perror(recvfrom failed);break;}}}int main(int argc, char** argv){int isocketfd;struct sockaddr_in client_addr;char send_buf[1024];struct Param Param1;pthread_t ntid;char reve_buff[1024];memset(send_buf, 0, sizeof(send_buf));if (argc 2){printf(Usage: %s ip_address\n, argv[0]);return -1;}client_addr.sin_family AF_INET; //指定协议族为IPV4版本的TCP/IP协议族client_addr.sin_port htons(PORT); //指定端口号//指定IP地址htons()函数是将一个本地字节序的short转为网络字节序的shortclient_addr.sin_addr.s_addr inet_addr(argv[1]);isocketfd socket(AF_INET, SOCK_DGRAM, 0);if (-1 isocketfd){printf(create socket failed!\n);return -1;}Param1.sockfd isocketfd;Param1.buff reve_buff;Param1.src_addr (struct sockaddr*)client_addr;int ret pthread_create(ntid, NULL, Receive_data, Param1);if (ret){printf(create pthread failed!\n);return -1;}while (1){if (fgets(send_buf, sizeof(send_buf), stdin)){sendto(isocketfd, send_buf, strlen(send_buf), 0, (struct sockaddr*)client_addr, sizeof(client_addr));}}return 0;} 图 7客户端测试结果
连接成功后即可发送和接收数据。
pthread_create()
函数结构
#include pthread.hint pthread_create(pthread_t* restrict tidp,const pthread_attr_t* restrict_attr,void* (*start_rtn)(void*),void *restrict arg);
描述
用来创建线程并向线程函数传递参数。
参数
- tidp
事先创建好的pthread_t类型的参数。成功时tidp指向的内存单元被设置为新创建线程的线程ID。
- attr
用于定制各种不同的线程属性。通常直接设为NULL。
- start_rtn
新创建线程从此函数开始运行。无参数时arg设为NULL即可。
- arg
start_rtn函数的参数。无参数时设为NULL即可。有参数时输入参数的地址。当多于一个参数时应当使用结构体传入。
- 返回值
成功返回0失败返回错误码。