udp2

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;

/**
 * Connect to a daytime server using the UDP protocol. We use java.net

instead
 * of java.nio because DatagramChannel doesn't honor the setSoTimeout()

method
 * on the underlying DatagramSocket
 */
public class DaytimeClient {
  public static void main(String args[]) throws java.io.IOException {
    // Figure out the host and port we're going to talk to
    String host = args[0];
    int port = 13;
    if (args.length > 1)
      port = Integer.parseInt(args[1]);

    // Create a socket to use
    DatagramSocket socket = new DatagramSocket();

    // Specify a 1-second timeout so that receive() does not block forever.
    socket.setSoTimeout(1000);

    // This buffer will hold the response. On overflow, extra bytes are
    // discarded: there is no possibility of a buffer overflow attack here.
    byte[] buffer = new byte[512];
    DatagramPacket packet = new DatagramPacket(buffer, buffer.length, new

InetSocketAddress(host,
        port));

    // Try three times before giving up
    for (int i = 0; i < 3; i++) {
      try {
        // Send an empty datagram to the specified host (and port)
        packet.setLength(0); // make the packet empty
        socket.send(packet); // send it out

        // Wait for a response (or timeout after 1 second)
        packet.setLength(buffer.length); // make room for the response
        socket.receive(packet); // wait for the response

        // Decode and print the response
        System.out.print(new String(buffer, 0, packet.getLength(), "US-

ASCII"));
        // We were successful so break out of the retry loop
        break;
      } catch (SocketTimeoutException e) {
        // If the receive call timed out, print error and retry
        System.out.println("No response");
      }
    }

    // We're done with the channel now
    socket.close();
  }
}


import java.io.*;
import java.lang.*;
import java.net.*;
public class uclient
{
 private DatagramSocket cli;
 private DatagramPacket pac;
 private byte sb[];
 private String sen;
 public uclient()
 {
  Init();
 }
 public void Init()
 {
  try
  {
   //指定端口号,避免与其他应用程序发生冲突
   cli=new DatagramSocket(10002);
   sb=new byte[1024];
   sen="UDP方式发送数据";
   sb=sen.getBytes();
   pac=new DatagramPacket(sb,sb.length,InetAddress.getByName

("localhost"),10005);
   cli.send(pac);
   }
   catch(SocketException se)
   {
     se.printStackTrace();
   }
   catch(IOException ie)
   {
     ie.printStackTrace();
   }
 }
 public static void main(String args[])
 {
  new uclient();
 }
}
接收端数据:
//加以改进代码,可以改变接收方式
import java.io.*;
import java.lang.*;
import java.net.*;
public class userve
{
 private DatagramSocket ser;
 private DatagramPacket pac;
 private byte rb[];
 private String rev;
 public userve()
 {
   Init();
 }
 public void Init()
 {
  try
  {
   ser=new DatagramSocket(10005);
   rb=new byte[1024];
   pac=new DatagramPacket(rb,rb.length);
   rev="";
   int i=0;
   while(i==0)//无数据,则循环
   {
     ser.receive(pac);
     i=pac.getLength();
     //接收数据
     if(i>0)
     {
      //指定接收到数据的长度,可使接收数据正常显示,开始时很容易忽略这一点
      rev=new String(rb,0,pac.getLength());
      System.out.println(rev);
      i=0;//循环接收
     }  
   }
  }
  catch(Exception e)
  {
   e.printStackTrace();
  }
 }
 public static void main(String args[])
 {
   new userve();
 }
}

udp
概述
UDP
无连接的 connectionless不可靠的 unreliable数据报协议 datagram

应用:DNS, NFS, SNMP, ICQ

TCP
面向连接的 connection-oriented可靠的 reliable字节流协议 byte stream

应用:www, telnet ,ftp

 

----------------------------------------------------------------------------

----

UDP 客户-服务器程式的套接口函数
recvfrom 和 sendto 函数

#include
ssize_t recvfrom(int sockfd, void *buff, size nbytes, int flags, struct

sockaddr *from, socklen_t *addrlen);

sockfd: 描述字

buff: 指向输入缓冲器的指针

nbytes: 读字节大小

flag: 标志:0

from :对方协议地址

addrlen: 对方协议地址长度

函数返回值: 读入数据的长度,能为0.

ssize_t sendto(int sockfd, void *buff, size nbytes, int flags, const struct

sockaddr *to, socklen_t *addrlen);

TCP的字节流输入输出函数:
ssize_t readn(int sockfd, void *buff, size nbytes) ;
ssize_t writen (int sockfd, void *buff, size nbytes);


----------------------------------------------------------------------------

----

UDP回射服务器程式
//服务器main主程式

#include "unp.h"
Int main(int argc, char **argv)
{
int sockfd; //定义套接字
struct sockaddr_in servaddr, cliaddr; //IPv4套接口地址定义
sockfd = Socket(AF_INET, SOCK_DGRAM, 0); //建立UDP套接字
bzero(&servaddr, sizeof(servaddr)); //地址结构清零
servaddr.sin_family = AF_INET; //IPv4协议
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//内核指定地址
servaddr.sin_port = htons(SERV_PORT); //9877 端口
/*分配协议地址,绑定端口*/
Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));
/* 回射子程式*/
dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
}


