linux 网络编程 udp篇

    使用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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值