网络编程3 - UDP套接口编程
UDP程序同样分为服务端server_udp.c和客户端client_udp.c两部分。
两部分的基本步骤如下:
服务端:socket -> bind -> recvfrom -> sendto -> close
客户端:socket -> sendto -> recvfrm -> close
可以看到,UDP编程中没有了TCP编程中的connect(), listen(), accept()。主要通过recvfrom()和sendto()来完成信息的收发。
1.数据发送函数
#include <sys/socket.h>
ssize_t sendto (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *toaddr, socklen_t addrlen);
返回:成功则返回实际发送的字节数,出错返回-1。
参数:sockfd为套接口描述符,buf指向数据发送缓冲区,len为发送数据的长度,flags一般为0,toaddr指向目标套接口,addrlen为套接口数据的长度。
2.数据接收函数
ssize_t recvfrom (int sockfd, void *buf, size_t len, int flags, struct sockaddr *fromaddr, socklen_t *addrlen);
返回:成功则返回实际接收的字节数,出错返回-1。
参数:sockfd为套接口描述符,buf指向数据发送缓冲区,len为发送数据的长度,flags一般为0,fromaddr指向源套接口,addrlen为套接口数据的长度。
备注:sockfd应该是socket file describer的意思,也就是说socket的描述符类似于一个文件描述符,一个socket对应一个文件。socket的创建过程主要是声明协议,网络类型,也就是相当于文件类型。而真正的套接口数据结构sockadddr则包含了套接口的数据源,类似于文件的数据来源——屏幕或键盘等可以传递信息的节点。设定好文件类型和文件的数据来源后,就可以使用write,read,sendto,recvfrom等函数读写文件了。
3.服务端程序
server_udp.c代码如下:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <memory.h>
#define MAX_SIZE 1024
int main(int argc, char *argv[])
{
int PORT;
if( argc!=2 || (argc==2 && (PORT=atoi(argv[1]))<0) )
{
printf("please input the port like 8888");
exit(1);
}
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
perror("socket create error\n");
exit(1);
}
struct sockaddr_in addr;
int addrlen = sizeof(struct sockaddr_in);
bzero(&addr,addrlen);
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(PORT);
if( bind(sockfd,(struct sockaddr*)(&addr),addrlen)<0 )
{
perror("bind error\n");
exit(1);
}
char msg[MAX_SIZE];
int n;
while(1)
{
bzero(msg,MAX_SIZE);
n=recvfrom(sockfd,msg,sizeof(msg),0,(struct sockaddr*)(&addr),&addrlen);
printf("Receive messgae from client:%s",msg);
printf("please input message from server endpoint\n");
fgets(msg,MAX_SIZE,stdin);
printf("Server endpoint input message:%s",msg);
sendto(sockfd,msg,n,0,(struct sockaddr*)(&addr),addrlen);
}
close(sockfd);
return 0;
}
4.客户端程序
client_udp代码如下:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#define MAX_SIZE 1024
int main(int argc, char *argv[])
{
if(3!=argc)
{
printf("please input two parameter: IP and port\n");
exit(1);
}
uint16_t port;
if( (port=atoi(argv[2]))<0 )
{
printf("ensure port > 0\n");
exit(1);
}
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
printf("socket create error\n");
exit(1);
}
int addrlen=sizeof(struct sockaddr_in);
struct sockaddr_in addr;
bzero(&addr,addrlen);
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
if(inet_aton(argv[1],&addr.sin_addr)<0) //IP字符串转换为sin_addr地址
{
printf("IP error\n");
exit(1);
}
char buffer[MAX_SIZE];
int n;
while(1)
{
bzero(buffer,MAX_SIZE);
printf("please input message from client endpoint\n");
fgets(buffer,MAX_SIZE,stdin);
sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr*)(&addr),addrlen);
printf("client endpoint input message:%s\n",buffer);
n=recvfrom(sockfd,buffer,MAX_SIZE,0,(struct sockaddr*)(&addr),&addrlen);
printf("receive message from server:%s\n",buffer);
}
close(sockfd);
return 0;
}
运行过程:
生成server_udp,client_udp两个可执行程序,分别在两个终端中运行。
服务端输入:./server_udp 3456
客户端
输入:./client_dup localhost 3456
输出:please input message from client endpoint
输入:hello. this is msg from client!
输出:client endpoint input message:hello. this is msg from client!
同时,服务端输出:Receive messgae from client:hello. this is msg from client!
please input message from server endpoint
输入:Hi. This is msg from server!!
输出:Server endpoint input message:Hi. This is msg from server!!
...
可以看到,客户端和服务端能够相互发送消息。但是,以客户端为例,不能一边等待消息,一边发送消息。这种行为可能要求用到多线程处理,后面再完善。
UDP程序同样分为服务端server_udp.c和客户端client_udp.c两部分。
两部分的基本步骤如下:
服务端:socket -> bind -> recvfrom -> sendto -> close
客户端:socket -> sendto -> recvfrm -> close
可以看到,UDP编程中没有了TCP编程中的connect(), listen(), accept()。主要通过recvfrom()和sendto()来完成信息的收发。
1.数据发送函数
#include <sys/socket.h>
ssize_t sendto (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *toaddr, socklen_t addrlen);
返回:成功则返回实际发送的字节数,出错返回-1。
参数:sockfd为套接口描述符,buf指向数据发送缓冲区,len为发送数据的长度,flags一般为0,toaddr指向目标套接口,addrlen为套接口数据的长度。
2.数据接收函数
ssize_t recvfrom (int sockfd, void *buf, size_t len, int flags, struct sockaddr *fromaddr, socklen_t *addrlen);
返回:成功则返回实际接收的字节数,出错返回-1。
参数:sockfd为套接口描述符,buf指向数据发送缓冲区,len为发送数据的长度,flags一般为0,fromaddr指向源套接口,addrlen为套接口数据的长度。
备注:sockfd应该是socket file describer的意思,也就是说socket的描述符类似于一个文件描述符,一个socket对应一个文件。socket的创建过程主要是声明协议,网络类型,也就是相当于文件类型。而真正的套接口数据结构sockadddr则包含了套接口的数据源,类似于文件的数据来源——屏幕或键盘等可以传递信息的节点。设定好文件类型和文件的数据来源后,就可以使用write,read,sendto,recvfrom等函数读写文件了。
3.服务端程序
server_udp.c代码如下:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <memory.h>
#define MAX_SIZE 1024
int main(int argc, char *argv[])
{
int PORT;
if( argc!=2 || (argc==2 && (PORT=atoi(argv[1]))<0) )
{
printf("please input the port like 8888");
exit(1);
}
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
perror("socket create error\n");
exit(1);
}
struct sockaddr_in addr;
int addrlen = sizeof(struct sockaddr_in);
bzero(&addr,addrlen);
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(PORT);
if( bind(sockfd,(struct sockaddr*)(&addr),addrlen)<0 )
{
perror("bind error\n");
exit(1);
}
char msg[MAX_SIZE];
int n;
while(1)
{
bzero(msg,MAX_SIZE);
n=recvfrom(sockfd,msg,sizeof(msg),0,(struct sockaddr*)(&addr),&addrlen);
printf("Receive messgae from client:%s",msg);
printf("please input message from server endpoint\n");
fgets(msg,MAX_SIZE,stdin);
printf("Server endpoint input message:%s",msg);
sendto(sockfd,msg,n,0,(struct sockaddr*)(&addr),addrlen);
}
close(sockfd);
return 0;
}
4.客户端程序
client_udp代码如下:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#define MAX_SIZE 1024
int main(int argc, char *argv[])
{
if(3!=argc)
{
printf("please input two parameter: IP and port\n");
exit(1);
}
uint16_t port;
if( (port=atoi(argv[2]))<0 )
{
printf("ensure port > 0\n");
exit(1);
}
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
printf("socket create error\n");
exit(1);
}
int addrlen=sizeof(struct sockaddr_in);
struct sockaddr_in addr;
bzero(&addr,addrlen);
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
if(inet_aton(argv[1],&addr.sin_addr)<0) //IP字符串转换为sin_addr地址
{
printf("IP error\n");
exit(1);
}
char buffer[MAX_SIZE];
int n;
while(1)
{
bzero(buffer,MAX_SIZE);
printf("please input message from client endpoint\n");
fgets(buffer,MAX_SIZE,stdin);
sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr*)(&addr),addrlen);
printf("client endpoint input message:%s\n",buffer);
n=recvfrom(sockfd,buffer,MAX_SIZE,0,(struct sockaddr*)(&addr),&addrlen);
printf("receive message from server:%s\n",buffer);
}
close(sockfd);
return 0;
}
运行过程:
生成server_udp,client_udp两个可执行程序,分别在两个终端中运行。
服务端输入:./server_udp 3456
客户端
输入:./client_dup localhost 3456
输出:please input message from client endpoint
输入:hello. this is msg from client!
输出:client endpoint input message:hello. this is msg from client!
同时,服务端输出:Receive messgae from client:hello. this is msg from client!
please input message from server endpoint
输入:Hi. This is msg from server!!
输出:Server endpoint input message:Hi. This is msg from server!!
...
可以看到,客户端和服务端能够相互发送消息。但是,以客户端为例,不能一边等待消息,一边发送消息。这种行为可能要求用到多线程处理,后面再完善。