UNP卷1:第八章(基本UNP套接字编程)

1. 简单的UDP回射程序

1) 服务器udpsrv.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>

#define MAXLINE 1024
#define SA struct sockaddr

void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen);
int main(int argc, char **argv)
{
	int		sockfd;
	struct	sockaddr_in	servaddr, cliaddr;

	sockfd = socket(AF_INET, SOCK_DGRAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(9877);

	bind(sockfd, (SA *)&servaddr, sizeof(servaddr));

	dg_echo(sockfd, (SA *)&cliaddr, sizeof(cliaddr));

	return 0;
}

void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
{
	int			n;
	socklen_t	len;
	char		mesg[MAXLINE];

	for ( ; ; ){
		len = clilen;
		n = recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);

		sendto(sockfd, mesg, n, 0, pcliaddr, len);
	}
}


2) 客户端udpcli.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

#define MAXLINE 1024
#define SA struct sockaddr

void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen);
int main(int argc, char **argv)
{
	int		sockfd;
	struct	sockaddr_in	servaddr;

	if (argc != 2){
		printf("argument should be 2\n");
		exit(-1);
	}

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(9877);
	inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

	sockfd = socket(AF_INET, SOCK_DGRAM, 0);

	dg_cli(stdin, sockfd, (SA *)&servaddr, sizeof(servaddr));

	exit(0);
}
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
	int		n;
	char	sendline[MAXLINE], recvline[MAXLINE + 1];

	while (fgets(sendline, MAXLINE, fp) != NULL){
		sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
		n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);

		recvline[n] = 0;
		fputs(recvline, stdout);
	}
}


程序输入输出:

服务器:

leichaojian@ThinkPad-T430i:~$ ./udpsrv

客户端:

leichaojian@ThinkPad-T430i:~$ ./udpcli 127.0.0.1
i love you
i love you
^C

3) 服务器进程未运行时候客户端的阻塞

    如果服务器未启动而客户端发送一行文本,则客户永远阻塞于它的recvfrom调用,等待一个永不出现的服务器应答(所以下例中tcpdump只显示第一行hello world的信息,而其余的信息已经被阻塞了):

启动tcpdump:

root@ThinkPad-T430i:/home/leichaojian# tcpdump -i eth0 udp port 9877
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
22:18:50.481559 IP 192.168.0.7.60341 > 218.30.64.194.9877: UDP, length 12

直接启动客户端:

leichaojian@ThinkPad-T430i:~$ ./udpcli 218.30.64.194
hello world
i love you
what
    tcpdump只显示hello world发送的文本,而接着发送i love you和what则没有任何的反应,因为已经阻塞了。其中9877是服务器指定的总所周知的端口号,而60341为客户端临时端口。

2. UDP的connect函数

1) connect函数的起源

    除非套接字已连接,否则异步错误是不会返回到UDP套接字的。我们可以给UDP套接字调用connect,然而这样做的结果却与TCP连接大相近庭:没有三次握手过程。内核只是检查是否存在立即可知的错误,记录对端的IP地址和端口号(取自传递给connect的套接字地址结构),然后立即返回到调用进程。

    有了这个能力后,我们必须区分:

(1) 未连接UDP套接字,新创建UDP套接字默认如此。

(2) 已连接UDP套接字,对UDP套接字调用connect的结果。

    对于已连接UDP套接字,与默认的未连接UDP套接字相比,发生了三个变化:

(1) 我们再也不能给输出操作指定目的IP地址和端口号。也就是说,我们不使用sendto而改用write或send。写到已连接UDP套接字上的任何内容都自动发送到由connect指定的协议地址(例如IP地址和端口号)

(2) 我们不必使用recvfrom以获悉数据报的发送者,而改用read,recv或recvmsg。在一个已连接UDP套接字上,由内核为输入操作返回的数据报只有那些来自connect所指定协议地址的数据报。目的地为这个已连接UDP套接字的本地协议地址(例如IP地址和端口号),发源地却不是该套接字早先connect到的协议地址的数据报,不会投递到该套接字。这样就限制一个已连接UDP套接字能且仅能与一个对端交换数据报。

(3) 由已连接UDP套接字引发的异步错误会返回给它们所在的进程,而未连接UDP套接字不接收任何异步错误。

2) 使用connect的客户端

#include "myunp.h"

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 );

		if ( n < 0 ){
			printf("read error\n");
			return;
		}
		recvline[ n ] = 0;
		fputs( recvline, stdout );
	}
}

int main( int argc, char **argv )
{
	int	sockfd;
	struct	sockaddr_in	servaddr;
	
	bzero( &servaddr, sizeof( servaddr ) );
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons( 9877 );
	inet_pton( AF_INET, argv[ 1 ], &servaddr.sin_addr );

	sockfd = Socket( AF_INET, SOCK_DGRAM, 0 );

	dg_cli( stdin, sockfd, ( SA * )&servaddr, sizeof( servaddr ) );

	exit( 0 );
}

    如果服务端不启动,则输出结果如下:

服务端:

root@ThinkPad-T430i:/home/leichaojian# tcpdump -i eth0 udp port 9877
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
19:27:08.010090 IP 192.168.0.7.51328 > 218.30.64.194.9877: UDP, length 12

客户端分两种情况:

1) 

leichaojian@ThinkPad-T430i:~$ host leichaojian
leichaojian.router has address 218.30.64.194
Host leichaojian.router not found: 5(REFUSED)
Host leichaojian.router not found: 3(NXDOMAIN)
leichaojian@ThinkPad-T430i:~$ ./dgcliconnect 218.30.64.194
hello world
^C

2) 如果是以下的代码,则服务端无任何抓包行为:

leichaojian@ThinkPad-T430i:~$ ./dgcliconnect 127.0.0.1
hello world
n is:-1
read error


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值