管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。$ H# f# c$ w5 ^' A: \' u( E1 F
1 H9 b6 k7 y$ t5 j4 R1 e; S6 K% w1 W" ~- _
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。* }" J3 q* P4 I
' X W l3 |' \1 z7 Q( K/ z4 j# [" o) o+ K/ \
TCP协议3 @4 E! W% I d5 d( t
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。/ F4 W$ A) @( o" i1 D/ @
! k$ o7 G: l1 `; P1 B
6 n( I3 O5 j+ t1 D9 e# f% v0 q
关键词:三次握手,可靠,基于字节流。: N! K" v* H, f
% U# t. H3 \% q; h. \+ ~
6 |+ _" R% B& p4 N" ]可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
m% I1 |2 Y2 ] n& _
% k; ?) F$ `8 L( T" C7 K0 Y
TCP服务器端和客户端的运行流程
" N2 }+ T' J1 _- \. Y( L# \! K
2 k& g) t5 ]. V- J4 C, `" s! M; l$ `+ o& N4 H, }0 d) T: ?7 `) _! ^
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
6 f. K/ Y B1 U7 c# D' I8 E7 B& ?1 p
$ k4 K! {1 S8 T& b! u
1.创建socket
* Q9 d( y8 L% O6 _# c2 \ socket是一个结构体,被创建在内核中' y' u) X, V/ l
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
. v8 M% u5 h! x2 H# z
( C2 K& `5 z) q0 Q b8 Y6 g7 }" a7 a# T: s" k! }% a- T* h; y
2.调用bind函数
2 S K' A9 j+ `/ ^) V6 \ 将socket和地址(包括ip、port)绑定。
" H4 O4 |0 @1 u5 r5 a4 ~4 Q5 F 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
0 _) S6 r( G$ p* w struct sockaddr_in myaddr; //地址结构体
{" J1 p. Z# f& x- x6 X; @ bind函数
, q' A2 Q& b1 K6 k3 i bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))5 s* ?% C; c( X0 \/ l+ M1 b
; O5 E. {0 ]7 W: _. ?9 `) y
; _$ l2 M& P0 c/ b7 g0 r5 c3.listen监听,将接收到的客户端连接放入队列
3 `' Z9 ]" C X4 {7 ^% X4 W. ? listen(sockfd,8) //第二个参数是队列长度1 T3 f( Y0 l& j" Q
! s% A9 @, y$ Q
, P& i; w% {. C) g: b% f0 f4.调用accept函数,从队列获取请求,返回socket描 述符) w, v' O: K# n
如果无请求,将会阻塞,直到获得连接
- w" {& |$ y# c) [ w: B8 ]" a int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
9 z+ I% a9 O G
+ ?! e( f: v* r0 {
, F; T5 G" |- W2 c, @# h) y5.调用read/write进行双向通信
2 e0 i* r* |& p$ d9 a, [; U+ L( L
- Z( R# a3 L1 v- }( s
9 [0 R3 L: y. r4 ^. W) c, h6.关闭accept返回的socket
6 a6 W4 T; q; k L close(scokfd);: b; l. }$ X3 ^* F' l
+ ~- T4 l4 B' H. G% B
- V5 N" w$ D$ U! o3 K
3 Y, |4 B; }& i- ~. i* ~
. M5 ~9 ~( a. f1 J下面放出完整代码' l% v. e( M& F7 e
9 F' h. `5 P5 w3 R& e- /*服务器*/
. |+ ~. o5 \2 F; z - #include <stdio.h>6 V' K7 s- G3 u) `4 X+ D$ c
- #include <string.h>1 C7 Z5 H0 h; L0 M; F* B
- #include <stdlib.h>
' L$ t: w" A4 C, b5 Z1 e# N. v - #include <strings.h>
+ H; c2 R I+ a0 [& L3 g) o* n - #include <sys/types.h>! t& z& M7 A! w) | {1 h7 j+ X, ~+ X. G
- #include <sys/socket.h>. C9 b! U; `6 U0 a5 R( ~/ d
- #include <arpa/inet.h>* [" Y* c5 l/ C9 @) \$ k
- #include <netinet/in.h>% ]& B- \$ |6 d' D
- int main()
: M9 g! d; r6 [) Q5 B8 C - {
% e7 m0 Q& N+ ^2 k$ Q - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字! `/ S$ K% t) O, w+ \0 }% M
- if (sockfd < 0)4 p/ x* N" |% z! L6 I+ F# \
- {: r3 d; q4 u$ D4 m0 y
- perror("socket");' g/ ~. h# j; y0 J# i Y+ p
- return -1;
: R& t* P5 h& } - } //创建失败的错误处理
6 u3 [7 `1 @5 J, n - printf("socket..............
8 q. ?$ E1 F' e* C0 o - "); //成功则打印“socket。。。。”$ D7 O5 g, k- E/ g/ L& f
- 9 [! S# v0 p4 m
- struct sockaddr_in myaddr; //创建“我的地址”结构体
( J' x F U2 z0 \2 O; s) X1 h - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)* r* N- W* H+ T" L! c5 J7 q
- myaddr.sin_family = AF_INET; //选择IPV4地址类型' i$ e- C. T, ^3 n l
- myaddr.sin_port = htons(8888); //选择端口号! C! Z; J% a, N- Q8 l1 h
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址+ U$ X7 B8 g: u" p0 X/ Y
-
) f3 W& F R- @6 W6 d0 ^* `; x) Y - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
3 S! w; a: H, X2 M; H. S - {% c7 H A$ @+ I _9 V& a
- perror("bind");
) j* T& X! Z8 s$ |- S8 } - return -1;
9 w% y! F" O! j$ e/ ^ - }
: x& d1 r0 F6 _% G. s3 T - printf("bind........... A% x0 ~+ A c, I# E
- ");+ j/ W: ], [, P/ C
-
( g: K9 d; p4 c9 Q8 ~ l$ h& s9 c - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
8 n0 L( t# n" |! U# Q+ b - {
5 x2 A4 D: J/ S, a! y8 |, o. p. { - perror("listen");
$ a2 w K0 S o! ?4 T7 u - return -1;+ X3 z0 n/ p4 [% T& F8 W% ?
- }# d B$ p; z, a1 _) k% }
- printf("listen............2 H3 a1 U8 E8 m4 V' [/ v
- ");
4 I+ C/ D& W! O; X ] -
0 i. L, } G2 P. W$ N - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
1 V" ]8 h! G) w: \( d - if (connfd < 0): Q, z7 b" D/ c2 I9 M( T
- {
& R4 [8 o# o0 J1 \3 { - perror("accept");
; o8 S( `* v; T+ r - return -1;5 u0 |. Y/ c/ X$ m/ {+ I
- }0 i2 ]2 t& {6 [, w2 O1 }
- printf("accept..............3 Y- e2 r) H! @* W. t
- ");
# N. U: A, b3 g3 a - char buf[100];//定义一个数组用来存储接收到的数据. r, Z2 L0 j( l- g/ W
- int ret;: O; C( h+ {0 N+ \ `/ l( ]' r
- while (1)8 ?5 P, [1 P* \
- {
& _, Z7 ]: U7 m - memset(buf, 0, sizeof(buf));
- ^1 W, l& u% z/ K, |3 y, I - ret = read(connfd, buf, sizeof(buf));
& V5 G5 F2 A( I- u) u - if (0 > ret)
4 A; Z* n' ? t& ]+ k+ m - {8 ^, h- {9 D" p! \9 Y" n+ A( @
- perror("read");
" Q A2 v% u2 ^1 D4 J" S. q - break;4 R6 o8 S" R( U. J, ]* a. I3 n5 R
- }//执行while循环读取数据,当- H7 l7 u/ P8 p; \' _2 b; G
- else if (0 == ret)
, T% N6 y" R+ _+ {; o* L- S - {0 ^6 q J" P+ K; n J- U
- printf("write close!# T; a1 t' |: x$ p* `
- ");7 H, E/ }7 O5 W, T" a+ b% z
- break;# m+ V$ v k6 A& Q# o& h
- }7 t: r# B6 f- B d Q% [. u, {* k
- printf("recv: ");
. W* v$ r& D; D" Z! D; q - fputs(buf, stdout);//打印接收到的数据
/ w$ |# d; n6 A) T - } {# b( B7 N' q5 p
- close(sockfd);//关闭套接字% L4 N3 D. y4 W, v1 H3 a
- close(connfd);//断开连接/ [5 |( }! h: u& r
- return 0;
5 S$ B; h* I0 V. g' R; N- N+ C5 {* { - }
复制代码 3 V' d- N" |! ^# D" C
; {* c! Q1 ]2 u3 K& K- /*客户端*/(具体功能和服务器一样,所以不再加注释)9 _" `; _; c3 p- z6 G5 D% Z
- #include <stdio.h>1 v9 R# C& n* O( Q. c
- #include <string.h>7 [3 B. e8 e, M, w+ o b8 c& D7 c! b
- #include <stdlib.h>
' n+ {% V; Z c: Y3 E; R - #include <strings.h>8 d8 d7 G4 p% B6 H& }: p" ?- E
- #include <sys/types.h>$ y' } W2 r- D8 d) P2 N% y% s
- #include <sys/socket.h>4 b3 j1 w' i/ ^% q
- #include <netinet/in.h>4 g" V+ E& \" a
- #include <arpa/inet.h>9 `7 Y" L5 v8 g; ^- S4 W
- int main()" }0 E9 i- O p+ T, X* z
- {- O _: @0 C+ m) b \2 {
- int sockfd;
2 D0 M7 ~: I ?: }6 o9 L8 Z - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))) g* V5 Q$ h1 V
- {
5 z8 n% z) T$ Q* h% n) |$ P - perror("socket");
6 m* ?# B0 ~ `2 `7 D - return -1;' s% D5 p8 j" k* Z" X% j$ D
- }
9 S0 ]) z, m; ^8 V9 \; J - printf("socket...........# {( j' d" M! }% v, l. z6 ?# f; @- j
- ");
, t3 L" {/ M7 _ @1 u1 Q& O -
- [6 L! ~! c$ o; j3 ~- W - struct sockaddr_in srv_addr;
- P: j, h4 g: L/ g, {( ? - memset(&srv_addr, 0, sizeof(srv_addr));1 }4 Y9 R! O4 S# d: d
- srv_addr.sin_family = AF_INET;- c1 b, q( Z& q- E0 [, x7 \3 M
- srv_addr.sin_port = htons(8888);7 [9 E$ F* i( N& l
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");0 S& y. E% [! c+ {$ E9 R# f0 ~
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
5 a2 r8 d+ e9 p4 v1 g$ H! \ - {$ x# g' ^- ]3 f* ]7 |5 g' S2 y' k
- perror("connect");
+ l( u' K& _# Q+ s2 i5 w* N - return -1; //exit //pthread_exit
: ]" P5 Z- T5 | y5 A* e0 \ - }! p) N8 w9 B1 d9 M1 w+ \
- printf("connect..............: ^) _5 y# ]- N$ n# p6 F) x8 k% s
- ");
6 G0 U# N( M3 k, ^# @8 a7 { - char buf[100];
6 F7 h4 H" T* \; x2 B - int ret;
, i. [% S3 U, d7 g1 [5 y4 H - while (1)
) S0 r* j4 g Q* D' n' U" a& n0 \ - {
- U; I E- B( G' O f+ N+ f g - printf("send: ");* U) v4 h$ J# O& r
- fgets(buf, sizeof(buf), stdin);+ V7 @, a1 A) V: T, x
- ret = write(sockfd, buf, sizeof(buf));+ j$ T9 D0 W- F6 F) s9 f: I2 l# `
- if (ret < 0)2 f2 W% A* k- f/ b# H5 V0 V( i
- {
5 P; H; I+ l0 p# H9 k. W" q1 ` - perror("write");2 a& t2 K. i; Z" f" H
- break;9 s! U1 q- n3 w6 |( |2 K3 T
- }
1 K& h& R1 r# @4 A/ M8 X - if (strncmp(buf, "quit", 4) == 0)4 G2 ~; F8 I! T
- break;3 J8 d. h M: S3 d& [! P
- }
& }) _. u/ U7 d3 D! _ - close(sockfd);
- l. N9 W% N% H2 Y - return 0;
% R* \0 o, S' h4 Y( o - }
复制代码
- S0 z5 y& ~8 N m" z l1 h& f9 G, J0 @# O! n' Y
|
|