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

在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

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

 

 

一、学习目标

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

 

二、复习

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

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

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

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

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

 

4、TCP 数据传输过程

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

 

5、TCP 四次挥手

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

 

6、滑动窗口

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

 

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

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

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

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

 

在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。