{UDP 协议是一种无连接协议,两台计算机之间的数据传输类似于传递邮件:消息从一台计算机发送到另一台计算核差机,但是两者之间没有明确的连接。
由于UDP 协议不需要显式的连接,就需要在两个Winsock控件中间发送数据,关键需要完成以下的三步:
1.将RemoteHost属性设置为另一台计算机的名称。
2.将RemotePort属性设置为第二个控件的LocalPort属性。
3.调用Bind方法,指定使用的LocalPort。
因为两台计算机的地位可以看成“对等的”,这种应用程序也被称为点对点的应用程序。
下面将创建一个聊天应用程序,两个人可以通过它进行实时的交谈。请按照以下步骤制作:
1.创建一个新的 Standard EXE 工程。将缺省的窗体的名称修改为frmPeerA,将窗体的标题修改为“Peer A”。
2.在窗体中放入一个 Winsock 控件,并将其命名为 udpPeerA。在“属性”页上,单击“协议”并将协议修改为 UDPProtocol。
3.在窗体中添加两个 TextBox 控件。将第一个命名为 txtSend,第二个命名为 txtOutput。
4.为窗体添加如下的代码。
Private Sub Form_Load()
′控件的名字为udpPeerA
With udpPeerA
′重点:必须将 RemoteHost 的值修改为对方计算机的名字。
RemoteHost= ″PeerB″
RemotePort = 1001 ′连接的端口号。
Bind 1002 ′绑定到本地的端口。
End With
frmPeerB.Show′显示第二个窗体。
End Sub
Private Sub txtSend_Change()
′在键入文本时,立即将其发送出去。
udpPeerA.SendData txtSend.Text
End Sub
Private Sub udpPeerA_DataArrival _
(ByVal bytesTotal As Long)
Dim strData As String
udpPeerA.GetData strData
txtOutput.Text = strData
End Sub
要创建第二个 UDP 伙伴,请按照以下步骤执行:
1.在工程中添加一个标准窗体,将窗体的名字修改为 frmPeerB,将窗体的标题修改为“Peer B”。
2.在窗体中放入一个 Winsock 控件,并将其命名为 udpPeerB。
3.在“属性”页上,单击“协议”并将协议修改为“UDPProtocol”。
4.在窗体上添加两个 TextBox 控件。将第一个命名为 txtSend,第二个命名为 txtOutput。
5.在窗体中添加如下代启氏卜码
Private Sub Form_Load()
′控件的名字为 udpPeerB。
With udpPeerB
′重点悄穗:必须将RemoteHost的值改为对方计算机的名字。
RemoteHost= ″PeerA″
RemotePort = 1002 ′要连接的端口。
Bind 1001 ′绑定到本地的端口上。
End With
End Sub
Private Sub txtSend_Change()
′在键入后立即发送文本。
udpPeerB.SendData txtSend.Text
End Sub
Private Sub udpPeerB_DataArrival _
(ByVal bytesTotal As Long)
Dim strData As String
udpPeerB.GetData strData
txtOutput.Text = strData
End Sub
运行工程,然后在两个窗体的txtSend TextBox中分别键入一些文本。键入的文字将出现在另一个窗体的 txtOutput TextBox中。
这样,一个十分简单的实时聊天工具就做好了。
UDP Server程序1、编写UDP Server程序的步骤
(1)使用socket()来建立一个UDP socket,第二个参数为SOCK_DGRAM。
(2)初始化sockaddr_in结构的变量,并赋值。sockaddr_in结构定义:
struct sockaddr_in {
uint8_t sin_len
sa_family_t sin_family
in_port_t sin_port
struct in_addr sin_addr
char sin_zero[8]
}
这里使用“08”作为服务程序的端口,使用“INADDR_ANY”作为绑定的IP地址即任何主机上的地址。
(3)使用bind()把上面的socket和定义的IP地址和端口绑定。这里检查bind()是否执行成功,如果有错误就退出。这样可以防止服务程序重复运行的问题。
(4)进入无限循环程序,使用recvfrom()进入等待状态,直到接收到客户程序发送的数据,就处理收正或到的数据,并向客户程序发送反馈。这里是直接把收到的数据发回给客户程序。
2、udpserv.c程序内容:
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXLINE 80
#define SERV_PORT 8888
void do_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen)
{
int n
socklen_t len
char mesg[MAXLINE]
for()
{
len = clilen
/* waiting for receive data */
n = recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len)
/* sent data back to client */
sendto(sockfd, mesg, n, 0, pcliaddr, len)
}
}
int main(void)
{
int sockfd
struct sockaddr_in servaddr, cliaddr
sockfd = socket(AF_INET, SOCK_DGRAM, 0)/* create a socket */
/姿凯* init servaddr */
bzero(&servaddr, sizeof(servaddr))
servaddr.sin_family = AF_INET
servaddr.sin_addr.s_addr = htonl(INADDR_ANY)
servaddr.sin_port = htons(SERV_PORT)
/* bind address and port to socket */
if(bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
{
perror("bind error")
exit(1)
}
do_echo(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr))
return 0
}
UDP Client程序
1、编写UDP Client程序的步骤
(1)初始化sockaddr_in结构的变量,并赋值。这里使用“8888”作为连接的服务程序的端口,从命令行参数读取IP地址,并且判断IP地址是否符举册伍合要求。
(2)使用socket()来建立一个UDP socket,第二个参数为SOCK_DGRAM。
(3)使用connect()来建立与服务程序的连接。与TCP协议不同,UDP的connect()并没有与服务程序三次握手。上面我们说了UDP是非连接的,实际上也可以是连接的。使用连接的UDP,kernel可以直接返回错误信息给用户程序,从而避免由于没有接收到数据而导致调用recvfrom()一直等待下去,看上去好像客户程序没有反应一样。
(4)向服务程序发送数据,因为使用连接的UDP,所以使用write()来替代sendto()。这里的数据直接从标准输入读取用户输入。
(5)接收服务程序发回的数据,同样使用read()来替代recvfrom()。
(6)处理接收到的数据,这里是直接输出到标准输出上。
2、udpclient.c程序内容:
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#define MAXLINE 80
#define SERV_PORT 8888
void do_cli(FILE *fp, int sockfd, struct sockaddr *pservaddr, socklen_t servlen)
{
int n
char sendline[MAXLINE], recvline[MAXLINE + 1]
/* connect to server */
if(connect(sockfd, (struct sockaddr *)pservaddr, servlen) == -1)
{
perror("connect error")
exit(1)
}
while(fgets(sendline, MAXLINE, fp) != NULL)
{
/* read a line and send to server */
write(sockfd, sendline, strlen(sendline))
/* receive data from server */
n = read(sockfd, recvline, MAXLINE)
if(n == -1)
{
perror("read error")
exit(1)
}
recvline[n] = 0/* terminate string */
fputs(recvline, stdout)
}
}
int main(int argc, char **argv)
{
int sockfd
struct sockaddr_in srvaddr
/* check args */
if(argc != 2)
{
printf("usage: udpclient <IPaddress>\n")
exit(1)
}
/* init servaddr */
bzero(&servaddr, sizeof(servaddr))
servaddr.sin_family = AF_INET
servaddr.sin_port = htons(SERV_PORT)
if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
{
printf("[%s] is not a valid IPaddress\n", argv[1])
exit(1)
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0)
do_cli(stdin, sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))
return 0
}
运行例子程序
1、编译例子程序
使用如下命令来编译例子程序:
gcc -Wall -o udpserv udpserv.c
gcc -Wall -o udpclient udpclient.c
编译完成生成了udpserv和udpclient两个可执行程序。
2、运行UDP Server程序
执行./udpserv &命令来启动服务程序。我们可以使用netstat -ln命令来观察服务程序绑定的IP地址和端口,部分输出信息如下:
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:32768 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:6000 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN
udp 0 0 0.0.0.0:32768 0.0.0.0:*
udp 0 0 0.0.0.0:8888 0.0.0.0:*
udp 0 0 0.0.0.0:111 0.0.0.0:*
udp 0 0 0.0.0.0:882 0.0.0.0:*
可以看到udp处有“0.0.0.0:8888”的内容,说明服务程序已经正常运行,可以接收主机上任何IP地址且端口为8888的数据。
如果这时再执行./udpserv &命令,就会看到如下信息:
bind error: Address already in use
说明已经有一个服务程序在运行了。
3、运行UDP Client程序
执行./udpclient 127.0.0.1命令来启动客户程序,使用127.0.0.1来连接服务程序,执行效果如下:
Hello, World!
Hello, World!
this is a test
this is a test
^d
输入的数据都正确从服务程序返回了,按ctrl+d可以结束输入,退出程序。
如果服务程序没有启动,而执行客户程序,就会看到如下信息:
$ ./udpclient 127.0.0.1
test
read error: Connection refused
说明指定的IP地址和端口没有服务程序绑定,客户程序就退出了。这就是使用connect()的好处,注意,这里错误信息是在向服务程序发送数据后收到的,而不是在调用connect()时。如果你使用tcpdump程序来抓包,会发现收到的是ICMP的错误信息。
这是很久以前在网上找的,略作修改的一个小案例UDP聊天的,供你参考,改猛租改脊知兆里面的ip地址。import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetSocketAddress
public class UDPClient {
public static void main(String[] args) throws IOException {
new UDPClient().go()
}
private void go() {
ClientSendThread send = new ClientSendThread()
new Thread(send).start()
ClientRecvThread recv = new ClientRecvThread()
new Thread(recv).start()
}
class ClientSendThread implements Runnable {
@Override
public void run() {
try {
DatagramSocket ds = new DatagramSocket()
String str = ""
byte[] buf = null
while (true) {
System.out.println("请输入>")
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in))
str = br.readLine()
if ("bye".equals(str))
break
buf = str.getBytes()
/樱租/ System.out.println("-----buf.length-------" + buf.length)
DatagramPacket dp = new DatagramPacket(buf, buf.length,
new InetSocketAddress("127.0.0.1", 5678))
ds.send(dp)
buf = null
}
ds.close()
} catch (IOException e) {
e.printStackTrace()
}
}
}
class ClientRecvThread implements Runnable {
@Override
public void run() {
byte[] buf = new byte[1024]
//接收端的端口需要指定,不然发送端不知道向哪个端口发送数据包
DatagramSocket ds
try {
ds = new DatagramSocket(5679)
String message = ""
DatagramPacket dp = null
while(true){
dp = new DatagramPacket(buf, buf.length)
ds.receive(dp)
message = new String(buf,0,dp.getLength())
System.out.println("接收到Server端信息为:"+message)
}
} catch (IOException e) {
e.printStackTrace()
}
}
}
}
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetSocketAddress
public class UDPServer {
public static void main(String[] args) throws IOException {
new UDPServer().go()
}
private void go() {
//服务端启动2个线程,1个发送,1个接收
ServerSendThread send = new ServerSendThread()
new Thread(send).start()
ServerRecvThread recv = new ServerRecvThread()
new Thread(recv).start()
}
// 服务端发送线程
class ServerSendThread implements Runnable{
@Override
public void run() {
try {
DatagramSocket ds = new DatagramSocket()
String str = ""
byte[] buf = null
while (true) {
System.out.println("请输入>")
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in))
str = br.readLine()
if ("bye".equals(str))
break
buf = str.getBytes()
DatagramPacket dp = new DatagramPacket(buf, buf.length,
new InetSocketAddress("192.163.20.61", 5679))
ds.send(dp)
buf = null
}
ds.close()
} catch (IOException e) {
e.printStackTrace()
}
}
}
// 服务端接收线程
class ServerRecvThread implements Runnable{
@Override
public void run() {
byte[] buf = new byte[1024]
//接收端的端口需要指定,不然发送端不知道向哪个端口发送数据包
DatagramSocket ds
try {
ds = new DatagramSocket(5678)
String message = ""
while(true){
DatagramPacket dp = new DatagramPacket(buf, buf.length)
ds.receive(dp)
message = new String(buf,0,dp.getLength())
System.out.println("接收到Client端的信息为:"+message)
}
} catch (IOException e) {
e.printStackTrace()
}
}
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)