1、数据报socket和流socket的不同
socket有两种不同的选择,一种是基于流的socketTCP,它是用来连接不相关的进程的,另一种被称为数据报socketUDP。流socket传送数据就跟电话网中传送声音一样,客户先建立连接,然后使用该连接进行单向、双向或类似管道的字节流传送。数据报通信则与从一个邮箱到另一个邮箱发送包裹类似,客户不必建立连接,只要向特定的地址发送消息,而服务器进程在该地址接收消息。流socket使用的网络协议叫TCP即传输控制协议(Transmission Control Protocol),数据报socket叫UDP即用户数据报协议(User Datagram Protocol)。
流socket对传送负责,数据报socket则不。流socket的接收端检查数据片的顺序来确保数据完整到达,接收端提示发送端丢失的数据片编号,并等待丢失数据片的重传。数据报socket并不检查丢失数据报,也不要求重传,如果丢失则不可达。UDP相比于TCP更快更简单,给网络较少的负荷。
2、数据报编程
数据报编程主要涉及两个系统调用sendto和recvfrom:
- sendto():从数据报socket发送消息,nchars=sendto(int socket,const void *msg,size_t len,int flags,const struct sockaddr* dest,socklen_t dest_len),socket是socket id标识,msg是发送的字符类型数组,len是发送的字符数,flags是比特的集合设置发送属性0表示普通,dest指向远端socket地址的指针,dest_len远端地址长度,最后返回实际发送的字符数。sendto从源socket发送数据报到目的socket。前3个参数和write参数类似:发送的socket、发送字符串数组、发送字符数,返回实际发送的字符数。flags参数表明发送的各种属性。最后两个参数给出发送目的地的socket地址,如果是Internet类型地址包含目的主机IP和端口号。
- recvfrom():从数据报socket接收信息,nchars=recvfrom(int socket,const void *msg,size_t len,int flags,const struct sockaddr* sender,socklen_t *sender_len),socket是socket id标识,msg是存放字符类型数组,len是接收的字符数,flags是接收属性的比特的集合0表示普通,sender指向远端socket地址的指针,sender_len远端地址长度,最后返回接收的字符数。recvfrom从socket读取数据报。前3个参数和read参数类似:读取的socket、存放字符串数组、读取的字符数,返回实际接收的字符数。flags参数指出了接收时所用的各种属性。最后两个参数可以获得发送者的socket地址,地址长度必须提供给recvfrom,如果为空,发送者地址不被记录。
3、编写程序代码
dgrecv.c
简单的数据报socket例子的服务器
#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define oops(m,x) {perror(m);exit(x);}
#define HOSTLEN 256
int make_dgram_server_socket(int portnum)
{
struct sockaddr_in saddr;
char hostname[HOSTLEN];
struct hostent *hp;
int sock_id;
sock_id=socket(PF_INET,SOCK_DGRAM,0);
if(sock_id==-1)
oops("socket",1);
bzero((void*)&saddr,sizeof(saddr));
gethostname(hostname,HOSTLEN);
hp=gethostbyname(hostname);
bcopy((void*)hp->h_addr,(void*)&saddr.sin_addr,hp->h_length);
saddr.sin_port=htons(portnum);
saddr.sin_family=AF_INET;
if(bind(sock_id,(struct sockaddr *)&saddr,sizeof(saddr))!=0)
oops("bind",2);
return sock_id;
}
void say_who_called(struct sockaddr_in *addrp)
{
char host[BUFSIZ];
int port;
strncpy(host,inet_ntoa(addrp->sin_addr),BUFSIZ);
port=ntohs(addrp->sin_port);
printf("from:%s:%d\n",host,port);
}
int main(int argc,char *argv[])
{
int port;
int sock;
char buf[BUFSIZ];
size_t msglen;
struct sockaddr_in saddr;
socklen_t saddrlen=sizeof(saddr);
if(argc!=2 || (port=atoi(argv[1]))<=0)
{
fprintf(stderr,"usage:dgrecv portnum\n");
exit(1);
}
if((sock=make_dgram_server_socket(port))==-1)
oops("cannot make sock",3);
while((msglen=recvfrom(sock,buf,BUFSIZ,0,(struct sockaddr *)&saddr,&saddrlen))>0)
{
buf[msglen]='\0';
printf("dgrecv:got a message:%s\n",buf);
say_who_called(&saddr);
}
return 0;
}
dgsend.c
简单的数据报socket例子的客户端
#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define oops(m,x) {perror(m);exit(x);}
#define HOSTLEN 256
int make_dgram_client_socket(char *host,int portnum,struct sockaddr_in *servadd)
{
struct hostent *hp;
int sock_id;
sock_id=socket(PF_INET,SOCK_DGRAM,0);
if(sock_id==-1)
oops("socket",1);
bzero((void *)servadd,sizeof(struct sockaddr_in));
hp=gethostbyname(host);
if(hp==NULL)
oops(host,2);
bcopy((void*)hp->h_addr,(void*)&servadd->sin_addr,hp->h_length);
servadd->sin_port=htons(portnum);
servadd->sin_family=AF_INET;
return sock_id;
}
int main(int argc,char *argv[])
{
int sock;
char *msg;
struct sockaddr_in saddr;
if(argc!=4)
{
fprintf(stderr,"usage:dgsend host portnum message\n");
exit(1);
}
if((sock=make_dgram_client_socket(argv[1],atoi(argv[2]),&saddr))==-1)
oops("cannot make socket",2);
if(sendto(sock,argv[3],sizeof(argv[3]),0,(const struct sockaddr *)&saddr,sizeof(saddr))==-1)
oops("sendto",3);
return 0;
}
dgrecv2.c
带回复的数据报socket例子的服务器
#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define oops(m,x) {perror(m);exit(x);}
#define HOSTLEN 256
int make_dgram_server_socket(int portnum)
{
struct sockaddr_in saddr;
char hostname[HOSTLEN];
struct hostent *hp;
int sock_id;
sock_id=socket(PF_INET,SOCK_DGRAM,0);
if(sock_id==-1)
oops("socket",1);
bzero((void*)&saddr,sizeof(saddr));
gethostname(hostname,HOSTLEN);
hp=gethostbyname(hostname);
bcopy((void*)hp->h_addr,(void*)&saddr.sin_addr,hp->h_length);
saddr.sin_port=htons(portnum);
saddr.sin_family=AF_INET;
if(bind(sock_id,(struct sockaddr *)&saddr,sizeof(saddr))!=0)
oops("bind",2);
return sock_id;
}
void say_who_called(struct sockaddr_in *addrp)
{
char host[BUFSIZ];
int port;
strncpy(host,inet_ntoa(addrp->sin_addr),BUFSIZ);
port=ntohs(addrp->sin_port);
printf("from:%s:%d\n",host,port);
}
void reply_to_sender(int sock,char *msg,struct sockaddr_in *addrp,socklen_t len)
{
char reply[BUFSIZ+BUFSIZ];
sprintf(reply,"Thanks for your %d char message\n",strlen(msg));
if(sendto(sock,reply,strlen(reply),0,addrp,len)==-1)
oops("sendto",4);
}
int main(int argc,char *argv[])
{
int port;
int sock;
char buf[BUFSIZ];
size_t msglen;
struct sockaddr_in saddr;
socklen_t saddrlen;
if(argc!=2 || (port=atoi(argv[1]))<=0)
{
fprintf(stderr,"usage:dgrecv portnum\n");
exit(1);
}
if((sock=make_dgram_server_socket(port))==-1)
oops("cannot make sock",3);
saddrlen=sizeof(saddr);
while((msglen=recvfrom(sock,buf,BUFSIZ,0,(struct sockaddr *)&saddr,&saddrlen))>0)
{
buf[msglen]='\0';
printf("dgrecv: got a message: %s\n",buf);
say_who_called(&saddr);
reply_to_sender(sock,buf,&saddr,saddrlen);
}
return 0;
}
dgsend2.c
带回复的数据报socket例子的客户端
#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define oops(m,x) {perror(m);exit(x);}
#define HOSTLEN 256
int make_dgram_client_socket(char *host,int portnum,struct sockaddr_in *servadd)
{
struct hostent *hp;
int sock_id;
sock_id=socket(PF_INET,SOCK_DGRAM,0);
if(sock_id==-1)
oops("socket",1);
bzero((void *)servadd,sizeof(struct sockaddr_in));
hp=gethostbyname(host);
if(hp==NULL)
oops(host,2);
bcopy((void*)hp->h_addr,(void*)&servadd->sin_addr,hp->h_length);
servadd->sin_port=htons(portnum);
servadd->sin_family=AF_INET;
return sock_id;
}
int main(int argc,char *argv[])
{
int sock;
char *msg;
char buf[BUFSIZ];
size_t msglen;
struct sockaddr_in saddr;
if(argc!=4)
{
fprintf(stderr,"usage:dgsend host portnum message\n");
exit(1);
}
if((sock=make_dgram_client_socket(argv[1],atoi(argv[2]),&saddr))==-1)
oops("cannot make socket",2);
if(sendto(sock,argv[3],sizeof(argv[3]),0,(const struct sockaddr *)&saddr,sizeof(saddr))==-1)
oops("sendto",3);
msglen = recvfrom(sock,buf,BUFSIZ,0,NULL,NULL);
buf[msglen] = '\0';
printf("dgsend: got a message: %s\n", buf);
return 0;
}