Linux 網絡編程之UDP

1.介紹

UDP協議是無連接的,不可靠傳輸的協議. 服務器與客戶端的交互不需要建立連接,沒有流量控制的功能。與TCP一樣,它也是傳輸層協議,通信過程中需要IP地址與端口號。使用UDP進行程序設計包括服務器與客戶端,下面介紹一下服務器與客戶端的通信流程:

服務器流程:

(1)建立服務器套接字描socket

(2)將地址結構綁定到套接字上 bind

(3)數據傳輸 sendto/recvfrom (不需要連接,所心沒有監聽)

(4)關閉套接字close

客戶端流程:

(1)建立客戶端套接字 socket

(2)設置服務器端口

(3)數據傳輸 sendto/recvfrom

(4)關閉套接字close

可以看出,與TCP通信相比,UDP服務器沒有監聽端口與等待連接的過程,而客戶端沒有建立連接的過程.

2. 相關函數

int socket(int domain,int type,int protocol);

type: SOCK_DGRAM 數據報

#include <sys/types.h>

#include <sys/socket.h>

ssize_t recvfrom(int s,void*buf,size_t len,int flags,struct sockaddr* from,socklen_t *fromlen);

參數:

s-套接字描述符

buf-接收數據的緩衝區大小

len-接收數據的緩衝區長度

flags-接收數據的標誌

from-客戶端或者是服務器的地址

fromlen-客戶端或者是服務器的地址長度指針

返回值:

成功返回接收的字節數,發生錯誤時返回-1.

#include <sys/types.h>

#include <sys/socket.h>

ssize_t sendto(int s,const void*buf,size_t len,int flags,const struct sockaddr*to,socklen_t tolen);

參數:

s-套接字描述符

buf-發送緩衝區指針,const類型,由於發送的時候已經把數據寫入buf了.

len-發送數據的實際長度

flags-發送數據的標誌

to-發送的目的地址結構

tolen-目的地址結構長度

返回值:

成功返回發送的字節數,失敗返回-1. 返回0也是合法的表示沒有接收到數據。


3. UDP實例

服務器:

#include <sys/types.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
與TCP程序設計相比,UDP缺少了connect(),listen(),accept()函數,這是由於UDP無連接的特性決定的
UDP服務器端:
(1)建立數據報套接字socket
(2)綁定服務器的IP地址與端口號到套接字bind
(3)接收客戶端的數據recvfrom
(4)向客戶端發送數據sendto
(5)關閉套接字close()
recvfrom/sendto函數的介紹:
ssize_t recvfrom(int s,void*buf,size_t len,int flags,struct sockaddr* from,socklen_t *fromlen);
第1個參數表示套接字描述符
第2個參數表示接收數據的緩衝區
第3個參數表示接收緩衝區的長度
第4個參數發送標誌,MSG_DONTWAIT
第5個參數表示發送方的地址信息 struct sockaddr*
第6個參數表示發送方的地址長度sizeof(struct sockaddr_in)
返回值:
成功返回接收的字節數,返回0表示接收的字節數爲0,返回-1表示發生錯誤,錯誤碼放在errno
ssize_t sendto(int s,const void*buf,size_t len,int flags,const struct sockaddr* to,socklen_t tolen);
第1個參數表示套接字描述符
第2個參數表示發送緩衝區的指針
第3個參數表示發送緩衝區的大小
第4個參數是標誌
第5個參數表示目的主機的sockaddr_in指針
第6個參數表示目的主機的sizeof(struct sockaddr_in);
返回值:
成功返回已經發送的數據長度,數據長度可以爲0
-1表示發生錯誤,錯誤碼保存在errno
**/
#define PORT 8888
int main(int agrc,char*argv[]){
 int s;//定義服務器端的套接字描述符
 struct sockaddr_in server_addr,client_addr;//服務器地址結構與收到的客戶端地址結構
 int len;
 char buffer[1024];
 s=socket(AF_INET,SOCK_DGRAM,0);//建立一個數據報套接字
 if(s<0){
    perror("socket error");
    return;
}

memset(&server_addr,0,sizeof(server_addr));//將地址結構清0
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//任意地址,主機字節序轉換爲網絡字節序
int ret=bind(s,(struct sockaddr*)&server_addr,sizeof(server_addr));//將IP地址與端口號綁定到套接字上,表示服務器用此端口號進行數據的接收與發送
if(ret<0){
    perror("bind error");
    return;
}

//利用for循環進行數據的發送與接收

for(;;){
 bzero(buffer,1024);
 len=sizeof(client_addr);
 //接收數據,並將客戶端的地址信息保存到client_addr
 int rec=recvfrom(s,buffer,1024,0,(struct sockaddr*)&client_addr,&len);
 printf("%d\n",rec);
 printf("server:%s\n",buffer);
 if(ret==-1){
    perror("recv error");
    return;
}
sendto(s,buffer,rec,0,(struct sockaddr*)&client_addr,len);//將數據從服務器端發送給客戶端client_addr表示目我地址信息,len指目我地址長度信息
//client_addr保存着客戶
char *addr;
addr=(char*)inet_ntoa(client_addr.sin_addr);//返回值爲一靜態內存的指針,全局的,線程不安全,是不可重入的
// char addr[16];
// inet_ntop(AF_INET,(void*)&client_addr.sin_addr,addr,16);//協議族,in_addr指針,字符數組,數組長度
printf("client IP is:%s\n",addr);
int port=ntohs(client_addr.sin_port);//輸出客戶端的所使用的端口號
printf("Port is:%d\n",port);

}

close(s);
return 0;

}

客戶端:

#include <sys/types.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define PORT 8888
/**
UDP客戶端:
(1)建立套接字描述符
(2)向服務器發送數據sendto
(3)接收服務器發送的數據recvfrom
(4)關閉套接字close()
**/

int main(int argc,char*argv[]){
  int s;
  struct sockaddr_in server_addr,client_addr;
  s=socket(AF_INET,SOCK_DGRAM,0);//建立數據報套接字
 if(s<0){
    perror("socket error");
    return;
}

memset(&server_addr,0,sizeof(server_addr));//將地址結構清0
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//首先向服務器發送數據
char buffer[1024];
int size;
int len=sizeof(server_addr);
bzero(buffer,1024);
for(;;){
   size=read(0,buffer,1024);//從標準輸入讀到的數據
   if(size>0){//向服務器發送
    struct sockaddr_in from;
    sendto(s,buffer,size,0,(struct sockaddr*)&server_addr,len);//第3個參數是目標地址,size表示發送的數據長度
     bzero(buffer,1024);
    int recvsize=recvfrom(s,buffer,1024,0,(struct sockaddr*)&from,&len);//從服務器接收數據,from表示服務器端的地址信息
    //struct sockaddr* from代表服務器端的信息,打印服務器端的IP地址與端口號
    char *ip=(char*)inet_ntoa(from.sin_addr);
    printf("Server IP:%s\n",ip);
    short port=ntohs(from.sin_port);
    printf("Server Port:%d\n",port);
    if(recvsize>0){
        printf("recevied:%s",buffer);
    }
}

}
}

運行結果:

[root@localhost ~]# ./udp-s
6
server:hello

client IP is:127.0.0.1
Port is:46239

[root@localhost ~]# ./udp-c
hello
Server IP:127.0.0.1
Server Port:8888
recevied:hello

總結:

本文主要介紹了Linux UDP編程的基本流程,最後給出一個UDP程序設計的例子。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章