回射子程式:

include "unp.h"
void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
{
int n; //读入字节数
socklen_t len; //协议地址长度, 没有这个参数用 clilen也能
char mesg[MAXLINE];
for ( ; ; ) {
len = clilen;
/* 读入一行 */
n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
/* 回射到对方套接口 */
Sendto(sockfd, mesg, n, 0, pcliaddr, len);
}
}


----------------------------------------------------------------------------

----

UDP回射客户程式
//客户 main主程式

include "unp.h"
int main(int argc, char **argv) //命令行的第二个参数代表服务器地址
{ int sockfd; //套接字
struct sockaddr_in servaddr; //服务器地址结构
/* 必须在命令行指定服务器地址*/
if (argc != 2) err_quit("usage: udpcli ");
bzero(&servaddr, sizeof(servaddr)); //地址结构清零
servaddr.sin_family = AF_INET; //IPv4
servaddr.sin_port = htons(SERV_PORT); //9877端口
/*网络字节序的IP地址*/
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
/*建立UPD套接口*/
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
/*回射客户端子程式, stdin 为标准输入:键盘*/
dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));
exit(0); //子程式结束后退出程式
}

//客户端回射子程式

#include "unp.h"
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n; //读入字节数
char sendline[MAXLINE], recvline[MAXLINE + 1]; // 1:结束标志占用
/* 从键盘读入一行 */
while (Fgets(sendline, MAXLINE, fp) != NULL) { //如果不是^D结束
/* 将读入行发送到服务器套接口*/
Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
/*从读入回射,读入字节数为n, 不关心从何处读入
n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
recvline[n] = 0; /* recvline字符串的结束标志*/
Fputs(recvline, stdout); //输出到标准输出:显示器
} //while循环结束:直到从键盘读入结束符^D为止
}


----------------------------------------------------------------------------

----

验证收到的响应
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n; socklen_t len;
char sendline[MAXLINE], recvline[MAXLINE + 1];
struct sockaddr *preply_addr; //对方 (回应)地址指针
preply_addr = Malloc(servlen); //分配地址结构
while (Fgets(sendline, MAXLINE, fp) != NULL) {
Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
len = servlen;
/* 读入一行,并获得对方的套接口地址*/
n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);
/*对方套接口地址长度和指定服务器地址长度不相同*/
/*或套接口地址结构也不相同时,*/
if (len != servlen || memcmp(pservaddr, preply_addr, len) != 0) {
printf(“reply from %s (ignored)\n”, //忽略回射行,并输出对方地址
Sock_ntop(preply_addr, len) );
continue; //下一轮循环
}
recvline[n] = 0; /* null terminate */
Fputs(recvline, stdout);
}
}

 

----------------------------------------------------------------------------

----

服务器进程未运行
回射服务器-客户端程式执行的基本步骤:

??启动服务器
??启动客户程式

服务器没有启动,客户端没有回射行,一直等待

用tcpdump观察数据包 tcpdump icmp or arp or port 9877

有ARP请求和应答:端口不可达 port ... unreachable

异步错误:由sendto 引起的ICMP错误, 而sendto本身并不返回该错误

用已连接套接口才能返回到UDP套接口,需要调用connect.


