Linux高并发网络编程开发——tcp三次握手-并发
在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
10-Linux系统编程-第11天(tcp三次握手-并发)
一、学习目标
1、熟练掌握三次握手建立连接过程
2、熟练掌握四次挥手断开连接过程
3、掌握滑动窗口概念
4、掌握错误处理函数封装
5、实现多进程并发服务器
6、实现多线程并发服务
二、复习
三、TCP
1、TCP服务器端和客户端代码实现
》TCP服务器端
>touch tcp_server.c
>vi tcp_server.c
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <string.h> 7 #include <sys/socket.h> 8 #include <arpa/inet.h> 9 #include <ctype.h> 10 11 12 int main(int argc, const char* argv[]) 13 { 14 // 创建用于监听的套节字 15 int lfd = socket(AF_INET, SOCK_STREAM, 0); 16 if(lfd == -1) 17 { 18 perror("socket error"); 19 exit(1); 20 } 21 22 // 绑定 23 struct sockaddr_in serv_addr; 24 // init 25 memset(&serv_addr, 0, sizeof(serv_addr)); 26 // bzero(&serv_addr, sizeof(serv_addr)); 27 serv_addr.sin_family = AF_INET; // 地址族协议 ipv4 28 serv_addr.sin_port = htons(9999); // 本地端口, 需要转换为大端 29 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 0 是用本机的任意IP 30 31 int ret = bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); 32 if(ret == -1) 33 { 34 perror("bind error"); 35 exit(1); 36 } 37 38 // 设置监听 39 ret = listen(lfd, 64); 40 if(ret == -1) 41 { 42 perror("listen error"); 43 exit(1); 44 } 45 46 // 等待并接受连接请求 47 struct sockaddr_in cline_addr; 48 socklen_t clien_len = sizeof(cline_addr); 49 int cfd = accept(lfd, (struct sockaddr*)&cline_addr, &clien_len); 50 if(cfd == -1) 51 { 52 perror("accept error"); 53 exit(1); 54 } 55 56 char ipbuf[64]; 57 // int -> char* 58 printf("cliient ip: %s, port: %d\n", 59 inet_ntop(AF_INET, &cline_addr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)), 60 ntohs(cline_addr.sin_port)); 61 62 // 通信 63 while(1) 64 { 65 // 先接收数据 66 char buf[1024] = {0}; 67 int len = read(cfd, buf, sizeof(buf)); 68 if(len == -1) 69 { 70 perror("read error"); 71 break; 72 } 73 else if(len > 0) 74 { 75 // 顺利读出了数据 76 printf("read buf = %s\n", buf); 77 // 小写 -》 大写 78 for(int i=0; i<len; ++i) 79 { 80 buf[i] = toupper(buf[i]); 81 } 82 printf(" -- toupper: %s\n", buf); 83 84 // 数据发送给客户端 85 write(cfd, buf, strlen(buf)+1); 86 } 87 else if( len == 0 ) 88 { 89 printf("client disconnect ...\n"); 90 break; 91 } 92 } 93 94 close(lfd); 95 close(cfd); 96 97 return 0; 98 }
>gcc tcp_server.c -o server
》TCP客户端
>touch tcp_client.c
>vi tcp_client.c
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <string.h> 7 #include <arpa/inet.h> 8 #include <fcntl.h> 9 10 // tcp client 11 int main(int argc, const char* argv[]) 12 { 13 if(argc < 2) 14 { 15 printf("eg: ./a.out port\n"); 16 exit(1); 17 } 18 19 int port = atoi(argv[1]); 20 // 创建套接字 ( AF_INET为IPv4) 21 int fd = socket(AF_INET, SOCK_STREAM, 0);//查文档 :! man 'socket' 22 if(fd == -1) 23 { 24 perror("socket error"); 25 exit(1); 26 } 27 28 29 // 连接服务器 30 struct sockaddr_in serv_addr; 31 memset(&serv_addr, 0, sizeof(serv_addr)); 32 serv_addr.sin_family = AF_INET; 33 serv_addr.sin_port = htons(port); 34 //serv_addr.sin_addr.s_addr = htonl();//htonl括号中只能放整型,所以换用inet_pton 35 inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr); 36 int ret = connect(fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); 37 if(ret == -1) 38 { 39 perror("connect error"); 40 exit(1); 41 } 42 43 // 通信 44 while(1) 45 { 46 // 发送数据 47 // 接收键盘输入 48 char buf[1024]; 49 printf("请输入要发送的字符串:\n"); 50 fgets(buf, sizeof(buf), stdin); 51 // 发送给服务器 52 write(fd, buf, strlen(buf)+1); 53 54 // 等待接收服务器端的数据 55 int len = read(fd, buf, sizeof(buf)); 56 if(len == -1) 57 { 58 perror("read error"); 59 exit(1); 60 } 61 else if(len == 0) 62 { 63 printf("服务器端关闭了连接\n"); 64 break; 65 } 66 else 67 { 68 printf("read buf = %s, len = %d\n", buf, len); 69 } 70 } 71 close(fd); 72 73 return 0; 74 }
>gcc tcp_client.c -o client
>./server
(打开另一个终端,切换到目录下,运行./client 9999,然后输入要发送的字符串:hello,会收到服务器转换为大写的HELLO;查看原终端server,可以看到client IP:127.0.0.1, port: 34844, 收到字符串:hello,发送HELLO)
2、socket 函数封装
函数的封装在wrap.c和wrap.h,函数的调用在client.c和server.c中
理解
wrap.h
1 #ifndef __WRAP_H_ 2 #define __WRAP_H_ 3 4 void perr_exit(const char *s); 5 int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr); 6 int Bind(int fd, const struct sockaddr *sa, socklen_t salen); 7 int Connect(int fd, const struct sockaddr *sa, socklen_t salen); 8 int Listen(int fd, int backlog); 9 int Socket(int family, int type, int protocol); 10 ssize_t Read(int fd, void *ptr, size_t nbytes); 11 ssize_t Write(int fd, const void *ptr, size_t nbytes); 12 int Close(int fd); 13 ssize_t Readn(int fd, void *vptr, size_t n); 14 ssize_t Writen(int fd, const void *vptr, size_t n); 15 ssize_t my_read(int fd, char *ptr); 16 ssize_t Readline(int fd, void *vptr, size_t maxlen); 17 18 #endif
wrap.c
1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 #include <errno.h> 5 #include <sys/socket.h> 6 //错误输出 7 void perr_exit(const char *s) 8 { 9 perror(s); 10 exit(-1); 11 } 12 //接受 13 int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) 14 { 15 int n; 16 17 again: 18 if ((n = accept(fd, sa, salenptr)) < 0) 19 { 20 //ECONNABORTED 发生在重传(一定次数)失败后,强制关闭套接字 21 //EINTR 进程被信号中断 22 if ((errno == ECONNABORTED) || (errno == EINTR)) 23 { 24 goto again; 25 } 26 else 27 { 28 perr_exit("accept error"); 29 } 30 } 31 return n; 32 } 33 //绑定 34 int Bind(int fd, const struct sockaddr *sa, socklen_t salen) 35 { 36 int n; 37 38 if ((n = bind(fd, sa, salen)) < 0) 39 { 40 perr_exit("bind error"); 41 } 42 43 return n; 44 } 45 //连接 46 int Connect(int fd, const struct sockaddr *sa, socklen_t salen) 47 { 48 int n; 49 n = connect(fd, sa, salen); 50 if (n < 0) 51 { 52 perr_exit("connect error"); 53 } 54 55 return n; 56 } 57 58 int Listen(int fd, int backlog) 59 { 60 int n; 61 62 if ((n = listen(fd, backlog)) < 0) 63 { 64 perr_exit("listen error"); 65 } 66 67 return n; 68 } 69 70 int Socket(int family, int type, int protocol) 71 { 72 int n; 73 74 if ((n = socket(family, type, protocol)) < 0) 75 { 76 perr_exit("socket error"); 77 } 78 79 return n; 80 } 81 82 ssize_t Read(int fd, void *ptr, size_t nbytes) 83 { 84 ssize_t n; 85 86 again: 87 if ( (n = read(fd, ptr, nbytes)) == -1) //判断是否阻塞 88 { 89 if (errno == EINTR)//判断是否被信号中断 90 goto again; 91 else 92 return -1; 93 } 94 95 return n; 96 } 97 98 ssize_t Write(int fd, const void *ptr, size_t nbytes) 99 { 100 ssize_t n; 101 102 again: 103 if ((n = write(fd, ptr, nbytes)) == -1) //有可能写缓冲区满了,阻塞,等待 104 { 105 if (errno == EINTR)//判断是否被信号中断 106 goto again; 107 else 108 return -1; 109 } 110 return n; 111 } 112 113 int Close(int fd) 114 { 115 int n; 116 if ((n = close(fd)) == -1) 117 perr_exit("close error"); 118 119 return n; 120 } 121 122 /*参三: 应该读取的字节数*/ 123 //socket 4096 readn(cfd, buf, 4096) nleft = 4096-1500 124 ssize_t Readn(int fd, void *vptr, size_t n) 125 { 126 size_t nleft; //usigned int 剩余未读取的字节数 127 ssize_t nread; //int 实际读到的字节数 128 char *ptr; 129 130 ptr = vptr; 131 nleft = n; //n 未读取字节数 132 133 while (nleft > 0) 134 { 135 if ((nread = read(fd, ptr, nleft)) < 0) 136 { 137 if (errno == EINTR) 138 { 139 nread = 0; 140 } 141 else 142 { 143 return -1; 144 } 145 } 146 else if (nread == 0) 147 { 148 break; 149 } 150 151 nleft -= nread; //nleft = nleft - nread 152 ptr += nread; 153 } 154 return n - nleft; 155 } 156 157 ssize_t Writen(int fd, const void *vptr, size_t n) 158 { 159 size_t nleft; 160 ssize_t nwritten; 161 const char *ptr; 162 163 ptr = vptr; 164 nleft = n; 165 while (nleft > 0) 166 { 167 if ( (nwritten = write(fd, ptr, nleft)) <= 0) 168 { 169 if (nwritten < 0 && errno == EINTR) 170 nwritten = 0; 171 else 172 return -1; 173 } 174 nleft -= nwritten; 175 ptr += nwritten; 176 } 177 return n; 178 } 179 180 static ssize_t my_read(int fd, char *ptr)//静态函数 181 { 182 static int read_cnt;//静态变量 183 static char *read_ptr; 184 static char read_buf[100]; 185 186 if (read_cnt <= 0) { 187 again: 188 if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) //"hello\n" 189 { 190 if (errno == EINTR) 191 goto again; 192 return -1; 193 } 194 else if (read_cnt == 0) 195 return 0; 196 197 read_ptr = read_buf; 198 } 199 read_cnt--; 200 *ptr = *read_ptr++; 201 202 return 1; 203 } 204 205 /*readline --- fgets*/ 206 //传出参数 vptr 207 ssize_t Readline(int fd, void *vptr, size_t maxlen) 208 { 209 ssize_t n, rc; 210 char c, *ptr; 211 ptr = vptr; 212 213 for (n = 1; n < maxlen; n++) 214 { 215 if ((rc = my_read(fd, &c)) == 1) //ptr[] = hello\n 216 { 217 *ptr++ = c; 218 if (c == '\n') 219 break; 220 } 221 else if (rc == 0) 222 { 223 *ptr = 0; 224 return n-1; 225 } 226 else 227 return -1; 228 } 229 *ptr = 0; 230 231 return n; 232 }
server.c
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <strings.h> 6 #include <string.h> 7 #include <ctype.h> 8 #include <arpa/inet.h> 9 10 #include "wrap.h" 11 12 #define SERV_PORT 6666 13 14 int main(void) 15 { 16 int sfd, cfd; 17 int len, i; 18 char buf[BUFSIZ], clie_IP[128]; 19 20 struct sockaddr_in serv_addr, clie_addr; 21 socklen_t clie_addr_len; 22 23 sfd = Socket(AF_INET, SOCK_STREAM, 0); 24 25 bzero(&serv_addr, sizeof(serv_addr)); 26 serv_addr.sin_family = AF_INET; 27 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); 28 serv_addr.sin_port = htons(SERV_PORT); 29 30 Bind(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 31 32 Listen(sfd, 2); 33 34 printf("wait for client connect ...\n"); 35 36 clie_addr_len = sizeof(clie_addr_len); 37 cfd = Accept(sfd, (struct sockaddr *)&clie_addr, &clie_addr_len); 38 printf("cfd = ----%d\n", cfd); 39 40 printf("client IP: %s port:%d\n", 41 inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)), 42 ntohs(clie_addr.sin_port)); 43 44 while (1) 45 { 46 len = Read(cfd, buf, sizeof(buf)); 47 Write(STDOUT_FILENO, buf, len); 48 49 for (i = 0; i < len; i++) 50 buf[i] = toupper(buf[i]); 51 Write(cfd, buf, len); 52 } 53 54 Close(sfd); 55 Close(cfd); 56 57 return 0; 58 }
client.c
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <string.h> 4 #include <sys/socket.h> 5 #include <arpa/inet.h> 6 7 #include "wrap.h" 8 9 #define SERV_IP "127.0.0.1" 10 #define SERV_PORT 6666 11 12 int main(void) 13 { 14 int sfd, len; 15 struct sockaddr_in serv_addr; 16 char buf[BUFSIZ]; 17 18 sfd = Socket(AF_INET, SOCK_STREAM, 0); 19 20 bzero(&serv_addr, sizeof(serv_addr)); 21 serv_addr.sin_family = AF_INET; 22 inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); 23 serv_addr.sin_port = htons(SERV_PORT); 24 25 Connect(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 26 27 while (1) { 28 fgets(buf, sizeof(buf), stdin); 29 int r = Write(sfd, buf, strlen(buf)); 30 printf("Write r ======== %d\n", r); 31 len = Read(sfd, buf, sizeof(buf)); 32 printf("Read len ========= %d\n", len); 33 Write(STDOUT_FILENO, buf, len); 34 } 35 36 Close(sfd); 37 38 return 0; 39 }
makefile
1 src = $(wildcard *.c) 2 obj = $(patsubst %.c, %.o, $(src)) 3 4 all: server client 5 6 server: server.o wrap.o 7 gcc server.o wrap.o -o server -Wall 8 client: client.o wrap.o 9 gcc client.o wrap.o -o client -Wall 10 11 %.o:%.c 12 gcc -c $< -Wall 13 14 .PHONY: clean all 15 clean: 16 -rm -rf server client $(obj)
3、TCP 3次握手
4、TCP 数据传输过程
5、TCP 四次挥手
6、滑动窗口
7、多进程并发服务器分析
在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。