在学习Linux高并发网络编程开发总结了笔记,并分享出来。

10-Linux系统编程-第11天(tcp三次握手-并发)

 

 

一、学习目标

1、熟练掌握三次握手建立连接过程
2、熟练掌握四次挥手断开连接过程
3、掌握滑动窗口概念
4、掌握错误处理函数封装
5、实现多进程并发服务器
6、实现多线程并发服务

 

二、复习

Linux高并发网络编程开发——tcp三次握手-并发_#include

    

Linux高并发网络编程开发——tcp三次握手-并发_服务器_02

三、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次握手

Linux高并发网络编程开发——tcp三次握手-并发_服务器_03

Linux高并发网络编程开发——tcp三次握手-并发_字节数_04

       

Linux高并发网络编程开发——tcp三次握手-并发_其他_05

 

4、TCP 数据传输过程

Linux高并发网络编程开发——tcp三次握手-并发_字节数_06

 

5、TCP 四次挥手

Linux高并发网络编程开发——tcp三次握手-并发_#include_07

 

6、滑动窗口

Linux高并发网络编程开发——tcp三次握手-并发_其他_08

 

7、多进程并发服务器分析

Linux高并发网络编程开发——tcp三次握手-并发_字节数_09

Linux高并发网络编程开发——tcp三次握手-并发_#include_10

    

Linux高并发网络编程开发——tcp三次握手-并发_字节数_11

 

在学习Linux高并发网络编程开发总结了笔记,并分享出来。