----------------------------------------------------------------------------

----


UDP调用CONNECT
在末连接UDP套接口上给两个数据报调用函数sendto导致内核执行下列六步:


1.连接套接口;
2.输出第一个数据报
3.断开套接口连接;
4.连接套接口,
5.输出第二个数据报;
6.断开套接口连接

已连接套接口发送两个数据报的导致内核执行如下步骤;


1.连接套接口;
2.输出第一个数据报;
3.输出第二个数据报。

对同一套接口发送时,耗时减少1/3


----------------------------------------------------------------------------

----

dg_cli 函数(修订版)
调用connect 函数(有ICMP错误返回)
用read和write代替sendto 和 recvform

/* 调用connect函数的UDP 回射客户子程式*/

void
dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE + 1];
/* 和对方建立连接 */
Connect(sockfd, (SA *) pservaddr, servlen);

while (Fgets(sendline, MAXLINE, fp) != NULL) {
Write(sockfd, sendline, strlen(sendline));
n = Read(sockfd, recvline, MAXLINE);
recvline[n] = 0; /* null terminate */
Fputs(recvline, stdout);
}
}


----------------------------------------------------------------------------

----

UPD缺乏流量控制
UDP没有流量控制,他是不可靠的。

如果UDP发送方比UDP接收方运行速度快, 可能导致接收缓冲区满而造成数据报丢失。

对服务器或客户来说,并没有给出所有指示说这些数据报已丢失。

UDP套接口缓冲区

由UDP给特定套接口排队的UDP数据报数目受限于套接口接收缓冲区的大小。

用SO_RCVBUF套接口选项改动此值,能改善数据报丢失的情况,但并不能从根本

上解决问题。

/*增大套接口接收队列大小的函数*/

static void recvfrom_int(int); //内部函数
static int count;
void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen){
int n; socklen_t len;
char mesg[MAXLINE];
Signal(SIGINT, recvfrom_int);
n = 240 * 1024;
Setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
for ( ; ; ) {
len = clilen;
Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
count++;
}
}
static void recvfrom_int(int signo) {
printf("\nreceived %d datagrams\n", count);
exit(0);
}


----------------------------------------------------------------------------

----

UDP中外出接口的确定
已连接UDP套接口可用来确定用于待定目标的外出接口。

内核选择本地IP地址(假设进程并没有调用bind以明确地指派他)。

这个本地IP地址是通过给目的IP地址按索路由表,然后使用结果接口的主IP地址而选定

的。


例程:

int main(int argc, char **argv)
{
int sockfd;
socklen_t len;
struct sockaddr_in cliaddr, servaddr;
if (argc != 2) err_quit("usage: udpcli ");
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
len = sizeof(cliaddr);
Getsockname(sockfd, (SA *) &cliaddr, &len);
printf("local address %s\n", Sock_ntop((SA *) &cliaddr, len));
exit(0);
}

對TCP和UDP的理解
現在Internet上流行的協定是TCP/IP協定,該協定中對低於1024的埠都有確切的定義,

他們對應著Internet上一些常見的服務。這些常見的服務可以分為使用TCP埠(面向連接

)和使用UDP埠(面向無連接)兩種。

說到TCP和UDP,首先要明白“連接”和“無連接”的含義,他們的關係可以用一個形象地

比喻來說明,就是打電話和寫信。兩個人如果要通話,首先要建立連 接——即打電話時

的撥號,等待回應後——即接聽電話後,才能相互傳遞資訊,最後還要斷開連接——即

掛電話。寫信就比較簡單了,填寫好收信人的位址後將信投 入郵筒,收信人就可以收到

了。從這個分析可以看出,建立連接可以在需要痛心地雙方建立一個傳遞資訊的通道,

在發送方發送請求連接資訊接收方回應後,由於是 在接受方響應後才開始傳遞資訊,而

且是在一個通道中傳送,因此接受方能比較完整地收到發送方發出的資訊,即資訊傳遞

的可靠性比較高。但也正因為需要建立連 接,使資源開銷加大(在建立連接前必須等待

接受方回應,傳輸資訊過程中必須確認資訊是否傳到及斷開連接時發出相應的信號等)

