Linux 网络编程详解十二
UDP的特点 --无连接 --基于消息的数据传输服务 --不可靠 --UDP更加高效
UDP注意点 --UDP报文可能会丢失,重复 --UDP报文可能会乱序 --UDP缺乏流量控制(UDP缓冲区写满之后,没有流量控制,会覆盖缓冲区) --UDP协议数据报文截断(如果接收到的数据报文大于缓冲区,报文可以被截断,后面部分丢失) --recvfrom返回0,不代表连接关闭(实际上UDP是无连接的),sendto可以发送数据0包,只含有UDP头部 --UDP客户端也可以调用connect,但是并没有完成三次握手,只是维护了一个状态信息(和对等方的) --一旦调用了connect,就可以使用send函数
//UDP协议客户端 #define SETNO 0 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <termios.h> /** * clear_back - 退格不回显 * 成功返回0,失败返回-1 * */ int clear_back() { struct termios term; memset(&term, 0, sizeof(term)); //获取当前系统设置 if (tcgetattr(STDIN_FILENO, &term) == -1) { perror("tcgetattr() err"); return -1; } //修改系统设置 term.c_cc[VERASE] = '\b'; //立即生效 if (tcsetattr(STDIN_FILENO, TCSANOW, &term) == -1) { perror("tcgetattr() err"); return -1; } return 0; } int main(int arg, char *args[]) { if(clear_back()!=0) return -1; int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { perror("socket() err"); return -1; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(8080); //连接的服务器地址 addr.sin_addr.s_addr = inet_addr("127.0.0.1"); #if SETNO==0 /* * UDP客户端也可以调用connect,但是并没有完成三次握手, * 只是维护了一个状态信息(和对等方的),所以发送报文时,不需要传服务器ip*/ if(connect(sockfd,(struct sockaddr *)&addr,sizeof(addr))==-1) { perror("connect() err"); return -1; } #endif char buf[1024] = { 0 }; int rc = 0; while (fgets(buf, 1024, stdin) != NULL) { #if SETNO==0 rc = send(sockfd, buf,strlen(buf),0); #else rc = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *) &addr, sizeof(addr)); #endif if (rc == -1) { perror("sendto() err"); break; } memset(buf, 0, 1024); rc = recvfrom(sockfd, buf, 1024, 0, NULL, NULL); if (rc == -1) { perror("recvfrom() err"); break; } fputs(buf, stdout); memset(buf, 0, 1024); } return 0; }
//UDP协议服务器端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int arg, char *args[]) { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { perror("socket() err"); return -1; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(8080); //绑定本地IP地址 addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { perror("bind() err"); return -1; } int rc = 0; char buf[1024] = { 0 }; struct sockaddr_in peeraddr; socklen_t peerlen = sizeof(peeraddr); while (1) { memset(buf,0,sizeof(buf)); do { rc = recvfrom(sockfd, buf, 2, 0, (struct sockaddr *) &peeraddr, &peerlen); } while (rc == -1 && errno == EINTR); if (rc == -1) { perror("recvfrom() err"); break; } //fputs()函数必须以'\n'结尾才会打印在终端上,,否则需要清空缓冲区 fputs(buf, stdout); fflush(stdout); //发送数据 rc = sendto(sockfd, buf, rc, 0, (struct sockaddr *) &peeraddr, sizeof(peeraddr)); if (rc == -1) { perror("sendto() err"); break; } } return 0; }
.SUFFIXES:.c .o CC=gcc SRCS=hello.c OBJS=$(SRCS:.c=.o) EXEC=ser SRCS1=tec01.c OBJS1=$(SRCS1:.c=.o) EXEC1=clt start:$(OBJS) $(OBJS1) $(CC) -o $(EXEC) $(OBJS) $(CC) -o $(EXEC1) $(OBJS1) @echo "^_^-----OK------^_^" .c.o: $(CC) -Wall -g -o $@ -c $< clean: rm -f $(OBJS) rm -f $(EXEC)