udpclient.c
#include <stdio.h> //printf,sprintf,perror 相关函数在此声明
#include <string.h> //memset 相关函数在此声明
#include <unistd.h> //read,close 相关函数在此声明
#include <arpa/inet.h> //sockaddr_in,socket,AF_INET,SOCK_DGRAM,htons,inet_addr,sendto,recvfrom 相关函数和宏在此声明和定义
#include <fcntl.h> //open,O_RDONLY 相关函数和宏在此声明和定义
#define BUF_SIZE 1024
#define PORT 9000
int main(int argc,char *argv[])
{
struct sockaddr_in server_sai;
int sfd=0,res=-1,recvbytes=0,sendbytes=0,readbytes=0,fa=0;
int addrlen=sizeof(struct sockaddr);
char buf[BUF_SIZE],buf2[5]={0};
char *filename=argv[2]; //进行变量的定义和初始化
if(argc != 3)
{
printf("error number of argc:%dn",argc);
return res;
}
if (-1==(fa=open(argv[2],O_RDONLY,0644))) //将最后一个参数作为文件名,打开文件
{
printf("cannot open file:%sn",filename);
return res;
}
if(-1 == (sfd=socket(AF_INET,SOCK_DGRAM,0))) //创建一个IPV4的UDP socket
{
perror("socket");
return res;
}
server_sai.sin_family=AF_INET; //IPV4 协议族
server_sai.sin_port=htons(PORT); //9000端口
server_sai.sin_addr.s_addr=inet_addr(argv[1]); //使用第一个参数作为IP地址
memset(&(server_sai.sin_zero),0,sizeof(server_sai.sin_zero)); //将结构体剩余部分填零
memset(buf,0,sizeof(buf)); //将buf清零
do
{
if(-1 == (readbytes=read(fa,buf,sizeof(buf)))) //从指定文件中读取数据写到buf中
{
printf("read error on:%sn",filename);
return res;
}
if (-1 == (sendbytes=sendto(sfd,buf,readbytes,0,(struct sockaddr *)&server_sai,sizeof(struct sockaddr)))) //将buf中的数据写到远端
{
perror("sendto");
return res;
}
if (-1 == (recvbytes=recvfrom(sfd,buf2,5,0,(struct sockaddr *)&server_sai,(socklen_t *)&addrlen))) //从远端获取信息,用于同步节奏
{
perror("recvfrom");
return res;
}
}while(readbytes == sizeof(buf)); //如果读取的数据不再是一整块,就意味着已经读完,随即跳出循环
if (-1 == (recvbytes=recvfrom(sfd,buf2,5,0,(struct sockaddr *)&server_sai,(socklen_t *)&addrlen))) //从远端读取数据到buf2中
{
perror("recvfrom");
return res;
}
printf("%d --> %sn",recvbytes,buf2); //将接收到的字节数和数据内容打印出来
close(sfd);
close(fa); //进行清理工作,关闭描述符
res=0;
return res;
}
编译执行
代码语言:javascript复制emacs@ubuntu:~/c$ alias gtc
alias gtc='gcc -Wall -g -o'
emacs@ubuntu:~/c$ gtc udpserver.x udpserver.c; gtc udpclient.x udpclient.c
emacs@ubuntu:~/c$
此时系统中并没有开放9000端口
代码语言:javascript复制emacs@ubuntu:~/c$ netstat -anu | grep 9000
emacs@ubuntu:~/c$
运行服务端
代码语言:javascript复制emacs@ubuntu:~/c$ ./udpserver.x
此时系统中多了一个9000端口
代码语言:javascript复制emacs@ubuntu:~/c$ netstat -anu | grep 9000
udp 0 0 0.0.0.0:9000 0.0.0.0:*
emacs@ubuntu:~/c$
服务端也并没有 /tmp/x.download
这个文件
emacs@ubuntu:~/c$ ll /tmp/x.download
ls: 无法访问/tmp/x.download: 没有那个文件或目录
emacs@ubuntu:~/c$
运行客户端,会立刻返回
代码语言:javascript复制emacs@ubuntu:~/c$ du -sh 4.png
8.6M 4.png
emacs@ubuntu:~/c$ ./udpclient.x 127.0.0.1 4.png
4 --> DONE
emacs@ubuntu:~/c$
服务端会打印信息并且返回,对比两个文件也没有差异
代码语言:javascript复制emacs@ubuntu:~/c$ ./udpserver.x
i:8786
recvbytes:860
emacs@ubuntu:~/c$ diff /tmp/x.download 4.png
emacs@ubuntu:~/c$
编译执行过程中没有报错,从结果来看,符合预期
recvfrom
sys/socket.h
中有关于 recvfrom 的声明
/* Read N bytes into BUF through socket FD.
If ADDR is not NULL, fill in *ADDR_LEN bytes of it with tha address of
the sender, and store the actual size of the address in *ADDR_LEN.
Returns the number of bytes read or -1 for errors.
This function is a cancellation point and therefore not marked with
__THROW. */
extern ssize_t recvfrom (int __fd, void *__restrict __buf, size_t __n,
int __flags, __SOCKADDR_ARG __addr,
socklen_t *__restrict __addr_len);
从套接口上接收数据,并捕获数据发送源的地址
__fd
标识一个已连接套接口的描述字
__buf
接收数据缓冲区
__n
缓冲区长度
__flags
调用操作方式
__addr
(可选)指针,指向装有源地址的缓冲区
__addr_len
(可选)指针,指向__addr
缓冲区长度值
返回值:>0 返回读入的字节数; ==0 连接已中止; <0 返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码
代码语言:javascript复制EBADF 参数s非合法的socket处理代码
EFAULT 参数中有一指针指向无法存取的内存空间
ENOTSOCK 参数s为一文件描述词,非socket
EINTR 被信号所中断
EAGAIN 此动作会令进程阻断,但参数s的socket为不可阻断
ENOBUFS 系统的缓冲内存不足
ENOMEM 核心内存不足
EINVAL 传给系统调用的参数不正确
sendto
sys/socket.h
中有关于 sendto 的声明
/* Send N bytes of BUF on socket FD to peer at address ADDR (which is
ADDR_LEN bytes long). Returns the number sent, or -1 for errors.
This function is a cancellation point and therefore not marked with
__THROW. */
extern ssize_t sendto (int __fd, __const void *__buf, size_t __n,
int __flags, __CONST_SOCKADDR_ARG __addr,
socklen_t __addr_len);
适用于发送未建立连接的UDP数据包
__fd
一个标识套接口的描述字
__buf
包含待发送数据的缓冲区
__n
buf缓冲区中数据的长度
__flags
调用方式标志位
__addr
(可选)指针,指向目的套接口的地址
__addr_len
所指地址的长度
返回值 :>0 返回所发送数据的总数(请注意这个数字可能小于len中所规定的大小);==0 连接已中止 ;<0 返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码
代码语言:javascript复制EBADF 参数s非法的socket处理代码
EFAULT 参数中有一指针指向无法存取的内存空间
ENOTSOCK 参数 s为一文件描述词,非socket
EINTR 被信号所中断
EAGAIN 此动作会令进程阻断,但参数s的socket为不可阻断的
ENOBUFS 系统的缓冲内存不足
EINVAL 传给系统调用的参数不正确
SOCK_DGRAM
bits/socket.h
中有关于 SOCK_DGRAM 的定义
/* Types of sockets. */
enum __socket_type
{
SOCK_STREAM = 1, /* Sequenced, reliable, connection-based
byte streams. */
#define SOCK_STREAM SOCK_STREAM
SOCK_DGRAM = 2, /* Connectionless, unreliable datagrams
of fixed maximum length. */
#define SOCK_DGRAM SOCK_DGRAM
SOCK_RAW = 3, /* Raw protocol interface. */
#define SOCK_RAW SOCK_RAW
SOCK_RDM = 4, /* Reliably-delivered messages. */
#define SOCK_RDM SOCK_RDM
SOCK_SEQPACKET = 5, /* Sequenced, reliable, connection-based,
datagrams of fixed maximum length. */
#define SOCK_SEQPACKET SOCK_SEQPACKET
SOCK_DCCP = 6, /* Datagram Congestion Control Protocol. */
#define SOCK_DCCP SOCK_DCCP
SOCK_PACKET = 10, /* Linux specific way of getting packets
at the dev level. For writing rarp and
other similar things on the user level. */
#define SOCK_PACKET SOCK_PACKET
/* Flags to be ORed into the type parameter of socket and socketpair and
used for the flags parameter of paccept. */
SOCK_CLOEXEC = 02000000, /* Atomically set close-on-exec flag for the
new descriptor(s). */
#define SOCK_CLOEXEC SOCK_CLOEXEC
SOCK_NONBLOCK = 04000 /* Atomically mark descriptor(s) as
non-blocking. */
#define SOCK_NONBLOCK SOCK_NONBLOCK
};
里面规定 SOCK_DGRAM
为一种不连接,不可靠的传输模式
附:TCP和UDP的区别
代码语言:javascript复制Tip: 引自 《TCP和UDP的最完整的区别》
TCP与UDP基本区别
1.基于连接与无连接
2.TCP要求系统资源较多,UDP较少;
3.UDP程序结构较简单
4.流模式(TCP)与数据报模式(UDP);
5.TCP保证数据正确性,UDP可能丢包
6.TCP保证数据顺序,UDP不保证
UDP应用场景
1.面向数据报方式
2.网络数据大多为短消息
3.拥有大量Client
4.对数据安全性无特殊要求
5.网络负担非常重,但对响应速度要求高
具体编程时的区别
1.socket()的参数不同
2.UDP Server不需要调用listen和accept
3.UDP收发数据用sendto/recvfrom函数
4.TCP:地址信息在connect/accept时确定
5.UDP:在sendto/recvfrom函数中每次均 需指定地址信
6.UDP:shutdown函数无效
TCP与UDP区别总结
1.TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2.TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3.TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5.TCP首部开销20字节;UDP的首部开销小,只有8个字节
6.TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
总结
以下函数可以进行socket的创建与控制,是UDP网络编程的基础
- socket
- setsockopt
- bind
- recvfrom
- sendto
通过各方面资料弄懂其参数的意义和返回值的类型,是熟练掌握的基础
原文地址