,獨佔一個通道,在斷開連接錢 不能建立另一個連接,即兩人在通話過程中第三方不能

打入電話。而無連接是一開始就發送資訊(嚴格說來,這是沒有開始、結束的),只是

一次性的傳遞,是先不 需要接受方的回應,因而在一定程度上也無法保證資訊傳遞的可

靠性了,就像寫信一樣,我們只是將信寄出去,卻不能保證收信人一定可以收到。

TCP是面向連接的,有比較高的可靠性,   一些要求比較高的服務一般使用這個協定,

如FTP、Telnet、SMTP、HTTP、POP3等,而 UDP是面向無連接的,使用這個協定的常見服

務有DNS、SNMP、QQ等。對於QQ必須另外說明一下,QQ2003以前是只使用UDP協定的,其

伺服器 使用8000埠,偵聽是否有資訊傳來,用戶端使用4000埠,向外發送資訊(這也就

不難理解在一般的顯IP的QQ版本中顯示好友的IP位址資訊中埠 常為4000或其後續埠的原

因了),即QQ程式既接受服務又提供服務,在以後的QQ版本中也支援使用TCP協定了。
 

我在服务器端udp socket 进行数据传输的过程中发现sendto 返回值是我所发送的数据

长度,但但客户端却收不到数据,同时我在服务器端利用tcpdump也无法捕获数据包,同

时在客户端用Ethereal也无法捕获到数据包,我捕获数据包的条件是用客户端的Mac地址

,应该不会遗漏;不知道如何找原因,各位可遇过类似的情况吗?
其中我在CentOS和FreeBSD上都出现了这种情况;
2006-7-19 13:20wuye_chinaunix赫赫!

不面向连接的通讯的问题出来了吧。

udp从哪发出去的不确定。 我看你还是用IP条件在本地局域网捕获一下吧,就查出问题

在哪了!
2006-7-19 13:31fannyth原帖由 wuye_chinaunix 于 2006-7-19 13:20 发表
不面向连接的通讯的问题出来了吧。

udp从哪发出去的不确定。 我看你还是用IP条件在本地局域网捕获一下吧,就查出问题

在哪了! [/quote]

我在服务器端用tcpdump port 9004 作为条件捕获,如果从该断开出去的都应该能捕获

到,但是也是什么都没有捕获到。
2006-7-19 15:39liubinbj
2006-7-19 17:25fannyth[quote]原帖由 liubinbj 于 2006-7-19 15:39 发表
没发出去?有多网卡吗,对方是在同子网内吗? 

 

多网卡和单网卡的情况我都试验了,双方都在同一个子网内。

我刚开始也以为是多网卡的情况,换了一台机器还是有这种情况,目前我还没找到问题

出现的规律,根据捕获包的情况看不知是否与arp 包有关系,在程序运行的过程中,服

务器过一段时间就发一次arp包,不知道是否与此有关系;
2009-4-16 11:49jiangzj123老大,我现在也遇到你这样的问题,解决了么,希望能指教
2009-4-16 12:36ideawu如果偶尔出现是正常的, 因为UDP设计就是这样, UDP不是可靠

通信. 而在实现时, 有些实现, 甚至在数据被交给网卡之前丢弃后也返回正常.
2009-4-16 13:53jiangzj123不是偶尔,是一直都收不到,我是在做RIP测试的时候出现

的,520端口,采用多播,结果客户端收不到包
2009-4-16 16:43urapple回复 #3 fannyth 的帖子

如果没有收到一包,肯定不对。
先看看网络通不通。没问题再看你的代码。
程序正确的情况下,局域网不应该一包收不到。
多播,是往组里发,另外,要是收不到,看看有没有加组播路由。
2009-4-16 20:03jiangzj123我来说说我现在的情况吧。一个路由器进行RIP测试,路由

器的LAN口和WAN口都接在PC上,用CDrouter测试。PC机的LAN口能够把路由表发送到路由

器板子上来,但是路由器上发送的路由表(RIP是每隔30秒发一个跟新包),PC的LAN口

接收不到。通过调试,已经确认路由器的LAN的多播包已经发出来了(能抓到包),但运行

cdrouter的PC就是接收不到,郁闷啊,有谁碰到过这种情况么,防火墙都关闭了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值