Home Article Practice 13_tcp

13_tcp

2024-05-12 08:32  views:239  source:拼搏百天我要上蓝翔    

知识点1【TCP客户端编程】1、TCP的概述客户端:主动连接服务器、和服务器进行通信服务器:被动被客户端连接,
启动新的线程或进程 服务器客户端(并发服务器)2、创建TCP套接字1 int sockfd = socket(
AF_INET, SOCK_STREAM, 0);socket函数创建的TCP套接字,没有端口,默认为主动连接
特性3、调用connect函数连接服务器tcp客户端通信之前 必须事先 建立和服务器的连接1 #include
<sys/types.h> /* See NOTES */2 #include <sys/socket.h>
3 int connect(int sockfd, const struct sockaddr *addr,s
ocklen_t addrlen);addr地址结构体 存放的是服务器的IP、PORT返回值:成功为0 失败-
11 //连接服务器2 struct sockaddr_in ser_addr;3 bzero(&ser_ad
dr, sizeof(ser_addr));4 ser_addr.sin_family = AF_INET;5
ser_addr.sin_port = htons(8000);6 ser_addr.sin_addr.s_
addr = inet_addr("10.9.11.251");7 connect(sockfd, (stru
ct sockaddr *)&ser_addr, sizeof(ser_addr));如果sockfd没有固定
端口 在调用connect时系统自动分配随机端口为源端口4、send发送消息1 ssize_t send(in
t sockfd, const void *buf, size_t len, int flags);2 soc
kfd:套接字3 buf:需要发送的字符串的首元素地址4 len:需要发送的字符串的实际长度5 flags:默
认为0成功返回实际发送的字节数,失败返回-1注意:TCP并不能发送0长度报文,但是UDP可以5、recv接收消
息(默认阻塞)1 #include <sys/types.h>2 #include <sys/socket.h
>3 ssize_t recv(int sockfd, void *buf, size_t len, int
flags);4 sockfd:套接字5 buf:存放收到的消息6 len:最大能接收的长度7 flags:默
认为0成功返回收到的实际字节数, 失败返回-1recv如果收到0长度报文,表明对方已经断开连接。close(s
ockfd);断开连接6、tcp客户端 收发数据1 #include <stdio.h>2 #include
<sys/socket.h>3 #include <unistd.h>4 #include <sys/type
s.h>5 #include <netinet/in.h>6 #include <string.h>7 #in
clude <arpa/inet.h>8 int main(int argc, char const *arg
v[])9 {10 //创建TCP套接字11 int sockfd = socket(AF_INET, SOC
K_STREAM, 0);12 if (sockfd < 0)13 {14 perror("socket");
15 return 0;16 }17 printf("sockfd = %d\n", sockfd);1819
//连接服务器20 struct sockaddr_in ser_addr;21 bzero(&ser_ad
dr, sizeof(ser_addr));22 ser_addr.sin_family = AF_INET;
23 ser_addr.sin_port = htons(8000);24 ser_addr.sin_addr
.s_addr = inet_addr("10.9.11.251");25 connect(sockfd, (
struct sockaddr *)&ser_addr, sizeof(ser_addr));2627 //s
end发送数据28 send(sockfd, "hello tcp", strlen("hello tcp")
, 0);2930 //recv接收数据31 unsigned char buf[1500] = "";32
int len = recv(sockfd, buf, sizeof(buf), 0);33 printf("
len=%d buf=%s\n", len, buf);3435 //关闭套接字36 close(sockfd
);3738 return 0;39 }知识点2【TCP服务器编程】1、作为服务器的条件需要bind函数 为服
务器绑定固定的端口、IP使用listen函数 让服务器套接字 由主动变被动。等待客户端的连接到来 accept
提取到来的客户端。2、listen 监听函数1 #include <sys/types.h> /* See N
OTES */2 #include <sys/socket.h>34 int listen(int sockf
d, int backlog);功能:1、将sockfd由主动变被动,并且对sockfd进行监听客户端的连接到
来2、backlog代表的是连接队列的大小(客户端的个数)3、accept提取客户端的连接(阻塞)accept
只能从连接队列中的完成部分 提取连接。1 #include <sys/types.h> /* See NOTE
S */2 #include <sys/socket.h>3 int accept(int sockfd, s
truct sockaddr *addr, socklen_t *addrlen);sockfd:监听套接字a
ddr:存放的是客户端的地址信息addrlen:地址结构长度返回值:成功返回一个已连接套接字 代表服务器和该客
户端的连接端点(真正和客户端的连接)失败:返回值-1注意:调用一次 只能提取一个客户端对应的连接,如果连接队列
没有完成的连接 将阻塞。1 #include <stdio.h>2 #include <sys/socket.
h>3 #include <unistd.h>4 #include <sys/types.h>5 #inclu
de <netinet/in.h>6 #include <string.h>7 #include <arpa/
inet.h>8 int main(int argc, char const *argv[])9 {10 //
创建tcp套接字(如果是服务器 该套接字为监听套接字)11 int lfd = socket(AF_INET,
SOCK_STREAM, 0);1213 //bind绑定固定的IP、PORT14 struct socka
ddr_in my_addr;15 bzero(&my_addr, sizeof(my_addr));16 m
y_addr.sin_family = AF_INET;17 my_addr.sin_port = htons
(9000);18 my_addr.sin_addr.s_addr = htonl(INADDR_ANY);1
920 int ret = bind(lfd, (struct sockaddr *)&my_addr, si
zeof(my_addr));21 if (ret < 0)22 {23 perror("bind");24
return 0;25 }2627 //使用listen 对lfd进行监听客户端连接到来28 listen(l
fd, 10);2930 //提取客户端的连接31 struct sockaddr_in client_add
r;32 socklen_t client_len = sizeof(client_addr);33 int
cfd = accept(lfd, (struct sockaddr *)&client_addr, &cli
ent_len);3435 char ip_str[16] = "";36 unsigned short po
rt = 0;37 inet_ntop(AF_INET, &client_addr.sin_addr.s_ad
dr, ip_str, 16);38 port = ntohs(client_addr.sin_port);3
9 printf("连接:%s %hu到来了\n", ip_str, port);4041 //从已连接套接字
cfd 获取客户端的请求42 unsigned char buf[256] = "";43 recv(cfd,
buf, sizeof(buf), 0);44 printf("客户端的请求:%s\n", buf);454
6 //服务器应答客户端47 send(cfd, "ok", 2, 0);4849 close(lfd);50
close(cfd);51 return 0;52 }知识点3【close关闭套接字】1、作为客户端clos
e(套接字):断开当前的连接,导致服务器收到0长度 报文2、作为服务器close(监听套接字):该服务器 不能
监听新的连接到来 但是不影响 已有连接close(已连接套接字):只是断开与当前客户端的连接,不会影响监听套接
字(服务器可以继续监听新的连接到来)知识点4【三次握手】(重要)当客户端调用connect连接服务器时,底层会
发起3次握手信号,当3次握手信号完成,connect才会解阻塞往下执行。SYN:置1 表示该报文是连接请求报文
FIN:置1 表示该报文是关闭连接请求报文ACK:置1 表示当前为回应报文紧急URG: 此位置1,表明紧急指针
字段有效,它告诉系统此报文段中有紧急数据,应尽快传送PSH:推送报文RST:复位连接注意:序号:当前报文的编号
确认序号:当前报文希望接下来 对方发送的报文编号知识点5【四次挥手】(重要)45是客户端,251是服务器知识点
6【并发服务器--多进程版】并发服务器:服务器可以同时服务多个客户端案例1:多进程版本---echo并发服务器
客户端 发啥 服务器 回啥1 #include <stdio.h>2 #include <stdlib.h>3
#include <errno.h> //errno全局变量4 #include <sys/socket.h
>5 #include <unistd.h>6 #include <sys/types.h>7 #includ
e <netinet/in.h>8 #include <string.h>9 #include <arpa/i
net.h>10 #include <signal.h>11 #include <sys/wait.h>12
//信号的注册函数13 void waitpid_func(int signo)14 {15 while (1
)16 {17 pid_t pid = waitpid(‐1, NULL, WNOHANG);18 if (p
id > 0)19 {20 printf("子进程%d退出了\n", pid);21 }22 else if
(pid <= 0)23 break;24 }25 }26 void deal_client_fun(int
cfd)27 {28 while (1)29 {30 unsigned char buf[1500] = ""
;31 int len = recv(cfd, buf, sizeof(buf), 0);32 if (len
<= 0) //客户端断开连接 会导致 服务器收到0长度报文33 break;34 send(cfd, bu
f, len, 0);35 }3637 return;38 }39 int main(int argc, ch
ar const *argv[])40 {41 if (argc != 2)42 {43 printf("./
a.out 8000\n");44 return 0;45 }4647 //创建TCP服务器48 int lf
d = socket(AF_INET, SOCK_STREAM, 0);49 if (lfd < 0)50 {
51 perror("socket");52 _exit(‐1);53 }5455 //绑定固定端口56 st
ruct sockaddr_in my_addr;57 bzero(&my_addr, sizeof(my_a
ddr));58 my_addr.sin_family = AF_INET;59 my_addr.sin_po
rt = htons(atoi(argv[1]));60 my_addr.sin_addr.s_addr =
htonl(INADDR_ANY);61 int ret = bind(lfd, (struct sockad
dr *)&my_addr, sizeof(my_addr));62 if (ret < 0)63 {64 p
error("bind");65 _exit(‐1);66 }6768 //监听套接字69 listen(lf
d, 10);7071 struct sockaddr_in client_addr;72 socklen_t
client_len = sizeof(client_addr);7374 //给SIGCHLD信号注册 处
理函数75 signal(SIGCHLD, waitpid_func);//防止子进程出现僵尸进程7677 /
/循环提取客户端78 while (1)79 {80 int cfd = accept(lfd, (struc
t sockaddr *)&client_addr, &client_len);81 if (cfd < 0)
82 {83 if ((errno == ECONNABORTED) || (errno == EINTR))
84 continue;85 perror("accept");86 }87 else //提取到正常的客户端
88 {89 char ip_str[16] = "";90 unsigned short port = 0;
91 inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_
str, 16);92 port = ntohs(client_addr.sin_port);93 print
f("连接:%s %hu到来了\n", ip_str, port);9495 pid_t pid = fork
();96 if (pid == 0) //子进程 服务客户端97 {98 //子进程中lfd无意义99 cl
ose(lfd);100101 //子进程 对客户端的 应答程序102 deal_client_fun(cfd
);103104 //关闭 已连接套接诶自己cfd105 close(cfd);106107 //退出108
_exit(‐1);109 }110 else //父进程 继续建立提取新的客户端111 {112 //已连接
套接字 无意义113 close(cfd);114 }115 }116 }117118 //关闭监听套接字11
9 close(lfd);120121 return 0;122 }知识点7【端口复用概述】(了解)默认的情况
下,如果一个网络应用程序的一个套接字 绑定了一个端口( 占用了 8000 ),这时候,别的套接字就无法使用这个
端口( 8000 )端口复用:允许在一个应用程序可以把 n 个套接字绑在一个端口上而不出错SO_REUSEAD
DR可以用在以下四种情况下。 (摘自《Unix网络编程》卷一,即UNPv1)1、当有一个有相同本地地址和端口的
socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到
该选项。2、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但每个实例绑定的I
P地址是不能相同的。在有多块网卡或用IP Alias技术的机器可以测试这种情况。3、SO_REUSEADDR允
许单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。这和2很相似,区别请看UN
Pv1。4、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP注
意:置端口复用函数要在绑定之前调用,而且只要绑定到同一个端口的所有套接字都得设置复用知识点8【设置套接字端口复
用】(了解)1 int opt = 1;2 setsockopt(sockfd, SOL_SOCKET, SO
_REUSEADDR, &opt, sizeof(opt));上面的sockfd为需要使用同一端口复用的的套接
字知识点9【多线程并发服务器】多线程版本---echo并发服务器1 #include <stdio.h>2 #
include <stdlib.h>3 #include <errno.h> //errno全局变量4 #in
clude <sys/socket.h>5 #include <unistd.h>6 #include <sy
s/types.h>7 #include <netinet/in.h>8 #include <string.h
>9 #include <arpa/inet.h>10 #include <signal.h>11 #incl
ude <pthread.h>12 typedef struct13 {14 int cfd;15 char
ip_str[16];16 unsigned short port;17 } CLI_MSG;18 int l
fd = 0;19 //信号的注册函数20 void exit_func(int signo)21 {22 c
lose(lfd);23 _exit(‐1);24 }25 void *deal_client_fun(voi
d *arg)26 {27 CLI_MSG tmp = *(CLI_MSG *)arg;28 printf("
连接:%s %hu到来了\n", tmp.ip_str, tmp.port);2930 while (1)31
{32 unsigned char buf[1500] = "";33 int len = recv(tmp
.cfd, buf, sizeof(buf), 0);34 if (len <= 0) //客户端断开连接 会
导致 服务器收到0长度报文35 {36 printf("连接:%s %hu退出了\n", tmp.ip_str
, tmp.port);37 close(tmp.cfd);38 break; //一定要记得<=039 }4
041 send(tmp.cfd, buf, len, 0);42 }4344 pthread_exit(NU
LL);45 return NULL;46 }47 int main(int argc, char const
*argv[])48 {49 if (argc != 2)50 {51 printf("./a.out 80
00\n");52 return 0;53 }5455 //创建TCP服务器56 lfd = socket(A
F_INET, SOCK_STREAM, 0);57 if (lfd < 0)58 {59 perror("s
ocket");60 _exit(‐1);61 }6263 //端口复用64 int opt = 1;65 s
etsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(o
pt));6667 //绑定固定端口68 struct sockaddr_in my_addr;69 bzer
o(&my_addr, sizeof(my_addr));70 my_addr.sin_family = AF
_INET;71 my_addr.sin_port = htons(atoi(argv[1]));72 my_
addr.sin_addr.s_addr = htonl(INADDR_ANY);73 int ret = b
ind(lfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
74 if (ret < 0)75 {76 perror("bind");77 _exit(‐1);78 }7
980 //监听套接字81 listen(lfd, 10);8283 struct sockaddr_in c
lient_addr;84 socklen_t client_len = sizeof(client_addr
);8586 //给SIGINT信号注册 处理函数87 signal(SIGINT, exit_func);
//结束服务器时关闭监听套接字8889 //循环提取客户端90 while (1)91 {92 int cfd
= accept(lfd, (struct sockaddr *)&client_addr, &client
_len);93 if (cfd < 0)94 {95 if ((errno == ECONNABORTED)
|| (errno == EINTR))96 continue;97 perror("accept");98
}99 else //提取到正常的客户端100 {101 CLI_MSG tmp;102 tmp.cfd =
cfd;103 inet_ntop(AF_INET, &client_addr.sin_addr.s_add
r, tmp.ip_str,6);104 tmp.port = ntohs(client_addr.sin_p
ort);105106 //创建线程服务器客户端107 pthread_t tid;108 pthread_c
reate(&tid, NULL, deal_client_fun, (void *)&tmp);109 pt
hread_detach(tid);110 }111 }112113 //关闭监听套接字114 close(l
fd);115116 return 0;117 }知识点10【web服务器】html(超文本标记语言)显示文本
http(超文本传送协议) 传输协议URL 统一地址定位符web服务器使用的传送协议:http(超文本传送协议
) 基于TCP客户端是浏览器 服务器(用户实现)浏览器只需要提供请求的方式(GET\POST)每一个客户端 只
能有一个请求浏览器的请求方式:解析文件名,打开本地文件(成功、失败)服务器应答的格式:请求失败HTTP传送文件
没有固定大小限制。1 #include <stdio.h>2 #include <stdlib.h>3 #in
clude <errno.h> //errno全局变量4 #include <sys/socket.h>5 #
include <unistd.h>6 #include <sys/types.h>7 #include <n
etinet/in.h>8 #include <string.h>9 #include <arpa/inet.
h>10 #include <signal.h>11 #include <pthread.h>12 #incl
ude <sys/stat.h>13 #include <fcntl.h>14 char err[] = "H
TTP/1.1 404 Not Found\r\n"15 "Content‐Type: text/html\r
\n"16 "\r\n"17 "<HTML><BODY>File not found</BODY></HTML
>";18 char head[] = "HTTP/1.1 200 OK\r\n"19 "Content‐Ty
pe: text/html\r\n"20 "\r\n";21 typedef struct22 {23 int
cfd;24 char ip_str[16];25 unsigned short port;26 } CLI
_MSG;27 int lfd = 0;28 //信号的注册函数29 void exit_func(int s
igno)30 {31 close(lfd);32 _exit(‐1);33 }3435 //客户端核心任务线
程函数36 void *deal_client_fun(void *arg)37 {38 CLI_MSG tm
p = *(CLI_MSG *)arg;39 printf("连接:%s %hu到来了\n", tmp.ip_
str, tmp.port);4041 //获取浏览器的请求42 unsigned char cmd_buf[
1024] = "";43 int len = recv(tmp.cfd, cmd_buf, sizeof(c
md_buf), 0);44 if (len <= 0)45 {46 close(tmp.cfd);47 pt
hread_exit(NULL);48 }4950 //解析浏览器的请求51 char file_name[1
28] = "./html/";52 sscanf(cmd_buf, "GET /%[^ ]", file_n
ame + 7);53 if (file_name[7] == '\0')54 {55 strcat(file
_name, "index.html");56 }57 printf("file_name=##%s##\n"
, file_name);5859 //open打开本地文件60 int fd = open(file_nam
e, O_RDONLY);61 if (fd < 0)62 {63 //告诉浏览器40464 send(tmp
.cfd, err, strlen(err), 0);6566 close(tmp.cfd);67 perro
r("open");68 pthread_exit(NULL);69 }7071 //告诉浏览器 200 打开
成功准备接受72 send(tmp.cfd, head, strlen(head), 0);7374 //循环
读取本地文件数据发送给浏览器75 while (1)76 {77 unsigned char buf[512]
= "";78 //读取本地文件数据79 int len = read(fd, buf, sizeof(bu
f));80 send(tmp.cfd, buf, len, 0);81 if (len < 512)82 b
reak;83 }8485 close(fd);86 close(tmp.cfd);8788 pthread_
exit(NULL);89 return NULL;90 }91 int main(int argc, cha
r const *argv[])92 {93 if (argc != 2)94 {95 printf("./a
.out 8000\n");96 return 0;97 }9899 //创建TCP服务器100 lfd =
socket(AF_INET, SOCK_STREAM, 0);101 if (lfd < 0)102 {10
3 perror("socket");104 _exit(‐1);105 }106107 //端口复用108
int opt = 1;109 setsockopt(lfd, SOL_SOCKET, SO_REUSEADD
R, &opt, sizeof(opt));110111 //绑定固定端口112 struct sockadd
r_in my_addr;113 bzero(&my_addr, sizeof(my_addr));114 m
y_addr.sin_family = AF_INET;115 my_addr.sin_port = hton
s(atoi(argv[1]));116 my_addr.sin_addr.s_addr = htonl(IN
ADDR_ANY);117 int ret = bind(lfd, (struct sockaddr *)&m
y_addr, sizeof(my_addr));118 if (ret < 0)119 {120 perro
r("bind");121 _exit(‐1);122 }123124 //监听套接字125 listen(l
fd, 10);126127 struct sockaddr_in client_addr;128 sockl
en_t client_len = sizeof(client_addr);129130 //给SIGINT信
号注册 处理函数131 signal(SIGINT, exit_func); //结束服务器时关闭监听套接字1
32133 //循环提取客户端134 while (1)135 {136 int cfd = accept(l
fd, (struct sockaddr *)&client_addr, &client_len);137 i
f (cfd < 0)138 {139 if ((errno == ECONNABORTED) || (err
no == EINTR))140 continue;141 perror("accept");142 }143
else //提取到正常的客户端144 {145 CLI_MSG tmp;146 tmp.cfd = cfd
;147 inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, t
mp.ip_str,6);148 tmp.port = ntohs(client_addr.sin_port)
;149150 //创建线程服务器客户端151 pthread_t tid;152 pthread_creat
e(&tid, NULL, deal_client_fun, (void *)&tmp);153 pthrea
d_detach(tid);154 }155 }156157 //关闭监听套接字158 close(lfd);
159160 return 0



Disclaimer: The above articles are added by users themselves and are only for typing and communication purposes. They do not represent the views of this website, and this website does not assume any legal responsibility. This statement is hereby made! If there is any infringement of your rights, please contact us promptly to delete it.

字符:    改为:
去打字就可以设置个性皮肤啦!(O ^ ~ ^ O)