UDP協議實現聊天小程式
阿新 • • 發佈:2018-11-01
今天我們用之前講解過的UDP協議來寫一個最基礎,最簡單的網路聊天程式。
//我們通過udp協議來實現一個簡單的網路聊天程式 //這是客戶端的實現 //過程: // 1.建立套接字 // 2.繫結地址資訊 // 3.向服務端傳送資料 // 4.接受服務端傳送的資料 // 5.關閉socket #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<errno.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> int main() { int sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); //socket函式第一個引數為地址型別,目前只支援apra型別的地址 //第二個引數是套接字型別 如果是SOCK_DGRAM 面向資料報的套接字 //如果是SOCK_STREAM則是面向資料流的套接字 不 //第三個引數是用來指定具體協議,不想指定可以省略為0 預設資料報套接字為udp 資料流為tcp //這裡我們指定了IP協議族中的UDP協議。 if(sockfd<0) { perror("socket error\n"); return -1; } //繫結地址資訊,不過在客戶端的時候我們通常不繫結 //因為繫結的話可能會因為一些特殊情況失敗,但是 //客戶端傳送資料的時候使用哪個地址和埠都無所謂 //只要資料能夠傳送成功就可以,所以客戶端時我們一般 //不推薦繫結埠,這樣在傳送資料的時候,作業系統檢測 //到socket還沒有繫結地址,就會自動給他繫結一個這樣 //繫結方式不會出錯。 // struct sockaddr_in cli_addr; //sockaddr_in 是一個結構體解決了sockaddr的缺陷 //裡邊有四個成員變數分別是: //sin_family 儲存地址族 //sin_port 16位TCP/UDP埠號 //sin_addr 32位IP地址 //sin_zeor[8] 我們不會使用到 cli_addr.sin_family=AF_INET; cli_addr.sin_port=htons(9000); cli_addr.sin_addr.s_addr=inet_addr("192.168.76.130"); socklen_t len=sizeof(struct sockaddr_in); while(1) { //3.傳送資料 char buff[1024]={0}; scanf("%s",buff); sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&cli_addr,len); //sendto 指向一指定的目的地傳送資料 //第一個引數是我們套接字操作符 //第二個引數是要傳送的資料 //第三個引數傳送的資料長度 //第四個一般為0,如果緩衝區沒有資料那麼阻塞等待 //第五個指標指向目的套接字的地址 //第六個引數是第五個指標指向的地址長度 // //4.接受來自服務端的資料 memset(buff,0x00,1024);//把緩衝區變成0以防剛剛的資料影響現在新接受的資料 ssize_t r_len=recvfrom(sockfd,buff,1023,0,(struct sockaddr*)&cli_addr,&len); //recvfrom 是通過sock來接受資料的函式 //引數和上邊的相同不過這裡第六個引數也是指標指向緩衝區的長度 //如果成功返回的是接受的位元組數,失敗返回-1 if(r_len<0) { perror("recvfrom error\n"); return 0; } printf("server say:%s\n",buff); } close(sockfd); return 0; }
這是客戶端的程式,服務端和客戶端沒有什麼區別,不過要注意一個是先發資料,一個是先收資料,不然兩個程式會同時阻塞卡住。
//這個是UDP協議聊天程式的服務端 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<errno.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> int main() { int sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); if(sockfd<0) { perror("socket error\n"); return -1; } struct sockaddr_in ser_addr; ser_addr.sin_family=AF_INET; ser_addr.sin_port=htons(9000); ser_addr.sin_addr.s_addr=inet_addr("192.168.76.130"); socklen_t len=sizeof(struct sockaddr_in); int ret=bind(sockfd,(struct sockaddr*)&ser_addr,len); //bind 函式將所建立的套接字繫結到一個地址並且賦予一個埠號 //第一個引數是套接字描述符, //第二個引數為要繫結的地址資訊 //第三個引數為地址資訊的長度。 //返回-1代表繫結失敗 if(ret<0) { perror("bind error\n"); close(sockfd); return -1; } while(1) { //接受資料 char buff[1024]={0}; struct sockaddr_in cli_addr; len=sizeof(struct sockaddr_in); ssize_t rlen=recvfrom(sockfd,buff,1023,0,(struct sockaddr*)&cli_addr,&len); if(rlen<0) { perror("recvfrom error\n"); close(sockfd); return -1; } printf("client:%s %d say:%s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buff); memset(buff,0x00,1024); scanf("%s",buff); sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&cli_addr,len); } close(sockfd); return -1; }
一定是要弄明白哪個先收哪個先發, 不然可能會出現發了那邊收不到,只有另一邊也發的時候才能收到上一個對方傳送的資料。
這裡我們還在服務端輸出了,與他對話的客戶端的ip地址和埠號