sta sin pri 头文件 没有 center use 当前
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); |
参数 |
|
功能 |
发送数据 |
返回值 |
返回:成功返回发送字节数,出错返回 - 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
- #include
- #include
- #include
- #include
- #include
- #include
- #include int sockfd;
- voidsig_handler(int signo)
- {
- if(signo == SIGINT){
- printf("server close\n");
- close(sockfd);
- exit(1);
- }
- }
- //输出客户端的信息
- voidout_addr(structsockaddr_in* addr)
- {
- charip[16];
- intport =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);
- charbuff[1024];
- memset(buff, 0,sizeof(buff));
- //接受客户端的数据报文
- //使用recvfrom而不使用recv函数的主要目的是为了获取客户端信息
- if(recvfrom(sockfd, buff,sizeof(buff),0,
- (structsockaddr*)&cliAddr, &len) <0){
- perror("recvfrom error");
- }else{
- out_addr(&cliAddr);
- printf("client send info: %s\n", buff);
- //向客户端发送数据报文
- long intt = time(0);
- char* ptr = ctime(&t);
- size_t size = strlen(ptr) *sizeof(char);
- if(sendto(sockfd, ptr, size,0,(structsockaddr*)&cliAddr, len) <0){
- perror("sendto error");//cliAddr己经从recvfrom那里得到了客户端的信息
- }
- }
- }
- intmain(intargc,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;
- intopt =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;//IPv4servAddr.sin_port = htons(atoi(argv[1]));//portservAddr.sin_addr.s_addr = INADDR_ANY;//ip
- if(bind(sockfd, (structsockaddr*)&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
- #include
- #include
- #include
- #include
- #include intmain(intargc,char* argv[])
- {
- if(argc <3){
- printf("usage: %s ip port\n", argv[0]);
- exit(1);
- }
- /*步骤1:创建socket*/
- intsockfd = socket(AF_INET, SOCK_DGRAM,0);
- if(sockfd <0){
- perror("socket error");
- exit(1);
- }
- /*步骤2: 调用recvfrom和sendto等函数和服务端双向通信*/
- structsockaddr_in servAddr;//封装服务器的地址信息memset(&servAddr,0,sizeof(servAddr));
- servAddr.sin_family = AF_INET;//IPv4servAddr.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, (structsockaddr*)&servAddr,sizeof(servAddr)) <0){
- perror("connect error");
- exit(1);
- }
- charbuff[1024] ="hello world!";
- //向服务器发送数据报文
- if((sendto(sockfd, buff,sizeof(buff),0, (structsockaddr*)&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]14.udp]# bin/time_udp_client 127.0.0.1 8888
- * Sat Mar 18 10:01:12 2017
- * [[email protected]14.udp]#*/
第 14 章 UDP 编程(1)_UDP 客户端服务器模型
来源: http://www.bubuko.com/infodetail-2011392.html