基于UDP的服务器端/客户端---网络编程(Linux--C)
1、基于UDP的数据I/O函数
函数:#include
ssize_t sendto(int sock,void *buff,size_t nbytes,int flags,struct sockaddr*to,socklen_t addrlen);
成功返回传输的字节数,失败时返回-1。
参数:
ssize_t recvfrom(int sock,void *buff,size_t nbytes,int flags,struct sockaddr*from,socklen_t *addrlen);
成功时返回接收的字节数,失败时返回-1。
参数:
uecho_server.c代码:
#include#include #include #include #include #include #define BUF_SIZE 30 void error_handling(char *message); int main(int argc,char *argv[]) { int serv_sock; char message[BUF_SIZE]; int str_len; socklen_t clnt_adr_sz; struct sockaddr_in serv_adr,clnt_adr; if(argc!=2) { printf("Usage:%s \n",argv[0]); exit(1); } serv_sock=socket(PF_INET,SOCK_DGRAM,0); if(serv_sock==-1) error_handling("UDP socket creation error"); memset(&serv_adr,0,sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=htonl(INADDR_ANY); serv_adr.sin_port=htons(atoi(argv[1])); if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1) error_handling("bind() error"); while(1) { clnt_adr_sz=sizeof(clnt_adr); str_len=recvfrom(serv_sock,message,BUF_SIZE,0,(struct sockaddr*)&clnt_adr,&clnt_adr_sz); sendto(serv_sock,message,str_len,0,(struct sockaddr*)&clnt_adr,clnt_adr_sz); } close(serv_sock); return 0; } void error_handling(char *message) { fputs(message,stderr); fputc('\n',stderr); exit(1); } uecho_client.c代码: #include#include #include #include #include #include #define BUF_SIZE 30 void error_handling(char *message); int main(int argc,char *argv[]) { int sock; char message[BUF_SIZE]; int str_len; socklen_t adr_sz; struct sockaddr_in serv_adr,from_adr; if(argc!=3) { printf("Usage:%s \n",argv[0]); exit(1); } sock=socket(PF_INET,SOCK_DGRAM,0); if(sock==-1) error_handling("socket() error"); memset(&serv_adr,0,sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=inet_addr(argv[1]); serv_adr.sin_port=htons(atoi(argv[2])); while(1) { fputs("Insert message(q to quit):",stdout); fgets(message,sizeof(message),stdin); if(!strcmp(message,"q\n")||!strcmp(message,"Q\n")) break; sendto(sock,message,strlen(message),0,(struct sockaddr*)&serv_adr,sizeof(serv_adr)); adr_sz=sizeof(from_adr); str_len=recvfrom(sock,message,BUF_SIZE,0,(struct sockaddr*)&from_adr,&adr_sz); message[str_len]=0; printf("Message from server:%s",message); } close(sock); return 0; } void error_handling(char *message) { fputs(message,stderr); fputc('\n',stderr); exit(1); }
(2)UDP客户端套接字的地址分配
UDP程序中,调用sendto函数传输数据前应完成对套接字的地址分配工作,因此调用bind函数。
(3)UDP的数据传输特性和调用connect函数
UDP是具有数据边界的协议,传输中调用I/O函数的次数非常重要。因此,输入函数的调用次数应和输出函数的调用
次数完全一致,这样才能保证接收全部已发送数据。
bound_host1.c代码:
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc,char *argv[])
{
int sock;
char message[BUF_SIZE];
struct sockaddr_in my_adr,your_adr;
socklen_t adr_sz;
int str_len,i;
if(argc!=2)
{
printf("Usage:%s\n",argv[0]);
exit(1);
}
sock=socket(PF_INET,SOCK_DGRAM,0);
if(sock==-1)
error_handling("socket() error");
memset(&my_adr,0,sizeof(my_adr));
my_adr.sin_family=AF_INET;
my_adr.sin_addr.s_addr=htonl(INADDR_ANY);
my_adr.sin_port=htons(atoi(argv[1]));
if(bind(sock,(struct sockaddr*)&my_adr,sizeof(my_adr))==-1)
error_handling("bind()error");
for(i=0;i<3;i++)
{
sleep(5); //delay 5 sec
adr_sz=sizeof(your_adr);
str_len=recvfrom(sock,message,BUF_SIZE,0,(struct sockaddr*)&your_adr,&adr_sz);
printf("Message %d:%s\n",i+1,message);
}
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
bound_host2.c代码:
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc,char *argv[])
{
int sock;
char msg1[]="Hi!";
char msg2[]="I'm another UDP host!";
char msg3[]="Nice to meet you";
struct sockaddr_in your_adr;
socklen_t your_adr_sz;
if(argc!=3)
{
printf("Usage:%s\n",argv[0]);
exit(1);
}
sock=socket(PF_INET,SOCK_DGRAM,0);
if(sock==-1)
error_handling("socket() error");
memset(&your_adr,0,sizeof(your_adr));
your_adr.sin_family=AF_INET;
your_adr.sin_addr.s_addr=inet_addr(argv[1]);
your_adr.sin_port=htons(atoi(argv[2]));
sendto(sock,msg1,sizeof(msg1),0,(struct sockaddr*)&your_adr,sizeof(your_adr));
sendto(sock,msg2,sizeof(msg2),0,(struct sockaddr*)&your_adr,sizeof(your_adr));
sendto(sock,msg3,sizeof(msg3),0,(struct sockaddr*)&your_adr,sizeof(your_adr));
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
(4)已连接UDP套接字与未连接UDP套接字
TCP套接字中需注册待传输数据的目标IP和端口号,而UDP中则无需注册。因此,通过sendto函数传输数据的过
程大致分为以下3个阶段:
创建已连接UDP套接字
创建已连接UDP套接字的过程比较简单,只需针对UDP套接字调用connect函数。
sock=socket(PF_INET,SOCK_DGRAM,0);
memset(&adr,0,sizeof(adr));
adr.sin_family=AF_INET;
adr.sin_addr.s_addr=.....
adr.sin_port=....
connect(sock,(struct sockaddr*)&adr,sizeof(adr));
下列将之前的uecho_client.c程序改成基于已连接UDP套接字的程序
uecho_con_client.c代码:
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc,char *argv[])
{
int sock;
char message[BUF_SIZE];
int str_len;
struct sockaddr_in serv_adr;
if(argc!=3)
{
printf("Usage:%s\n",argv[0]);
exit(1);
}
sock=socket(PF_INET,SOCK_DGRAM,0);
if(sock==-1)
error_handling("socket() error");
memset(&serv_adr,0,sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
serv_adr.sin_port=htons(atoi(argv[2]));
connect(sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr));
while(1)
{
fputs("Insert message(q to quit):",stdout);
fgets(message,sizeof(message),stdin);
if(!strcmp(message,"q\n")||!strcmp(message,"Q\n"))
break;
write(sock,message,strlen(message));
str_len=read(sock,message,sizeof(message)-1);
message[str_len]=0;
printf("Message from server:%s",message);
}
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
代码中用write、read函数代替了sendto、recvfrom函数。