#include
#include
#include
#include
#include
#include
#include //sockaddr_in
#include
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0);
void echo_srv(int sock)
{
char recvbuf[1024] = {0};
struct sockaddr_in peeraddr;
socklen_t peerlen;
int n;
while (1)
{
peerlen = sizeof(peeraddr);
memset(recvbuf, 0, sizeof(recvbuf));
//指定对方的地址信息:peeraddr,peerlen
n = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*)&peeraddr, &peerlen);
if (n == -1)
{
if (errno == EINTR)//被信号中断的话,继续接收
continue;
ERR_EXIT("recvfrom");
}
else if (n > 0)
{
fputs(recvbuf, stdout);
sendto(sock, recvbuf, n, 0, (struct sockaddr*)&peeraddr, peerlen);
}
}
close(sock);
}
int main(void)
{
int sock;
//PF_INET:IPv4的地址家族
//SOCK_DGRAM:IPv4家族中的udp套接口,所以第三个参数可为0
if ((sock = socket(PF_INET, SOCK_DGRAM, 0))< 0);
ERR_EXIT("socket");
//三个参数:地址族,端口,本机ip地址
struct sockaddr_in servaddr;
memset(&servaddr, 0, servaddr);
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
if (bind(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) <0)
ERR_EXIT("bind");
echo_srv(sock);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include //sockaddr_in
#include
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0);
void echo_cli(int sock)
{
//三个参数:地址族,端口,本机ip地址
struct sockaddr_in servaddr;
memset(&servaddr, 0, servaddr);
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//INADDR_ANY:表示本机任意地址
//解决异步错误,connect保证该套接字不饿能发送数据给其它地址,与TCP3次握手不同
connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr));
int ret;//测试异步错误
char sendbuf[1024] = {0};
char recvbuf[1024] = {0};
/*
对于已连接套接字而言:
获取本地地址可以用getsockname
获取远程地址可以用getpeername
当前的套接字是未连接的,仅仅可以获取本地的地址,该套接字是第一次sendto的时候绑定的
*/
while (fgets(sendbuf, sizeof(sendbud), sdtin) !=NULL)
{
//若只开启udp客户端,不开启udp服务端
//会产生ICMP异步错误:sento只是将应用层的缓冲区拷贝套套接口对应的缓冲区,是不报错的,该错误会延迟到recvfrom才收到通知
//而recvfrom也不不能通知,因为TCP/IP规定:这种异步错误是不能返还给未连接的套接字的,所以recvfrom会一直阻塞
// sendto(sock, sendbuf, strlen(sendbuf), 0, (struct sockaddr*)&servaddr, sizeof(servaddr));
//解决异步错误,因为这里的sock的对等方的地址已经由connect指定了
sendto(sock, sendbuf, strlen(sendbuf), 0, NULL, 0);
//等价于send(sock, sendbuf, strlen(sendbuf))
ret = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, NULL, NULL);
if(ret == -1)
{
if (errno == EINTR)
continue;
ERR_EXIT("recvfrom");
}
fputs(recvbuf, stdout);
memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
}
close(sock);
}
int main(void)
{
int sock;
//PF_INET:IPv4的地址家族
//SOCK_DGRAM:IPv4家族中的udp套接口,所以第三个参数可为0
if ((sock = socket(PF_INET, SOCK_DGRAM, 0))< 0);
ERR_EXIT("socket");
echo_cli(sock);
return 0;
}
#include
#include
#include
#include
#include
#include
#include //sockaddr_in
#include
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0);
void echo_srv(int sock)
{
char recvbuf[1024] = {0};
struct sockaddr_in peeraddr;
socklen_t peerlen;
int n;
while (1)
{
peerlen = sizeof(peeraddr);
memset(recvbuf, 0, sizeof(recvbuf));
//指定对方的地址信息:peeraddr,peerlen
n = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*)&peeraddr, &peerlen);
if (n == -1)
{
if (errno == EINTR)//被信号中断的话,继续接收
continue;
ERR_EXIT("recvfrom");
}
else if (n > 0)
{
fputs(recvbuf, stdout);
sendto(sock, recvbuf, n, 0, (struct sockaddr*)&peeraddr, peerlen);
}
}
close(sock);
}
int main(void)
{
int sock;
//PF_INET:IPv4的地址家族
//SOCK_DGRAM:IPv4家族中的udp套接口,所以第三个参数可为0
if ((sock = socket(PF_INET, SOCK_DGRAM, 0))< 0);
ERR_EXIT("socket");
//三个参数:地址族,端口,本机ip地址
struct sockaddr_in servaddr;
memset(&servaddr, 0, servaddr);
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
if (bind(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) <0)
ERR_EXIT("bind");
//服务端也是客户端
//若sendto中的4写成0的话,不会发送任何字节数据,但是实际上若不算数据链路层,
//发送的就是一个TCP的头部(20字节)和一个IP头部(20字节),共40字节,对方受到返回0,不代表连接的关闭
//因为udp是无连接的
sendto(sock, "ABCD", 4, 0, (struct sockaddr*)&servaddr, sizeof(servaddr));
int n;
char recvbuf[1];
int i;
for (i = 0; i < 4, i++)
{
//接收的长度sizeof(recvbuf)一定要大于发送方所发送的数据报的长度ABCD,才不会出现截断现象
//这里是出现截断了
n = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, NULL, NULL);
if (n == -1)
{
if (errno == EINTR)
continue;
ERR_EXIT("recvfrom");
}
else if (n >0)
printf("n=%d %c\n",n, recvbuf[0]);
}
return 0;
}
recvfrom返回0,不代表连接关闭,因为udp是无连接的
ICMP异步错误
(1)如何解决ICMP异步错误?
UDP也使用connet
(2)测试代码:NetworkProgramming-master (1)\LinuxNetworkProgramming\P19udpechocli.c
UDP connect
(1)代码:NetworkProgramming-master (1)\LinuxNetworkProgramming\P19udpechocli.c
(2)测试:操作:只运行客户端,
异步错误返还给已连接的套接字
UDP外出接口的确定
(1)假定客户端有多个ip
客户端 服务端
192.168.1.100 192.168.1.33
192.168.2.100 192.168.2.33
(2)若客户端sendto(sock, 192.168.1.33),则客户端udp的sock会自动选择与192.167.1.100进行绑定,则外出接口地址就会选择192.168.1.100,若没有connect,则发送给谁由sendto决定;
(3)若客户端connect(sock, 192.168.1.33),则客户端udp的sock会自动选择与192.167.1.100进行绑定,则外出接口地址就会选择192.168.1.100,这里仅仅绑定外出接口地址,而端口的绑定是第一次调用sendto时绑定的
Makefile
下面的可执行文件的编译结果,是由编译器自动推导得到的
.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
BIN=echosrv echocli trunc
all:$(BIN)
%.o:%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o $(BIN)