TCP协议是面向流的协议,在底层通信中这些数据可以被拆成许多数据包发送,但是一个数据包有多少字节对应用程序是不可见的。
TCP接收到的数据需要做分包处理(无法区分每一包数据)。
注:TCP客户端发送的数据拆包处理,则TCP服务器端只有完整接收到数据包后才会通过read上报上层可以read数据了。
比如发送端发送了一包数据,被查分成3包:序号1,序号2,序号3。由于某种原因,3个包未全部收到时,上层read不会返回(阻塞)或无事件(socket不可读,非阻塞)。只有3个包都收到后,Socket才可read。若包未收取成功,会要求对端重发。长时间未收到包,怎样处理????若阻塞,则一直等待;若非阻塞,则不会触发读时间。当判断对端断开后,read()返回0。
//tcp server #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/un.h> #include <sys/socket.h> #include <sys/stat.h> #include <errno.h> #define BUF_SIZE 10 #define DES_PATH "/tmp/main.socket" int main(int argc, char *argv[]) { int sd, cfd; struct sockaddr_un un, peer_un; socklen_t len; int i; int ret; char buf[BUF_SIZE]; sd = socket(AF_UNIX, SOCK_STREAM, 0); if(sd < 0) { perror("socket"); return -1; } unlink(DES_PATH); memset(&un, 0, sizeof(struct sockaddr_un)); un.sun_family = AF_UNIX; strncpy(un.sun_path, DES_PATH, sizeof(un.sun_path) - 1); ret = bind(sd, (struct sockaddr *)&un, sizeof(struct sockaddr_un)); if(ret < 0) { perror("bind"); return -1; } ret = listen(sd, 100); while(1) { len = sizeof(struct sockaddr_un); cfd = accept(sd, (struct sockaddr *)&peer_un, &len); if(cfd < 0) { perror("accept"); return -1; } while(1) { memset(buf, 0, BUF_SIZE); sleep(10); ret = read(cfd, buf, BUF_SIZE); if(ret > 0) { printf("Recvfrom [%d] bytes from >>%s: ", ret, peer_un.sun_path); for(i = 0; i < BUF_SIZE; i++) { printf("0x%.2x ", 0xFF&buf[i]); if(0 == (i + 1) % 5) { printf(" "); } } } else if(ret == 0){ printf("peer close "); close(cfd); break; } else { if(errno == EINTR) { break; } else { perror("read"); return -1; } } } } close(sd); return 0; }
// tcp client #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stddef.h> #include <fcntl.h> #include <time.h> #include <sys/un.h> #include <sys/socket.h> #include <sys/stat.h> int main(int argc, char *argv[]) { int sd; struct sockaddr_un un; socklen_t len; int tnode; int ret ; if(argc < 2) { return -1; } tnode = atoi(argv[1]); sd = socket(AF_UNIX, SOCK_STREAM, 0); if(sd < 0) { perror("socket"); return -1; } memset(&un, 0, sizeof(struct sockaddr_un)); un.sun_family = AF_UNIX; // strcpy(un.sun_path, "/tmp/main.socket"); snprintf(un.sun_path, sizeof(struct sockaddr_un), "%u.%u.sock", time(NULL), getpid()); printf("sockaddr is %s ", un.sun_path); ret = bind(sd, (struct sockaddr *)&un, sizeof(struct sockaddr_un)); if(ret < 0) { perror("bind"); return -1; } memset(&un, 0, sizeof(struct sockaddr_un)); un.sun_family = AF_UNIX; strcpy(un.sun_path, "/tmp/main.socket"); len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path) + 1; ret = connect(sd, (struct sockaddr *)&un, len); if(ret < 0) { perror("connect"); return -1; } else { write(sd, &tnode, sizeof(int)); write(sd, &tnode, sizeof(int)); write(sd, &tnode, sizeof(int)); } close(sd); return 0; }
~$./tc 1000 sockaddr is 1480431031.3755.sock ~$./tc 100 sockaddr is 1480431034.3756.sock
~$./ts Recvfrom [10] bytes from >>1480431031.3755.sock: 0xe8 0x03 0x00 0x00 0xe8 0x03 0x00 0x00 0xe8 0x03 Recvfrom [2] bytes from >>1480431031.3755.sock: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 peer close Recvfrom [10] bytes from >>1480431034.3756.sock: 0x64 0x00 0x00 0x00 0x64 0x00 0x00 0x00 0x64 0x00 Recvfrom [2] bytes from >>1480431034.3756.sock: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 peer close
在server端读数据前延迟一段时间如10s,在client端一个sock多次write相同数据,server读取数据为当前缓冲区中所有数据(多个包已合并),可知TCP需做分包处理。
UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据。
UDP每读取一次都是一包数据(UDP已做分包处理),无需再做分包处理。
具体事例可参考“Unix domain socket IPC”。