学习笔记《实战Linux Socket编程》第六章 面向非连接的协议

 第六章  面向非连接的协议
6.1  非连接通信的优点:
    1)更加简单:不需要建立连接
    2)富有弹性:每一次的消息发送都可以定向到不同的接收者
    3)高效:因为不需要建立和拆除连接,所以避免了大量在网络中传递消息分组的开销
    4)快速:因为不需要建立和拆除连接,所以只有消息本身被发送.
    5)具备广播能力.
 
6.2  缺点:
    1)通信过程不可靠.
    2)多数据报的无序性.
    3)消息的尺寸有限制:理论上UDP数据报的最大尺寸略小于64KB,但实际上许多UNIX主机只提供32KB的最大尺寸,有的甚至只有8KB,最终套接口接收程序还会把这个最大尺寸限制为接收缓冲区的大小,许多程序UDP消息尺寸只有512B或更小.
 
6.3  sendto(2)函数
#include <sys/types.h>
#include <sys/socket.h>
int sendto(int s, const void *msg, int len, unsigned flags,  
                  const struct sockaddr *to, int tolen)
参数s是套接口的描述符,它是由socket()函数生成的
参数msg是指向容纳所要发送的数据报信息的缓冲区的指针
参数len数据报信息的长度
参数flags一般为0
参数to接收端的地址
参数tolen地址长度
调用成功,返回值为发送的字节数,失败返回-1,可以通过检查errno的值来判断错误原因.
 
sendto()的flags参数的值
flags          十六进制       意义
0              0x0000         正常--无特定选项
MSG_OOB        0x0001         外理带外数据
MSG_DONTROUTE  0x0004         旁路路由,使用直接接口
MSG_DONTWAIT   0x0040         非阻塞,等待写入
MSG_NOSIGNAL   0x4000         当另一端断开连接时,不生成SIGPIPE信号
 
6.4  recvfrom(2)函数
#include <sys/types.h>
#include <sys/socket.h>
int recvfrom(int s, const void *buf, int len, unsigned flags,  
                  const struct sockaddr *from, int fromlen)
参数s是套接口的描述符
参数buf是指向容纳所要接收的数据报信息的缓冲区的指针
参数len缓冲区的长度
参数flags一般为0
参数from发送端的地址
参数fromlen地址长度
调用成功,返回值为接收的字节数,失败返回-1,可以通过检查errno的值来判断错误原因.
 
sendto()的flags参数的值
flags          十六进制       意义
0              0x0000         正常--无特定选项
MSG_OOB        0x0001         外理带外数据
MSG_PEEK       0x0002         读入一个数据报,但不从内核中删除
MSG_WAITALL    0x0100         进行阻塞,直到所有的要求满足
MSG_ERRQUEUE   0x2000         从错误队列中接收一个分组
MSG_NOSIGNAL   0x4000         当另一端断开连接时,不生成SIGPIPE信号

代码实例
clien
#include  < stdio.h >

#include 
< unistd.h >
#include 
< stdlib.h >
#include 
< errno.h >
#include 
< string .h >
#include 
< time.h >
#include 
< sys / types.h >
#include 
< sys / socket.h >
#include 
< netinet / in .h >
#include 
< arpa / inet.h >

static   void  bail( const   char   * on_what)
{
    fputs(strerror(errno), stderr);

    fputs(
":", stderr);

    fputs(on_what, stderr);

    fputc(
' ', stderr);

    exit(
1);
}


int  main( int  argc,  char   * argv[])

