管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
$ d5 a; t/ ?; d5 n! F9 x7 E9 K) U b# L/ e: ~6 H6 m! K
$ q$ O b* W% f$ Z) l2 S2 p5 hsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。% ^( q; Q) M: G: C. i5 x
# M8 s. c; ~: z! R
1 I" k2 L2 x2 [+ E- _7 |1 V
TCP协议
' Y3 J# D7 D* h6 eTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
; h8 G: v: P% u7 v3 S
# k: ^) \6 D j+ d
1 ~# ?6 C4 i$ c2 R关键词:三次握手,可靠,基于字节流。
8 m6 D9 q/ q- C8 j# j" q
# Q& E2 W, f1 ]# S
5 x- E7 t5 e- R/ O) F6 U可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。; p) B' C3 q, G
- Z/ B5 K8 [0 R% {+ x
TCP服务器端和客户端的运行流程
2 a9 m% ~3 h# s/ p4 v( k4 S# H( C& m2 z3 ~. y* ^
' N$ M. E/ @4 Z+ a5 x* K/ F
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?2 A4 i8 g+ F0 h. X" z+ t
u3 W; F g2 y
4 }* I7 Z" f5 ]8 o
1.创建socket
, s( f0 p% y. m0 N socket是一个结构体,被创建在内核中
5 s6 p t. \* S! A6 T4 G sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议* R. i$ ?% E7 U; D+ g
3 ~0 J' v, V P- W G( B+ ~2 V+ \
$ h. B4 X3 I- T9 x& P5 W+ E o- S% N2.调用bind函数
9 {- n! W& W+ P. N# T' ? 将socket和地址(包括ip、port)绑定。8 m: p5 y8 {3 d q
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序; O( Z$ _& N- w/ G5 c
struct sockaddr_in myaddr; //地址结构体0 I% W7 x+ ]. h0 _6 D% y
bind函数% i5 w$ q$ x6 K5 d* ^' p
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% r% @. e: |& L5 C
. I b. n% ?# {) x/ x
B9 P* s5 D) @/ h2 w2 D3 z3.listen监听,将接收到的客户端连接放入队列
- M; p; j: s5 l$ Y; x0 M W listen(sockfd,8) //第二个参数是队列长度
% a8 v W* J( R' o. z, M2 V: f, C, F! _8 [9 B7 k
9 \) z/ G3 \! B7 ]- K) P }5 w4.调用accept函数,从队列获取请求,返回socket描 述符. r5 T$ m; N3 z+ l4 s
如果无请求,将会阻塞,直到获得连接4 s9 J2 x2 {( ]: l O9 [
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
" `0 u0 ~% h2 `0 d h: m6 \
. w+ F- E6 O9 K/ k8 v# ^0 b& C& x% b! m5 }, a
5.调用read/write进行双向通信
6 [$ k- f# I- u, q1 @, P* _5 b" h, H6 g0 c
; |2 {& t8 I {' D. I/ R
6.关闭accept返回的socket: r# S+ Z0 w' ]
close(scokfd);7 c" Z4 X4 G& L4 W- L& J, f
) o( O0 G A& y/ i/ w# A
- Q w8 M; Z0 `# @( h
; V. r& f* X* a5 n2 ^9 q% |9 D$ [. V) b
下面放出完整代码
3 k" q6 y5 a, @, a3 S0 I# f6 P
, ]$ N9 |3 ]& s4 F' C- /*服务器*/) i# }4 c/ a0 E. x4 K/ i0 P/ s8 M- @
- #include <stdio.h>1 C$ Y; t3 S% {/ s9 u4 |2 M* C
- #include <string.h>
# c2 t9 I/ r7 F: G1 Q - #include <stdlib.h>5 n. P5 g+ v, g7 d8 z6 x+ ]! z- @
- #include <strings.h>: i1 s. I; S {) U
- #include <sys/types.h>0 C: b( `6 G3 ^2 j! J$ n: j d
- #include <sys/socket.h>0 c! v8 j2 ^& F6 I9 S4 ?5 P
- #include <arpa/inet.h>0 f8 K0 D) k4 ^7 O( z
- #include <netinet/in.h>$ [: v9 C7 `% z( `# B: A7 x
- int main()4 r: ?0 r' X3 J
- {
- i* } S* j1 r - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字) Z' d2 ~0 F* r9 k
- if (sockfd < 0)
& s; d j# g+ t2 { - {
. h$ b0 f* T3 l- ]$ X7 z - perror("socket");
3 j$ D) Y7 p: i- d - return -1;
1 v& U. x7 x5 ] - } //创建失败的错误处理
; k- S- Z1 K, ^, c9 C - printf("socket..............
" z: [$ M0 n$ y. q3 [0 J - "); //成功则打印“socket。。。。”; `& c0 t# D+ e; O8 h
- 7 E; r- U. c3 Z; E; G- o+ Q
- struct sockaddr_in myaddr; //创建“我的地址”结构体7 I/ ^* X" x9 P% ? A" G, O
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
, m- D7 y* q9 R5 o - myaddr.sin_family = AF_INET; //选择IPV4地址类型
. A, t" g( ?4 _' _+ y- M5 {6 J - myaddr.sin_port = htons(8888); //选择端口号4 ]! g2 a' e$ p3 ]* R+ n
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
4 H5 `! G. d' g3 L1 I- J - % h+ v, z; a4 j+ r7 S3 A
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字8 ~0 @+ f7 y% j& K3 u! ]
- {0 F+ [' w1 F( h: D2 w* _2 c- w8 V* p
- perror("bind");5 K6 W: z) \5 _; S1 ?- U" a
- return -1;& \% k. y4 M- K- |% R' I# D
- }
$ U, N: F- W& ] - printf("bind..........
( k' A3 I( s3 K7 V5 J - ");5 e. Z/ i0 K' O
-
. C) ` W4 `0 D; L) m9 s - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听* S2 Q: v; s; ]; P4 q/ I
- {
1 o& P/ o8 \4 l( Z - perror("listen");
6 H( o) k; n# ~2 A* t - return -1;
9 b# i3 D: N* R) a - }
! s2 {6 o" m0 C! w1 l* H! E - printf("listen............
5 A# T" z% d. l - ");
- q7 T9 Q/ h" k! r. q -
9 U! b m) ` ` - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求# n' M5 o5 X p- j/ S' Y D" M
- if (connfd < 0)
7 i z/ n ?5 C/ y - {
: i: X N; ^8 e/ u - perror("accept");+ O6 c1 A* _) i: j0 F7 E; D
- return -1;- t$ ]" p/ B9 W7 [0 u$ s
- }
3 H4 w' z5 X& B o# `4 w' d - printf("accept..............3 L$ J6 R Z R5 D- {4 b
- ");
: o5 E* [9 L# C, d& l) M; x% u - char buf[100];//定义一个数组用来存储接收到的数据3 D: j, m9 d9 ?; g6 F# k
- int ret;
. r4 {& V* n3 L; G t6 E - while (1)
- ~7 R7 }) c) W+ o. D* [ - {
' q Z3 @6 ]! _3 Y - memset(buf, 0, sizeof(buf));* z! v& u2 \0 E, B! D {4 I: e
- ret = read(connfd, buf, sizeof(buf));2 l! ?5 r3 p7 S( k
- if (0 > ret)! M; _# B/ A" i1 A
- {
9 J8 ]& t1 m( ? {4 H - perror("read");
& f+ j, ?3 r7 X* j3 M$ f - break;8 r" i* E5 r- A H
- }//执行while循环读取数据,当
; Z2 w- q7 n2 k$ u - else if (0 == ret)
& I( c7 F2 q/ c& {) |% V, ~ - {
8 Q) P. M/ a, y) h - printf("write close!2 l6 l1 H4 |3 h: ]
- ");
7 B0 V7 {1 S6 ]% D$ E4 O - break;& m+ j; n( x, K& c) ~% L4 W1 u
- }
! a' V2 O- r( N - printf("recv: ");2 r# l. ]3 b; }4 S% x$ v5 m% W
- fputs(buf, stdout);//打印接收到的数据( d, ]' @) D9 s6 j6 s" a1 V
- } y$ Z. }9 Q% e( F
- close(sockfd);//关闭套接字
4 b/ |. g1 Z) n, C9 s - close(connfd);//断开连接( b( l2 c9 f; J9 @$ j0 e+ I+ i) p
- return 0;6 O1 w9 w$ m* ?
- }
复制代码
. T, q6 R& P5 A8 R- X4 S& @6 |) z. s% Q" b' D
- /*客户端*/(具体功能和服务器一样,所以不再加注释)0 v$ l. V2 p* b
- #include <stdio.h>
}+ t4 C; W, I& w0 k [ - #include <string.h>1 \+ B. u0 h. T/ B N4 ~3 ^
- #include <stdlib.h>5 a! T1 ]: l/ f9 P# K2 b1 q d, S( O
- #include <strings.h>
2 n/ O) C" S3 |$ D - #include <sys/types.h>
: t# Y+ k5 M# n* L - #include <sys/socket.h># m- h8 ^3 u5 @
- #include <netinet/in.h>$ T5 U% P0 `0 b
- #include <arpa/inet.h>
. j# k% M9 K+ G, E2 X - int main()
+ ?% M: @6 j* l0 e - {$ G- B7 a, g; N6 [" Z; k- M
- int sockfd;* v3 t- N5 Q2 Q% c
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))$ O. X1 D m! a
- {# N8 M' H$ F+ |3 D
- perror("socket");
9 v( M: x& A* }3 i% q" T - return -1;
+ r, g0 X1 C. X - }2 M2 u% d: J0 u
- printf("socket...........
0 M" T ]1 c+ ~% [% j% s! L - ");. K8 o, M; }4 B, r
- / y4 |- Y: H0 k: x# Z
- struct sockaddr_in srv_addr;
9 |& S. |# m0 w) i7 ] - memset(&srv_addr, 0, sizeof(srv_addr));
; | h! H/ h; O$ O5 s - srv_addr.sin_family = AF_INET;4 q+ @: B2 L/ |4 J/ o
- srv_addr.sin_port = htons(8888);+ v" V" a$ T" |
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
u+ D2 E" B5 S: H4 t; I/ z - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))+ A+ h0 i0 L! R3 q
- {
2 h- _3 e3 N- A$ D* f+ a8 } - perror("connect");; h$ J; G$ E5 Z" s
- return -1; //exit //pthread_exit5 `& j" ~+ x1 B* k7 j
- }) \' u k. b( Z) \# I' W* `
- printf("connect..............
: M4 Z3 T3 o( y - ");/ I8 M& T# E u2 ]7 e2 R
- char buf[100];2 `% o# @$ K0 Y- k c
- int ret;
( R% Y$ O% t0 z! @7 N - while (1)
( {) {9 Z+ E/ B7 i8 y% r G - {
7 o# \4 Q) h& B - printf("send: ");& Y6 X) F' x3 Z8 z- ^, ?; q7 r5 m7 O
- fgets(buf, sizeof(buf), stdin);; `& N* l {- u# O1 }
- ret = write(sockfd, buf, sizeof(buf));8 z! f6 w- g$ @4 M
- if (ret < 0)
6 A3 g: d2 C" T0 s - {
' R) D; B/ a1 Z8 K: h0 \0 | - perror("write");
: ~% t, n7 o: o- }( P; Y - break;
1 ]# ]8 G1 O8 q7 k4 _% N, |! Q2 q - }
: o. C5 J+ @( t' s8 k2 @/ D! `9 P! V/ O - if (strncmp(buf, "quit", 4) == 0)
6 h/ B9 W; S4 _9 [ - break;( E5 F) e4 w' \. R3 _+ E
- }
- C+ h9 o" w/ s - close(sockfd);
3 a* k1 h% f5 F! {$ q9 L$ s - return 0;0 e8 y) M* l( b. X% x0 i
- }
复制代码 9 f; i8 z0 e% h: Q& f& [) D/ R
( O/ Q$ P+ a/ X! S9 w( \ |
|