管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
7 Z, {7 W, ?! n& d2 d- f8 H i
- n, h# G, _. t! r( ^- `& K( \, d3 e! d
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
( _3 }8 R% l/ i# k' S! k
! Q" F4 k/ ^5 b9 X) A8 \. |
$ g8 n! S- c( a" ^ LTCP协议
% b# p# J& @$ L! y' yTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
7 Z; V- W" T+ t3 s3 V( i
+ n( y9 Z0 M) R4 ?3 ]- r5 ~! k+ w
关键词:三次握手,可靠,基于字节流。
/ f! W* k/ d6 z5 z5 z
0 M% N4 ?6 n% D2 `( m v; ` l8 n c8 u' s6 _: a
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
; q: M0 o# R6 B* R" r( ~) _9 D9 h
9 }/ k* S% s( F4 u$ g- Y
TCP服务器端和客户端的运行流程
8 w. u- u7 f7 H+ F7 I
. p4 t" w( A/ O) t ^; f* M/ O# y3 Z5 q0 |$ T8 a
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?) F( J. c" [/ c
* `* J0 o, c: j
+ G8 g% s% f- Y' G$ O8 u- v1.创建socket
6 c! o* A/ y' y5 }) w5 S$ G! ]& E: D socket是一个结构体,被创建在内核中; T$ \; e# R, M! D8 @& R* U
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议; B3 y* O' k0 o7 D/ \* S
/ g( ?- _/ j3 V; v: G' O$ J) s
# B: r& {3 B/ B, \8 X S. Y2.调用bind函数
9 Z/ i6 O$ b) ~: T9 c0 | 将socket和地址(包括ip、port)绑定。; S" n/ B1 o7 D" v4 m7 r% s' K
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序: }+ V8 j3 |) B: [8 j1 e+ u: G- Z
struct sockaddr_in myaddr; //地址结构体6 w4 d1 ~) M+ ^- n6 ~% i+ i
bind函数+ }1 M9 v) s" k- g
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
6 t( i7 ?- A7 B, G1 p6 E
8 K# y0 q( m2 x7 Q* q$ R5 L, W; e5 ~& `
3.listen监听,将接收到的客户端连接放入队列# }% k, a! y! j2 f. \8 J/ d. w
listen(sockfd,8) //第二个参数是队列长度% t8 t1 o& ]$ |6 Q8 Q
+ a7 i! G- u& ]
7 Y. o6 K; ~& W& B6 v& o4.调用accept函数,从队列获取请求,返回socket描 述符
4 |8 ]. \) `0 _1 B4 V 如果无请求,将会阻塞,直到获得连接
- s2 S0 n4 j1 s: t" M# ?8 w int fd=accept(sockfd, NULL,NULL);//这边采用默认参数4 }7 v" ?. a. u6 d" t
2 F$ d; p( [8 T9 p0 K3 w9 `
3 h8 S, V% I4 f' y) M, h O: I5.调用read/write进行双向通信$ O i: d% d1 }) j2 u O
7 D; ~7 n1 a/ e, N7 ?4 B9 b! t7 S
9 u8 p& n' Z$ T! T: |, Z
6.关闭accept返回的socket" m( y& @% j4 \* J9 b
close(scokfd);
1 R6 W% L; z1 I3 V8 H& F9 A6 h. |* B2 V% j' ]2 j
$ Q, K2 R$ m: H! m- K
( a! r9 ?. b1 A! D
( g# d5 ]3 r D q# P1 }& K1 s/ J下面放出完整代码* [0 q; S- z5 B) a- ~: p
, \1 x, k6 l) t5 Q- /*服务器*/
: `$ x' x9 D. A; M% U } - #include <stdio.h># v4 R& L3 e$ E" H& v' }
- #include <string.h>
3 [# t# A* E6 p z; l9 m5 \' ~$ g" W - #include <stdlib.h>
h& c; _0 m6 c F! p - #include <strings.h>
, d6 p7 q5 [9 o, O% n5 S/ Z - #include <sys/types.h>
L" y \) Y, [- i: p5 v+ ? - #include <sys/socket.h>6 t W8 E% B/ r& F
- #include <arpa/inet.h>
5 I8 t6 w- f' M! e v - #include <netinet/in.h>) p) b y5 e8 C; Z. u2 W
- int main()/ A1 O/ z n5 _' ~5 N: M. p
- {7 F f% X& z& U4 O
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
" X8 r+ O' U5 t0 b3 K) @8 T - if (sockfd < 0)0 _! U+ ]/ s- D5 r. c4 P, \
- {, }2 [- q( a3 B% i5 k
- perror("socket");
! t8 \5 Q+ H! |1 P' q/ J0 T - return -1;
+ S: @( k, m* q4 m - } //创建失败的错误处理
. a6 ]7 q Y$ v4 r - printf("socket..............
7 L9 F6 i4 B b6 {1 X6 Z2 L - "); //成功则打印“socket。。。。”
6 `, Q9 z! i/ [, B; h1 ~* [ -
* ~# w: k2 |5 T3 W7 k# A. @ - struct sockaddr_in myaddr; //创建“我的地址”结构体
; q, R; i( l9 C! f7 ~ - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)* b4 G# d8 \: s/ l
- myaddr.sin_family = AF_INET; //选择IPV4地址类型' ]( {# p7 b: P; {7 g
- myaddr.sin_port = htons(8888); //选择端口号
, h& G; M/ K# ?! J$ W8 P" u6 _ - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址* C% n( Z) C5 J4 z8 n& W5 p
- * K1 w- B% v. \. _* u7 r2 A
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字+ L( O: h" f) v1 }$ E! G
- {+ W" G7 d+ e! y
- perror("bind");
) \9 @/ p' X, p, a0 e - return -1;5 T% ]8 Z8 t& \$ N2 v% U6 m
- }3 j. g3 y" ]% @% B. M6 |3 C. g
- printf("bind..........
9 h2 q+ R& }5 z9 Z - ");
$ z+ h5 J) _* E, P- ~! i' o, [ -
$ |; t8 a4 P: J: R- ~7 k; S) x - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
" t; G$ Q- `% V. N - {$ \9 H1 j9 d' }! d6 Q) g
- perror("listen");5 i5 [3 n* \ T7 p
- return -1;+ l+ r! }9 w% a) V2 Y+ _
- }5 D4 u' }* J- Z, O
- printf("listen............
1 o: C4 g) d* y- H/ @ - ");+ e! y! d' P+ n4 x6 D- o+ U# h# e s
- 9 C0 Y3 Z+ s' m% \2 W8 }; V; i
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
7 I3 {5 k5 P& B$ ]0 W1 y - if (connfd < 0)
1 A: i; V, b( x7 } - {! P& A) D Q% P2 a
- perror("accept");4 l. v/ J4 M5 s! C* N$ r
- return -1;
0 g! z" H* D# Q9 t6 G+ }5 Y) [ - }
: F0 Y0 M- G8 Q& m - printf("accept..............4 t- ^, p3 Y' r9 q7 q5 Z
- ");
) }+ K) n$ Z6 Z; {( B" W - char buf[100];//定义一个数组用来存储接收到的数据
0 u8 o( V5 O) B, D& R: J, r8 X - int ret;
. k/ \5 Z2 ~. L6 \. ^' D C, |1 h' | - while (1)
+ c7 M6 r/ L- r/ g+ h - {
) N2 h& x/ t. {0 }% _, ~: R$ p - memset(buf, 0, sizeof(buf));
5 Y6 a9 ~9 L2 s - ret = read(connfd, buf, sizeof(buf));
4 U1 Q" K- x1 A - if (0 > ret)
2 g1 r( j8 n' l: c! | - {, w1 ]+ H. K5 X% H3 O( g* _. o
- perror("read");
4 H1 u0 E) I5 d' b8 w ? - break; f3 l8 c( C y3 e$ a1 G
- }//执行while循环读取数据,当
: z9 \% p ~9 q, { - else if (0 == ret)4 e0 R3 X) d. P- f- L8 v
- {
( v/ u$ [0 {- m$ C - printf("write close!
: S& Q: @- V4 b( B% O8 ?1 U5 \ - ");# W6 X. W! z& w/ a N" w# P
- break;
& E" p# H, ]6 E" d4 \2 f9 E# C - }
3 E ?! U; T5 H: o' }# F- {) T - printf("recv: ");( E2 u2 s8 _: Y+ P
- fputs(buf, stdout);//打印接收到的数据
& I- B. {+ E1 ^9 ~( L( ?! Y% u - }+ e! `) E5 Z; R
- close(sockfd);//关闭套接字: I4 @. E* m' n6 d6 Y
- close(connfd);//断开连接
2 i" }* i; X8 d# c, y- s# k& d - return 0;" u8 t9 O& Y1 E1 d7 d P: d
- }
复制代码 7 z5 b/ }! W$ y0 a
/ y' a, u4 f3 T! y+ y9 T( q- /*客户端*/(具体功能和服务器一样,所以不再加注释)
' ^* K' v! ?5 g# y& E3 X - #include <stdio.h>
6 d: i F8 W6 N# g - #include <string.h>! ?( S) f+ p" `& r: P8 s2 P8 ^
- #include <stdlib.h>
5 M( H% E% X# M7 ~4 f1 ?0 | - #include <strings.h>2 T: M+ c8 B+ Y# w. m/ p% v Y
- #include <sys/types.h>
& R% a! h% U/ H# h - #include <sys/socket.h>
$ R( O, l' w8 B" G6 |- B - #include <netinet/in.h>
& T0 K. n- f6 y1 v8 v: D8 z - #include <arpa/inet.h>1 b+ l0 c3 x! C! `& f! K M2 J# B
- int main()
8 x& L7 L) |( f# \! ~6 r! z& u - {* v* B" J8 L+ i4 v1 Z
- int sockfd;' T. `" z: k$ o" E/ U1 C
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
2 b+ M4 d9 R0 A: l7 ~5 \ - {1 b1 o8 r) i; k: a8 z7 D- t- w4 ^2 S
- perror("socket");# Q$ }6 c; D/ }: [/ k4 z& m
- return -1;8 D$ o9 y; E2 K5 b
- }& ]7 L# e" ?6 G+ g3 N# G
- printf("socket...........% e, R) |' G/ P; c/ l9 \) M8 l
- ");( J4 y, e+ q9 K& s% }- }8 O
-
; f! }$ s4 y) H2 N - struct sockaddr_in srv_addr;
( U7 q& Y$ g: [' o7 R( O - memset(&srv_addr, 0, sizeof(srv_addr));
! W: c. S+ N- m& m5 ]2 Y4 J* C - srv_addr.sin_family = AF_INET;
2 H/ L3 M8 h; t/ c! x K6 P" o - srv_addr.sin_port = htons(8888);
/ s) N& `4 q* l- s* ` - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");( l! `9 \) o! a U' t( U3 ^
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
% P! E& y6 O6 E7 F - {
O+ v8 m6 O) ?8 p& ?2 d" u! k - perror("connect");; f0 a3 D( |5 `! _
- return -1; //exit //pthread_exit
% D5 h" C) E7 X! m) M: w, w7 {* x1 ?1 h - }- ^( {" \0 X1 o
- printf("connect..............
0 W# o7 v" I+ x% d. u+ W8 h: ? - ");: ?) q6 p/ h. z' p+ s5 ^
- char buf[100];
z8 D' H% T0 T# d7 _$ f - int ret;" s! c# t0 j4 u4 {0 c: _
- while (1)# Q+ j- C3 B" u6 G' ]
- {
* O8 W1 y3 L7 R+ U. F - printf("send: ");' a- s A% O! ?" ~9 k4 q7 Y B
- fgets(buf, sizeof(buf), stdin);9 A2 U ^, @3 F K
- ret = write(sockfd, buf, sizeof(buf));
+ L. l% @4 q! f5 `) r" L2 i - if (ret < 0)
( i2 n/ |' O- s - {
8 Q$ A6 \6 s) D0 q - perror("write");* X& s' `& q! `2 R+ t: W9 L
- break;
; L/ p. B9 n m+ Q7 b7 U - }+ y5 m; S/ `: O; V$ j% `. ]
- if (strncmp(buf, "quit", 4) == 0)
. Z, o5 \4 d( J& }" ~0 _ - break;
4 o, s. o+ |& k. F& L - }9 M- ~7 Y8 y- _
- close(sockfd);
6 c3 |' ^2 Q' H - return 0;1 }% Y6 Z w- U6 y8 ?& @
- }
复制代码 , z7 n# L5 v- w# q7 N2 ?7 J1 D
+ m2 N. { W- G0 l7 ]! y1 _& E |
|