{

    
int z;
    
int x;
    
char *srvr_addr = NULL;
    
struct sockaddr_in adr_srvr;
    
struct sockaddr_in adr;
    
int len_inet;
    
int s;
    
char dgram[512];
    
    
if (argc >= 2)
    
{
        srvr_addr 
= argv[1];
    }
else
    
{
        srvr_addr 
= "127.0.0.23";
    }

    
    memset(
&adr_srvr, 0sizeof(adr_srvr));
    adr_srvr.sin_family 
= AF_INET;
    adr_srvr.sin_port 
= htons(9090);
    adr_srvr.sin_addr.s_addr 
= inet_addr(srvr_addr);
    
    
if (INADDR_NONE == adr_srvr.sin_addr.s_addr)
        bail(
"bad address.");
    
    len_inet 
= sizeof(adr_srvr);
    
    s 
= socket(AF_INET, SOCK_DGRAM, 0);
    
if (-1 == s)
        bail(
"socket()");
    
    
for (;;)
    
{
        fputs(
" Enter format string: ", stdout);
        
if(!fgets(dgram, sizeof(dgram), stdin))
            
break;
        
        z 
= strlen(dgram);
        
if (z > 0 && dgram[--z] == ' ')
            dgram[z] 
= 0;
        
        z 
= sendto(s, dgram, strlen(dgram), 0,
                    (
struct sockaddr *)&adr_srvr, len_inet);
        
if (z < 0)
            bail(
"sendto()");
        
        
if (!strcasecmp(dgram, "quit"))
            
break;
        
        x 
= sizeof(adr);
        z 
= recvfrom(s, dgram, sizeof(dgram), 0,
                        (
struct sockaddr *)&adr, &x);
        
if (z < 0)
            bail(
"recvfrom()");
        
        dgram[z] 
= 0;
        
        printf(
"Result from %s port %u : '%s' ",
                    inet_ntoa(adr.sin_addr),
                    (unsigned)ntohs(adr.sin_port), dgram);
    }

    
    close(s);
    putchar(
' ');
    
return 0;

}


/*
测试:
$./udpserver 192.168.1.6
//上面的IP是服务器IP,没有则是程序中默认的IP

$./udpclient 192.168.1.6
Enter format string:%D
........
//上面的和下面的%D,%A(%D)是要输入的时间的格式
Enter format string:%A(%D)
...........
Enter format string:quit
//退出
*/

server
#include  < stdio.h >

#include 
< stdlib.h >

#include 
< string .h >

#include 
< errno.h >

#include 
< unistd.h >

#include 
< sys / types.h >

#include 
< sys / socket.h >

#include 
< netinet / in .h >

#include 
< arpa / inet.h >

#include 
< time.h >



static   void  bail( char   * on_what)

{

    fputs(strerror(errno), stderr);

    fputs(
":", stderr);

    fputs(on_what, stderr);

    fputc(
' ', stderr);

    exit(
1);

}


int  main( int  argc,  char   * argv[])

{

    
int z;

    
char *srvr_addr = NULL;

    
struct sockaddr_in adr_inet;

    
struct sockaddr_in adr_clnt;

    
int len_inet;

    
int s;

    
char dgram[512];

    
char dtfmt[512];

    time_t td;

    
struct tm tm;



    
if (argc >= 2)

    
{

        srvr_addr 
= argv[1];

    }
else{

        srvr_addr 
= "127.0.0.23";

    }




    s 
= socket(AF_INET, SOCK_DGRAM, 0);

    
if (-1 == s)

        bail(
"socket()");



    memset(
&adr_inet, 0sizeof(adr_inet));

    adr_inet.sin_family 
= AF_INET;

    adr_inet.sin_port 
= htons(9090);

    adr_inet.sin_addr.s_addr 
= inet_addr(srvr_addr);



    
if (adr_inet.sin_addr.s_addr == INADDR_NONE)

        bail(
"bad address.");



    len_inet 
= sizeof(adr_inet);



    z 
= bind(s, (struct sockaddr *)&adr_inet, len_inet);

    
if (-1 == z)

        bail(
"bind()");



    
for (;;)

    
{

        len_inet 
= sizeof(adr_clnt);

        z 
= recvfrom(s, dgram, sizeof(dgram), 0

            (
struct sockaddr *)&adr_clnt, &len_inet);



        
if (z < 0)

            bail(
"recvfrom()");


        printf(
"Client from %s port %u; ", inet_ntoa(adr_clnt.sin_addr),
                (unsigned)ntohs(adr_clnt.sin_port));


        dgram[z] 
= 0;

        
//结束服务器

        
if (!strcasecmp(dgram, "quit"))

            
break;



        time(
&td);        //获取当前时间

        tm 
= *localtime(&td);

        

        
//格式化结果, 结果最大尺寸, 输入日期格式, 输入日期值

        strftime(dtfmt, 
sizeof(dtfmt), dgram, &tm);



        z 
= sendto(s, dtfmt, strlen(dtfmt), 0

            (
struct sockaddr *)&adr_clnt, len_inet);

        
if (z < 0)

            bail(
"sendto()");

    }




    close(s);

    
return 0;

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值