首页 > 系统相关 >Linux高并发网络编程开发——tcp三次握手-并发

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

时间:2022-11-08 20:03:02浏览次数:47  
标签:serv addr int tcp 并发 fd Linux include buf

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

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

 

 

一、学习目标

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

 

二、复习

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

    

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三次握手-并发_#include_08

 

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

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

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

    

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

 

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

标签:serv,addr,int,tcp,并发,fd,Linux,include,buf
From: https://blog.51cto.com/u_15405812/5834776

相关文章

  • Linux 中使用脚本启动 Java 服务
    Linux中使用脚本启动Java服务#!/bin/sh#服务启动参数#JAVA_OPTS="-Xms512m-Xmx512m-XX:MetaspaceSize=512m-XX:MaxMetaspaceSize=1024m-XX:ParallelGCThreads=......
  • dotnet Core 在linux 下设置成Service
    1、新建.service文件cd/etc/systemd/system//进入改目录touchCore.service//新建Core服务文件viCore.service//编辑2、插入下面代码注意自己的服务名,以及项......
  • 常用的并发工具类
    CountDownLatch概念CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。CountDownLatch能够使一个线程在等待另外一......
  • 20221107 24. Linux 核心编译与管理
    24.1编译前的任务:认识核心与取得核心源代码“核心(kernel)”是整个操作系统的最底层,他负责了整个硬件的驱动,以及提供各种系统所需的核心功能,包括防火墙机制、是否支持LVM......
  • 20220802 鸟哥 Linux 私房菜【归档】
    参考资料鸟哥Linux私房菜-基础学习篇(第四版)鳥哥私房菜-基礎學習篇目錄-forCentOS7环境信息CentOS7.x书中使用版本:7.1练习使用版本:7.9目录00.计算......
  • 20220803 01. Linux是什么与如何学习
    1.1Linux是什么Linux就是一组软件1.1.1Linux是什么?操作系统/应用程序?Linux就是一套操作系统Linux就是核心与系统调用接口那两层早期的Linux是针对386来开发的Tor......
  • 【Linux相关】安全漏洞处理
    一、前言近期甲方使用漏洞软件扫描网络环境下所有主机,扫描出一大堆安全漏洞,最好的处理方式当然是升级系统相关组件至最新的软件版本。然而,整个软件系统引用了部分第三方......
  • 20220810 06. Linux 文件与目录管理
    6.1目录与路径6.1.1相对路径与绝对路径路径(PATH)绝对路径:路径的写法“一定由根目录/写起”,例如:/usr/share/doc这个目录。相对路径:路径的写法“不是由/写起......
  • Linux 服务器使用git 作为仓库
    https://www.cnblogs.com/JayYang/p/16436643.html https://www.cnblogs.com/JayYang/p/16436643.html https://blog.csdn.net/qq_40692629/article/details/1244209......
  • xampp配置多域名泛域名虚拟主机For linux xampp wampp 多域名设置只有第一个生效的
     安装XAMPP下载xampp最新版FORLINUX#wget​​http://nchc.dl.sourceforge.net/sourceforge/xampp/xampp-linux-1.7.1.tar.gz​​安装:tarxvfzxampp-linux-1.7.1.tar.g......