基于UDP客户端服务器的编程模型-linux网络编程
堅持在代碼中注釋,邊讀代碼邊學(xué)習(xí)Linux網(wǎng)絡(luò)編程
使用到的發(fā)送函數(shù)原型:
#include <sys/types.h>#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);成功返回發(fā)送字節(jié)數(shù),出錯返回-1ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);成功返回發(fā)送字節(jié)數(shù),出錯返回-1ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);成功返回發(fā)送字節(jié)數(shù),出錯返回-1使用到的接受函數(shù)原型:
#include <sys/types.h>#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);成功返回消息的字節(jié)數(shù),無消息返回0,出錯返回-1?
設(shè)置套接字選項函數(shù)和獲取套接字選項函數(shù):
#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);編寫一個UDP客戶端和服務(wù)器:
客戶端代碼:
#include <sys/socket.h> #include <netdb.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <signal.h> #include <time.h> #include <memory.h> #include <arpa/inet.h>int main(int argc, char *argv[]) {if(argc < 3){printf("usage: %s ip port \n",argv[0]);exit(1);}/*步驟1: 創(chuàng)建socke套接字*/int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){perror("socket error!");exit(1);}/**步驟2: 調(diào)用recvfrom和sendto等函數(shù)和服務(wù)器進行雙向通信*/struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET; //使用IPV4serveraddr.sin_port = htons(atoi(argv[2]));inet_pton(AF_INET, argv[1], &serveraddr.sin_addr.s_addr);char buffer[1024] = "hello iotek";//向服務(wù)器發(fā)送數(shù)據(jù)報文if(sendto(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0){perror("sendto error!");exit(1);}else{//若是成功發(fā)送客戶端就開始接受服務(wù)端發(fā)送的數(shù)據(jù)報文memset(buffer, 0, sizeof(buffer));//若是上面已經(jīng)使用,sendto函數(shù)已經(jīng)發(fā)送成功下面可以直接使用recv//否則需要使用recvfrom函數(shù)//在使用tcp的時候如果接受的是0 說明對方斷開連接,但是udp是面向無連接的不需要判斷接受的數(shù)據(jù)是否等于0//只需要判斷是否小于0即可,當接收的數(shù)據(jù)小于0的時候代表接收出錯if(recv(sockfd, buffer, sizeof(buffer), 0) < 0){perror("recv error");exit(1);}else{printf("%s\n",buffer);}}}服務(wù)器代碼:
#include <sys/socket.h> #include <netdb.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <signal.h> #include <time.h> #include <arpa/inet.h>int sockfd; void do_service(int fd); void sig_handler(int signo); void out_addr(struct sockaddr_in *clientaddr);int main(int argc, char * argv[]) {if(argc < 2){printf("usage: %s port\n", argv[0]);exit(1);}if(signal(SIGINT, sig_handler) == SIG_ERR) //開始捕獲信號 SIGINT {perror("signal sigint error!");exit(1);}/*步驟1: 創(chuàng)建socket*/sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){perror("socket error!");exit(1);}/*設(shè)置選項使停掉的端口馬上就可以恢復(fù)使用*/int ret;int opt = 1;//設(shè)置套接字選項if((ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) < 0){perror("setsockopt error!");exit(1);}/*步驟2: 調(diào)用bind函數(shù)對socket和地址進行綁定*/struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[1])); //portserveraddr.sin_addr.s_addr = INADDR_ANY; //ip匹配所有的ipif(bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0){perror("bind error!");exit(1);}/*步驟3: 和客戶端進行雙向數(shù)據(jù)通訊*/while(1){do_service(sockfd);}}void do_service(int fd) {struct sockaddr_in clientaddr;socklen_t len = sizeof(clientaddr);char buffer[1024];memset(buffer, 0, sizeof(buffer));//接受客戶端發(fā)送過來的數(shù)據(jù)報文 可以獲得對方的地址信息if(recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr*)&clientaddr, &len) < 0){perror("recvfrom error!");}else{out_addr(&clientaddr);printf("client send infomation: %s\n", buffer);//服務(wù)器向客戶端發(fā)送報文long int t = time(0);char *ptr = ctime(&t);size_t size = strlen(ptr) * sizeof(char);if(sendto(fd, ptr, size, 0, (struct sockaddr*)&clientaddr, len) < 0){perror("send error!");}}} void sig_handler(int signo) {if(signo == SIGINT){printf("serrver clolse!\n");;close(sockfd);exit(1);} }//輸出客戶端信息 void out_addr(struct sockaddr_in *clientaddr) {char ip[16];int port;memset(ip, 0, sizeof(ip));inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, ip, sizeof(ip));port = ntohs(clientaddr->sin_port);printf("client : %s(%d)\n", ip, port);}說明:在客戶端中若是不想使用sendto函數(shù),而是使用send函數(shù)需要結(jié)合connect函數(shù)使用:
if(sendto(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0){perror("sendto error!");exit(1);} 等價于if(connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) {perror("connect error!");exit(1); }+if(send(sockfd, buffer, sizeof(buffer), 0) < 0) {perror("sendto error!");exit(1);}但是需要注意的一點就是在udp中使員工connect函數(shù)并不像tcp中會進行三次握手,在udp中使用send函數(shù),只是向內(nèi)核中記錄服務(wù)器的地址信息和端口等數(shù)據(jù)信息;即相當于實現(xiàn)相應(yīng)sockfd描述符與服務(wù)器地址的綁定,因此再調(diào)用send函數(shù)就不需要再次提供服務(wù)器地址信息,就能將相應(yīng)的數(shù)據(jù)報文正確的發(fā)送。
調(diào)用connect的好處就是在udp的客戶端中能夠保證接受的數(shù)據(jù)是來之連接的服務(wù)器的數(shù)據(jù)報文,否則不使用connect的話,客戶端有可能接受不是來之服務(wù)器的數(shù)據(jù)報文
測試:
同樣為了測試方便使用環(huán)回地址andrew@andrew-Thurley:~/work/network$ bin/time_udp_server 8888 client : 127.0.0.1(45082) client send infomation: hello iotekandrew@andrew-Thurley:~/work/network$ bin/time_udp_client 127.0.0.1 8888 Sun Aug 19 12:22:21 2018?
?
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的基于UDP客户端服务器的编程模型-linux网络编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 作者:曹玉社(1990-),男,国防科学
- 下一篇: sock使用UDP协议进行广播发送数据