您的位置:首页 > 理论基础 > 计算机网络

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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: