通過五一好幾天的娛樂,這幾天翻過頭又把socket的UDP編程反芻一下,借了幾本書又思考了一下,又把給我解答問題的朋友們的留言看了一遍,對UDP的socket編程終於有所小獲,在網絡中跨平臺試了一下,好使,甚是開心,並打算在此總結一下以供初入門的朋友們少走彎路,並在此感謝積極給我解答問題的朋友們 @聖何塞白話人 @mallon @zino @xinzaibing @mallon @dd 是大家讓我懂得了開源的精神,我也會以開源支持者的身份積極參與其中。下面是個人正文。編程
Linux下的socket編程主要就是幾個結構體和幾個函數就能夠實現用協議通訊的功能。主要的結構體以下:服務器
struct in_addr { in_addr_t s_addr; //in_addr_t 其實就是unsigned long }; struct sockaddr { unsigned short sa_family; char sa_data[14]; }; struct sockaddr_in { short int sin_family; unsigned short int sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; };
第二個結構體和第三個結構體其實內容是同樣的,只是後面講的要用到的socket的函數bind, recvfrom, sendto, listen, accept, recv, send 等函數中用到的參數都是struct sockaddr,可是賦值的時候仍是用sockaddr_in方便,因而就略顯「畫蛇添足」的給struct sockaddr_in定義的結構體的成員變量賦值,在調用函數的時候再用(struct sockaddr*)強制轉換一下(這些在書上都是不說的)。
主要須要調用的函數上面提到了一些,這裏就不說了,用終端中的man能夠查到很詳細的東西。下面貼一下程序:網絡
服務器程序:socket
#include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #define MAXLINE 80 #define sport 4567 #define sip "49.140.191.30" void do_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) { int n; socklen_t len; char mesg[MAXLINE]; int i=0; while(1) { printf("OK %d\n",i); i++; len = clilen; /* waiting for receive data */ n = recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len); /* sent data back to client */ sendto(sockfd, mesg, n, 0, pcliaddr, len); } } /*主函數*/ int main(void) { int sockfd; struct sockaddr_in servaddr, cliaddr; sockfd = socket(AF_INET, SOCK_DGRAM, 0); /* create a socket */ /* init servaddr */ bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr=inet_addr(sip); //servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(sport); /* bind address and port to socket */ if(bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) { perror("bind error"); exit(1); } do_echo(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)); return 0; }
客戶端程序:
#include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <arpa/inet.h> #include <unistd.h> #define MAXLINE 80 #define SERV_PORT 4567 void do_cli(FILE *fp, int sockfd, struct sockaddr *pservaddr, socklen_t servlen) { int n; char sendline[MAXLINE], recvline[MAXLINE + 1]; /* connect to server */ if(connect(sockfd, (struct sockaddr *)pservaddr, servlen) == -1) { perror("connect error"); exit(1); } while(fgets(sendline, MAXLINE, fp) != NULL) { /* read a line and send to server */ write(sockfd, sendline, strlen(sendline)); /* receive data from server */ n = read(sockfd, recvline, MAXLINE); if(n == -1) { perror("read error"); exit(1); } recvline[n] = 0; /* terminate string */ fputs(recvline, stdout); } } int main(int argc, char **argv) { int sockfd; struct sockaddr_in servaddr; /* check args */ if(argc != 2) { printf("usage: udpclient <IPaddress>\n"); exit(1); } /* init servaddr */ bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) { printf("[%s] is not a valid IPaddress\n", argv[1]); exit(1); } sockfd = socket(AF_INET, SOCK_DGRAM, 0); do_cli(stdin, sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); return 0; }
在實際狀況下,服務器用的是公網IP地址,而我在學校用的校園網正好也是給每一個學生分配公網IP地址(封殺路由器),因此正好就把本機的公網IP做爲服務器的IP地址,端口隨意設了一個沒有用的。客戶端的程序和書上不一樣之處(優勢)在於:當客戶端鏈接到主機以後(connect調用以後),檢測一下是否讀到服務器回覆的消息,若是沒有回覆就顯示錯誤並退出,防止接收不到消息一直等待。
在本機中測試良好;在網絡環境中,我在本機中開啓服務器端程序,同窗在win下用udp小工具做爲客戶端,一樣能夠達到互聯的功能。同窗用的是CMCC,是私有IP地址。(但這都不是問題的關鍵,由於NAT的做用把私有IP地址和端口號都轉變爲公有的IP地址和另外一個端口號)函數
在這篇文章中要重點說書上沒有的東西:(按個人我的理解,因爲還在不斷學習中,共同進步爲原則,寫出來在批判的過程當中一塊兒發現本質)工具
1,socket究竟是什麼?學習
int sockfd; sockfd = socket(AF_INET, SOCK_DGRAM, 0);
sockfd是int類型,但其實它是具備指針功能的(雖然它不是指針),可是在建立socket的同時已經開闢了一段存儲空間,用於存放和此socket綁定的相關信息。雖然在表面上不是指針,可是經過socket號能夠肯定是哪段存儲空間這樣便肯定是哪段信息,和指針功能同樣。
2,在服務器程序中,在37行測試
struct sockaddr_in servaddr, cliaddr;
對servaddr和cliaddr作了定義,可是後面只對servaddr賦值,cliaddr中只是定義時系統賦的初始值,用gdb顯示以下:spa
(gdb) print cliaddr $5 = {sin_family = 45860, sin_port = 42, sin_addr = {s_addr = 2797556}, sin_zero = "U<\026\000Y\207\004\b"}
可是,服務器獲得了客戶端發送的信息而且回覆了客戶端。它到底是如何獲得客戶端的IP地址和端口號的呢?因爲UDP是在傳輸層,不少下層一些的東西都不知道,要想弄明白這個我還須要繼續學習(這也是我要作的),可是如今入門姑且這樣理解,在這個日子mark一下,等發下一篇文章的時候就知道更本質的東西了,也知道我這些天到底有沒有用功啦~~
姑且就這兩個問題是書上沒有解釋的,如今的書都是一大抄,借來一堆書發現內容都同樣,甚至程序都同樣;還有的書程序很亂,編譯都出錯,書名就不提了,《21天。。。》。。。好了,期待本身的下一篇文章,也是下一個階段(雖然我如今還很菜~~)。.net