linux 网络编程 udp篇
2010-09-24 22:14
573 查看
使用TCP通信的过程:
客户端: socket connect write read close
服务端: socket bind listen accept read write
使用udp通信的过程:
服务端:socket bind recvfrom/sendto
客户端:socket recvfrom/sendto
Socket 网络编程用到的函数
1. Socket系统调用: int socket(int domain,int type,int protocol);
包含头文件:
#include <sys/types.h>
#incldue <sys/socket.h>
参数domain用来指定发生通信的域,可能的值有(常量PF_XXXX也可以定义为AF_XXXX):
PF_LOCAL:用于本地通信的UNIX协议族。 PF_UNIX
PF_INET:IPV4互联网协议族。
PF_ISO:ISO协议族。
PF_CCITT:ITU-T协议,类似X.25。
PF_NS:XEROX网络系统协议。
PF_IPX:IPX—Novell协议族。
PF_X25:ITU-T X.25 ISO-8208协议族。
参数type用来指明通信的类型,现在使用的定义有如下几种:
SOCK_STREAM:提供顺序 可靠和基于字节流的双向连接。支持带外数据传输(out-of-band),对于带外传输,要求发送方在发送缓冲区中的数据之前发送带外数据,接收方在处理缓冲区之前处理带外数据。全双工的字节流,一个流类型的socket必须连接后才能传递数据。与另一个socket的连接由connect 连接而成
SOCK_DGRAM:支持数据报通信(无连接 不可靠 固定的信息最大长度)。
SOCK_SEQPACKET:提供原始网络协议访问。
SOCK_RAW:提供原始网络访问。
SOCK_RDM:提供一个可靠的数据报层,但不保证报文到达的顺序。
参数protoco:
采用IDPROTO_UDP,和IDPROTO_TCP。
调用出错返回:
EPROTONOSUPPORT:在该通信域中不支持该类型或不支持指定的协议。
ENFILE:没有足够核心内存分配给新socket结构。
EMFILE:进程描述符表溢出。
2. bind: int bind(int s,struct sockaddr *my_addr,int addrlen); 该调用将一个名字绑定到一个socket上。
包含头文件:
#include <sys/types.h>
#incldue <sys/socket.h>
bind为一个未命名的socket分配一个名字。换句话说,bind调用是给套接字s赋予本地的地址my_addr,这个my_addr的地址长度为addrlen,对于不同通信域联编的规则是不同的。可以将sockaddr_in的成员sin_addr设置为IDADDR_ANY,接收任何端口的报文。
3. recv和recvfrom调用
系统调用recv和recvfrom用来从一个socket接收消息。该调用的声明格式如下:
int recv(int s,void *buf,int len,unsigned int flags);
int recvfrom(int s,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
包含头文件:
#include <sys/types.h>
#incldue <sys/socket.h>
s为要接收的socket描述符。参数buf指向接收数据后存放的用户缓冲区的地址。参数len指出用户缓冲区的大小。recvfrom如果参数from为空,则该socket不是面向连接的,则返回时from存放的是该消息的源地址。fromlen指出from的实际大小。如果消息太长,缓冲区不能完全存放,可能丢弃,但视消息的类型而定。MSG_NOSIGNAL,即即使对方故不能比这个socket连接也不会产生SIGPIPE信号。MSG_ERRQUEUE,从错误队列中接收报文。与平台有关。
参数flags,MSG_PEEK该标志使得接收操作返回接收队列头部数据,并且不将数据从队列中移出。MSG_WAITALL,请求将被阻塞,直到请求数据量满足为止。
4. send和sendto调用
send只用于处于连接状态的socket,而sengto不需要socket处于连接状态。
如果不需要特殊的俄设置,flags直接赋值0,否者MSG_NOSIGNAL,当一端断开连接,基于流的socket会产生错误,并发送SIGPIPE给send或sengto进程,仍会返回错误代码EPIPE。
NSG_DONTWAIT,允许非阻塞操作。
5. shutdown调用关闭一个全双工连接的一端。
int shutdown(int s,int how);
成功返回0,否者返回-1。可能错误代码有EBADF参数s不是一个有效的描述符。ENOTSOCK参数s是一个文件描述符,而不是socket描述符。ENOTCONN:指定的socket没有处于连接状态。
。
7. listen调用
该调用在一个socket侦听连接。int listen(int s,int backlog)
指定接受连接的队列长度限制,然后由accept调用来接受连接请求。该调用只能在SOCK_STREAM或SOCK_SEQPACKET类型的socket上。参数backlog指定队列的最大长度。
8. accept调用
该调用在制定的socket上接受一个连接请求。 int accept(int s,struct sockaddr *addr,int *addrlen)
如果socket被设定为非阻塞,返回错误。如果调失败,可能设置的俄错误代码 EBADF EMFILE进程描述符表满 ENFILE指定socket类型不是sock_stream EOPENOTSUPP参数addr不再用户可写的地址空间内 EFAULT EWOULDBLOCK:socket被设置为非阻塞方式且没有连接请求。
9. gethostbyname调用
包含头文件:
#include <netdb.h>
extern int h_errno;
struct hostent *gethostbyname(const char *name);
strcut hostent *gethostbyaddr(const char *addr,int len,int type);
void sethostent(int stayopen);
void endhostent(void);
hostent结构如下:
struct hostent{
char *h_name;// 主机的正式名字
char **h_aliases;//主机的别名类表
int h_addrtype;//主机地址类型
int h_length;//地址长度
char **h_addr_list;//地址列表
}
10. 复制二进制数据函数:
void bzero(void *s,int n);
void bcopy(const void *src,void *dest,int n);
void memcpy(void *dest,const void *src,size_t n);
11. getprotoent函数调用:得到登记协议。
包含头文件:
#include <netdb.h>
struct protoent *getprotoent(void);
struct protoent *getprotbyname(const char *name);
struct protoent *getprotobynumber(int proto);
void setprotoent(int stayopen);
void endprotoent(void);
12. inet_addr等函数的调用
int inet_aton(const char *cp,struct in_addr *inp);//将internet的标准主机地址 转换成二进制数据格式
unsigned long int inet_addr(const char *cp);//将字符串形式 主机地址转换成网络字节顺序 二进制
unsigned long int inet_network(const char *cp);//从地址cp中取出主机字节顺序的
char *inet_ntoa(struct in_addrin);//将internet的标准主机地址 转换成字符串
struct in_addr inet_makeaddr(int net,int host);//将net所指向的网络地址和参数host所指的在net网络中的本地地址经过运算结合,得到一个网络字节顺序的internet主机地址。
unsigned long int inet_lnaof(struct in_addr_in);//以本地主机字节顺序返回本地主机在本子网中的地址
unsigned long int inet_netof(struct in_addr_in);//返回参数in所指的internet地址中的网络号
ushort ntohs(ushort);//将网络字节 的数据转换成主机字节顺序数据 例如端口号转换时使用
附带一个linux下udp通信的例子(客户端发送数据,服务端接收并发回去,接收端再显示出来),下次写一个基于tcp的并且用到尽可能多的函数:
服务端程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int sock;
//sendto中使用的对方地址
struct sockaddr_in toAddr;
//在recvfrom中使用的对方主机地址
struct sockaddr_in fromAddr;
int recvLen;
unsigned int addrLen;
char recvBuffer[128];
sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(sock < 0)
{
printf("创建套接字失败了./r/n");
exit(0);
}
memset(&fromAddr,0,sizeof(fromAddr));
fromAddr.sin_family=AF_INET;
fromAddr.sin_addr.s_addr=htonl(INADDR_ANY);
fromAddr.sin_port = htons(4000);
if(bind(sock,(struct sockaddr*)&fromAddr,sizeof(fromAddr))<0)
{
printf("bind() 函数使用失败了./r/n");
close(sock);
exit(1);
}
while(1){
addrLen = sizeof(toAddr);
if((recvLen = recvfrom(sock,recvBuffer,128,0,(struct sockaddr*)&toAddr,&addrLen))<0)
{
printf("()recvfrom()函数使用失败了./r/n");
close(sock);
exit(1);
}
printf("%d/n",recvLen);
if(sendto(sock,recvBuffer,recvLen,0,(struct sockaddr*)&toAddr,sizeof(toAddr))!=recvLen){
printf("sendto fail/r/n");
close(sock);
exit(0);
}
return 0;
}
}
客户端程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("请输入要传送的内容./r/n");
exit(0);
}
int sock;
//sendto中使用的对方地址
struct sockaddr_in toAddr;
//在recvfrom中使用的对方主机地址
struct sockaddr_in fromAddr;
unsigned int fromLen;
char recvBuffer[128];
sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(sock < 0)
{
printf("创建套接字失败了./r/n");
exit(1);
}
char a[128];
memset(a,0,sizeof(a));
strcpy(a,argv[1]);
printf("%d/n",strlen(a));
memset(&toAddr,0,sizeof(toAddr));
toAddr.sin_family=AF_INET;
toAddr.sin_addr.s_addr=inet_addr("127.0.0.1");
toAddr.sin_port = htons(4000);
if(sendto(sock,a,strlen(a),0,(struct sockaddr*)&toAddr,sizeof(toAddr)) != strlen(argv[1]))
{
printf("sendto() 函数使用失败了./r/n");
close(sock);
exit(1);
}
fromLen = sizeof(fromAddr);
int len;
if((len=recvfrom(sock,recvBuffer,128,0,(struct sockaddr*)&fromAddr,&fromLen))<0)
{
printf("()recvfrom()函数使用失败了./r/n");
close(sock);
exit(1);
}
printf("%d/n",len);
recvBuffer[len]='/0';
printf("接收到数据:%s/n 从 %s/t",recvBuffer,inet_ntoa(fromAddr.sin_addr));
printf("端口号:%d/n",ntohs(fromAddr.sin_port));
close(sock);
return 0;
}
客户端: socket connect write read close
服务端: socket bind listen accept read write
使用udp通信的过程:
服务端:socket bind recvfrom/sendto
客户端:socket recvfrom/sendto
Socket 网络编程用到的函数
1. Socket系统调用: int socket(int domain,int type,int protocol);
包含头文件:
#include <sys/types.h>
#incldue <sys/socket.h>
参数domain用来指定发生通信的域,可能的值有(常量PF_XXXX也可以定义为AF_XXXX):
PF_LOCAL:用于本地通信的UNIX协议族。 PF_UNIX
PF_INET:IPV4互联网协议族。
PF_ISO:ISO协议族。
PF_CCITT:ITU-T协议,类似X.25。
PF_NS:XEROX网络系统协议。
PF_IPX:IPX—Novell协议族。
PF_X25:ITU-T X.25 ISO-8208协议族。
参数type用来指明通信的类型,现在使用的定义有如下几种:
SOCK_STREAM:提供顺序 可靠和基于字节流的双向连接。支持带外数据传输(out-of-band),对于带外传输,要求发送方在发送缓冲区中的数据之前发送带外数据,接收方在处理缓冲区之前处理带外数据。全双工的字节流,一个流类型的socket必须连接后才能传递数据。与另一个socket的连接由connect 连接而成
SOCK_DGRAM:支持数据报通信(无连接 不可靠 固定的信息最大长度)。
SOCK_SEQPACKET:提供原始网络协议访问。
SOCK_RAW:提供原始网络访问。
SOCK_RDM:提供一个可靠的数据报层,但不保证报文到达的顺序。
参数protoco:
采用IDPROTO_UDP,和IDPROTO_TCP。
调用出错返回:
EPROTONOSUPPORT:在该通信域中不支持该类型或不支持指定的协议。
ENFILE:没有足够核心内存分配给新socket结构。
EMFILE:进程描述符表溢出。
2. bind: int bind(int s,struct sockaddr *my_addr,int addrlen); 该调用将一个名字绑定到一个socket上。
包含头文件:
#include <sys/types.h>
#incldue <sys/socket.h>
bind为一个未命名的socket分配一个名字。换句话说,bind调用是给套接字s赋予本地的地址my_addr,这个my_addr的地址长度为addrlen,对于不同通信域联编的规则是不同的。可以将sockaddr_in的成员sin_addr设置为IDADDR_ANY,接收任何端口的报文。
3. recv和recvfrom调用
系统调用recv和recvfrom用来从一个socket接收消息。该调用的声明格式如下:
int recv(int s,void *buf,int len,unsigned int flags);
int recvfrom(int s,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
包含头文件:
#include <sys/types.h>
#incldue <sys/socket.h>
s为要接收的socket描述符。参数buf指向接收数据后存放的用户缓冲区的地址。参数len指出用户缓冲区的大小。recvfrom如果参数from为空,则该socket不是面向连接的,则返回时from存放的是该消息的源地址。fromlen指出from的实际大小。如果消息太长,缓冲区不能完全存放,可能丢弃,但视消息的类型而定。MSG_NOSIGNAL,即即使对方故不能比这个socket连接也不会产生SIGPIPE信号。MSG_ERRQUEUE,从错误队列中接收报文。与平台有关。
参数flags,MSG_PEEK该标志使得接收操作返回接收队列头部数据,并且不将数据从队列中移出。MSG_WAITALL,请求将被阻塞,直到请求数据量满足为止。
4. send和sendto调用
send只用于处于连接状态的socket,而sengto不需要socket处于连接状态。
如果不需要特殊的俄设置,flags直接赋值0,否者MSG_NOSIGNAL,当一端断开连接,基于流的socket会产生错误,并发送SIGPIPE给send或sengto进程,仍会返回错误代码EPIPE。
NSG_DONTWAIT,允许非阻塞操作。
5. shutdown调用关闭一个全双工连接的一端。
int shutdown(int s,int how);
成功返回0,否者返回-1。可能错误代码有EBADF参数s不是一个有效的描述符。ENOTSOCK参数s是一个文件描述符,而不是socket描述符。ENOTCONN:指定的socket没有处于连接状态。
。
7. listen调用
该调用在一个socket侦听连接。int listen(int s,int backlog)
指定接受连接的队列长度限制,然后由accept调用来接受连接请求。该调用只能在SOCK_STREAM或SOCK_SEQPACKET类型的socket上。参数backlog指定队列的最大长度。
8. accept调用
该调用在制定的socket上接受一个连接请求。 int accept(int s,struct sockaddr *addr,int *addrlen)
如果socket被设定为非阻塞,返回错误。如果调失败,可能设置的俄错误代码 EBADF EMFILE进程描述符表满 ENFILE指定socket类型不是sock_stream EOPENOTSUPP参数addr不再用户可写的地址空间内 EFAULT EWOULDBLOCK:socket被设置为非阻塞方式且没有连接请求。
9. gethostbyname调用
包含头文件:
#include <netdb.h>
extern int h_errno;
struct hostent *gethostbyname(const char *name);
strcut hostent *gethostbyaddr(const char *addr,int len,int type);
void sethostent(int stayopen);
void endhostent(void);
hostent结构如下:
struct hostent{
char *h_name;// 主机的正式名字
char **h_aliases;//主机的别名类表
int h_addrtype;//主机地址类型
int h_length;//地址长度
char **h_addr_list;//地址列表
}
10. 复制二进制数据函数:
void bzero(void *s,int n);
void bcopy(const void *src,void *dest,int n);
void memcpy(void *dest,const void *src,size_t n);
11. getprotoent函数调用:得到登记协议。
包含头文件:
#include <netdb.h>
struct protoent *getprotoent(void);
struct protoent *getprotbyname(const char *name);
struct protoent *getprotobynumber(int proto);
void setprotoent(int stayopen);
void endprotoent(void);
12. inet_addr等函数的调用
int inet_aton(const char *cp,struct in_addr *inp);//将internet的标准主机地址 转换成二进制数据格式
unsigned long int inet_addr(const char *cp);//将字符串形式 主机地址转换成网络字节顺序 二进制
unsigned long int inet_network(const char *cp);//从地址cp中取出主机字节顺序的
char *inet_ntoa(struct in_addrin);//将internet的标准主机地址 转换成字符串
struct in_addr inet_makeaddr(int net,int host);//将net所指向的网络地址和参数host所指的在net网络中的本地地址经过运算结合,得到一个网络字节顺序的internet主机地址。
unsigned long int inet_lnaof(struct in_addr_in);//以本地主机字节顺序返回本地主机在本子网中的地址
unsigned long int inet_netof(struct in_addr_in);//返回参数in所指的internet地址中的网络号
ushort ntohs(ushort);//将网络字节 的数据转换成主机字节顺序数据 例如端口号转换时使用
附带一个linux下udp通信的例子(客户端发送数据,服务端接收并发回去,接收端再显示出来),下次写一个基于tcp的并且用到尽可能多的函数:
服务端程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int sock;
//sendto中使用的对方地址
struct sockaddr_in toAddr;
//在recvfrom中使用的对方主机地址
struct sockaddr_in fromAddr;
int recvLen;
unsigned int addrLen;
char recvBuffer[128];
sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(sock < 0)
{
printf("创建套接字失败了./r/n");
exit(0);
}
memset(&fromAddr,0,sizeof(fromAddr));
fromAddr.sin_family=AF_INET;
fromAddr.sin_addr.s_addr=htonl(INADDR_ANY);
fromAddr.sin_port = htons(4000);
if(bind(sock,(struct sockaddr*)&fromAddr,sizeof(fromAddr))<0)
{
printf("bind() 函数使用失败了./r/n");
close(sock);
exit(1);
}
while(1){
addrLen = sizeof(toAddr);
if((recvLen = recvfrom(sock,recvBuffer,128,0,(struct sockaddr*)&toAddr,&addrLen))<0)
{
printf("()recvfrom()函数使用失败了./r/n");
close(sock);
exit(1);
}
printf("%d/n",recvLen);
if(sendto(sock,recvBuffer,recvLen,0,(struct sockaddr*)&toAddr,sizeof(toAddr))!=recvLen){
printf("sendto fail/r/n");
close(sock);
exit(0);
}
return 0;
}
}
客户端程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("请输入要传送的内容./r/n");
exit(0);
}
int sock;
//sendto中使用的对方地址
struct sockaddr_in toAddr;
//在recvfrom中使用的对方主机地址
struct sockaddr_in fromAddr;
unsigned int fromLen;
char recvBuffer[128];
sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(sock < 0)
{
printf("创建套接字失败了./r/n");
exit(1);
}
char a[128];
memset(a,0,sizeof(a));
strcpy(a,argv[1]);
printf("%d/n",strlen(a));
memset(&toAddr,0,sizeof(toAddr));
toAddr.sin_family=AF_INET;
toAddr.sin_addr.s_addr=inet_addr("127.0.0.1");
toAddr.sin_port = htons(4000);
if(sendto(sock,a,strlen(a),0,(struct sockaddr*)&toAddr,sizeof(toAddr)) != strlen(argv[1]))
{
printf("sendto() 函数使用失败了./r/n");
close(sock);
exit(1);
}
fromLen = sizeof(fromAddr);
int len;
if((len=recvfrom(sock,recvBuffer,128,0,(struct sockaddr*)&fromAddr,&fromLen))<0)
{
printf("()recvfrom()函数使用失败了./r/n");
close(sock);
exit(1);
}
printf("%d/n",len);
recvBuffer[len]='/0';
printf("接收到数据:%s/n 从 %s/t",recvBuffer,inet_ntoa(fromAddr.sin_addr));
printf("端口号:%d/n",ntohs(fromAddr.sin_port));
close(sock);
return 0;
}
相关文章推荐
- Linux 网络编程——原始套接字实例:发送 UDP 数据包
- Linux网络编程-UDP接收数据丢包解决方案
- 【Linux网络编程】原始套接字实例:发送 UDP 数据包
- Linux网络通信编程(套接字模型TCP\UDP与IO多路复用模型select\poll\epoll)
- linux网络编程之socket(UDP)
- Linux网络编程之UDP
- 【linux高级程序设计】(第十五章)UDP网络编程应用 2
- linux网络编程-UDP
- 基于UDP的服务器端/客户端---------网络编程(Linux----C)
- Linux网络编程下UDP洪水攻击实例介绍
- Linux网络编程:基于UDP的程序开发回顾篇
- Linux 网络编程五(UDP协议)
- linux网络编程Socket之TCP与UDP
- Linux网络编程 之 UDP 组播实例
- linux之UDP网络编程
- Linux网络编程:基于UDP的程序开发回顾篇
- Linux下网络编程实现UDP
- Linux下网络编程TCP/UDP简介
- Linux网络编程 之 UDP 单播
- linux网络编程--第一弹UDP基本通信