管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
/ e D! M; u: @/ l3 i2 k( C8 _# ?1 E) O) k+ t$ R( {
0 }7 O5 O3 y7 V) |/ J7 L m
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。# _& v7 E ^' X2 f9 d# i
: H( R2 f" i) F" R1 m- V
7 a. A& N: y$ f, z6 f6 \4 W# n2 A6 Z) _TCP协议' m. s" E' Y6 k, [
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
' d3 q, i6 g8 j# R9 S, z( J% _/ ~0 R/ i
8 u) ?. M: V3 p7 W( o7 w关键词:三次握手,可靠,基于字节流。8 T' z& e* {6 o! w, Y
& [8 k3 @2 O& q7 F5 s! W$ B- f; {
" W: ~* i* d9 c& v* [: w
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
$ b9 `1 B8 R- ?& k% y$ }
; T+ O' M: A& X E3 Y; CTCP服务器端和客户端的运行流程
3 d% i9 M$ C0 ^$ H/ t
2 {, p0 S% j- h, e$ d: ]# A: a- |$ l$ f( }
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
* C4 u. X: K- M- O- l
3 B; c' f" t2 O3 C8 c" S Z
' ~ Y/ m3 J8 m3 l# f1.创建socket: i8 Z) o( L9 q" q2 R
socket是一个结构体,被创建在内核中& `7 P8 {; D6 r5 K* I# n
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议. V, }4 W: Q5 T# A, _; _8 P$ f* y; P
5 y: m2 u. x0 ~% v _
- N$ r8 i* K6 G4 ?7 N+ L6 E7 W2.调用bind函数
/ [0 B3 J; y9 K3 A& Y. X) | 将socket和地址(包括ip、port)绑定。
. n) d; ]$ u4 ~" S 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
2 A8 u, G2 M( V# s struct sockaddr_in myaddr; //地址结构体7 C R7 T0 u4 v; O6 P" T
bind函数
( }$ t% v2 |( d) M% K bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
" S1 `) b+ R( Z9 {) G4 b) I3 ]" h7 ?9 K( F7 b5 x I3 S; v
z/ I$ n( R$ N0 h) U4 j3.listen监听,将接收到的客户端连接放入队列. d; l: [" m7 l) u# i0 ?
listen(sockfd,8) //第二个参数是队列长度
5 b4 f7 v+ ?% F" {9 O0 e8 N7 b+ L( N. K5 K- `9 k6 l2 w8 E
) {" n6 P1 j. ^$ V+ [5 V' t, J" _/ o
4.调用accept函数,从队列获取请求,返回socket描 述符5 p" `7 w9 K; v9 P& `$ p S" }7 c
如果无请求,将会阻塞,直到获得连接
4 b( `- d7 c% Z' e+ U/ Y9 G4 E int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
: Z% H5 C' @6 }+ B d" A0 m% n9 b( t! a: p( C3 w9 f( R q
. p9 L% O! r B: h$ j& ^
5.调用read/write进行双向通信6 }0 ?- I1 ?9 g" }' L9 C
$ X* k+ z) ?" L3 `" e) s* K/ K) K9 w
6.关闭accept返回的socket
& e1 J) [; \2 z7 R$ q close(scokfd);
, [$ }; ~1 Y% B8 h: O* b; {3 V- I6 y. J' i0 a" g/ m \
0 c5 E* n3 s0 G9 \
1 f! j/ S# u! T' e: O) o
5 p- z2 Y/ ^3 b# {下面放出完整代码
( V- W: e5 T# |2 U, Q8 I$ {+ R0 v$ Y& P# K! q% {- p( ]9 m/ I
- /*服务器*/6 b5 X6 i% d9 l
- #include <stdio.h>' o( ~. |! z9 l( `" j
- #include <string.h>
% I5 r4 t( u) @% ~ - #include <stdlib.h>
2 z; L+ E- ?$ e0 A! P4 r - #include <strings.h>8 u0 {0 N3 r7 Q0 {% S1 t
- #include <sys/types.h>/ l- y+ d( E# A# y( _8 k
- #include <sys/socket.h>5 G% D G9 c+ _) w8 q6 N
- #include <arpa/inet.h>+ T. `9 A2 f1 Q- F4 F& e% O
- #include <netinet/in.h>
4 v3 V, O/ q$ N/ A* q6 O - int main()# U8 C/ n8 y0 n* }6 S
- {
0 J4 r3 q8 o: v3 X! _: Y# Y - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- M# @: j* `; l* \
- if (sockfd < 0): n+ E* c2 n8 I8 @) i! W$ L6 V& Z
- {: A/ y5 K# n, H0 P {
- perror("socket");
5 l: Z# w z/ z2 }. {9 z - return -1;
- C4 y1 U( ^1 ^' V) h - } //创建失败的错误处理
R, H, w ^! v5 L; \: W c1 ` - printf("socket..............8 ?" B! _0 K# a
- "); //成功则打印“socket。。。。”
, [. u4 ?+ K6 ^- A5 z8 c -
' _5 ^, ]' o; S8 @4 ^3 z* q! i3 `/ J - struct sockaddr_in myaddr; //创建“我的地址”结构体# X1 c" ^3 S+ X
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见). k! t% ~( e% c' V- j _
- myaddr.sin_family = AF_INET; //选择IPV4地址类型7 _% a X/ J' D# r, { D; X
- myaddr.sin_port = htons(8888); //选择端口号
/ _. b- B: x6 E" j- | - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
( U' k+ F/ }1 B( v6 [- P0 C -
* r+ v" O3 ^. P5 a7 z& p; Q - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字* k/ z; `1 H7 N" i+ m( }
- {$ m3 j9 `$ g2 z$ r% `) k
- perror("bind");
% p( N, M. v! `) Y( Z - return -1;
/ u! q5 @ U/ i+ m - }7 V* s# u( @9 a" p) x) b6 `
- printf("bind..........+ Z$ g t* B* c, Z+ D% ~4 x+ r
- ");1 \$ Q$ Q+ A! r4 h
-
( V% z }, `, i$ w8 F! l, [; q - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听$ i6 Y- D- V h+ q4 j$ R
- {, K' K+ o" j/ ~0 H% o
- perror("listen");
3 z$ @5 e! \* r) j3 { - return -1;4 b4 K- l7 g6 C2 _5 E4 L/ c
- }
7 h# @9 p; S% i# y+ d9 h7 j - printf("listen............2 Q3 L: f8 o g) B% j- C
- ");5 v$ N8 L4 f8 s) ^- l4 f3 F% x
- 2 W. m4 O$ O6 x3 h3 o+ q( k. n6 g9 r
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求9 b- I; {# M6 ]7 W5 p) T, k
- if (connfd < 0)
' a" p* M2 S9 r; `4 c* ]6 s - {, n0 Y: V: b+ X/ b7 k
- perror("accept");
2 s4 e/ g, b X7 u/ z, R9 y3 r8 B5 N3 C - return -1;
8 k9 a' u* U/ F) z3 f - }
3 Q) _; _+ \$ ? - printf("accept..............+ ^* \+ f# i. T, [: g4 t& |* R
- ");
. _$ m/ S1 ?( B+ A Q O' o2 J) F - char buf[100];//定义一个数组用来存储接收到的数据% G. i( K6 R s- s' ]5 S
- int ret;
0 t4 n1 E5 N% f4 H% F* w - while (1)( ]) n2 a! d, \$ A9 m
- {: [/ e. n0 S6 q& R2 N
- memset(buf, 0, sizeof(buf));+ t1 C: a. Y1 U! L
- ret = read(connfd, buf, sizeof(buf));
1 \" L7 W8 p7 G, W9 ~$ R6 o - if (0 > ret)
0 d7 \0 R- }6 M4 p( f - {
, Z# \; }, g! I" ~' f4 @ - perror("read");5 M+ |- k' r. g& n5 P% t! ~
- break;
( L6 U+ K* M; V) | - }//执行while循环读取数据,当
: ?1 A' e. z% H% x9 s - else if (0 == ret)* W, m5 N0 {0 g' C) d
- {2 C4 H& l: u+ O* I; l
- printf("write close!. D! k! `& W, o* Z5 d% b; s7 }
- ");
" `/ x) f# y6 @- @. ^ - break;
4 _; v p6 K+ }. z5 Y - }
" Q: a4 ^+ |# m# s, o - printf("recv: ");' i6 T( L% I7 k8 c
- fputs(buf, stdout);//打印接收到的数据/ C, ?0 c6 r/ c9 t4 [. C( r
- }6 o/ i: q- l) Q% O, R
- close(sockfd);//关闭套接字" v# O+ a7 z `3 K& x- ]
- close(connfd);//断开连接
# e h/ b4 f/ q - return 0;0 V& R q$ J# j" [
- }
复制代码 , g# d* f% a4 ~7 f. x) M
3 n/ O6 \9 }& Z- S2 @. d: E7 T- /*客户端*/(具体功能和服务器一样,所以不再加注释)
# r W4 G' c A - #include <stdio.h>5 ?( K: L, T. C4 L
- #include <string.h>
+ b- x* j4 ~+ s - #include <stdlib.h>
K5 t* L2 r& D! R/ ]3 P - #include <strings.h>
( g, R0 s" a: Q F6 h$ @+ g" H - #include <sys/types.h>
3 v9 H+ n, J8 q3 z$ N" R2 p7 o5 a - #include <sys/socket.h>
. Q& W5 ^2 X: \1 U - #include <netinet/in.h>
. x( F; h; Y0 p: f - #include <arpa/inet.h>6 I+ `- J- t( g7 Q2 ?
- int main()
1 D* |. R/ {! Y2 ~* M7 k% A8 K - {' l: {8 r) y9 V# L/ S
- int sockfd;2 Z# H+ l( l" \0 A8 l# f
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
; C! T/ q; S! D1 N* p - {
$ F- h; B& k0 J. P) t9 l - perror("socket");
& C, ]% h) W1 K' N - return -1;
F+ E" M- N+ E6 f7 h - }1 D0 q; U1 R* c" t
- printf("socket...........! |+ r8 _! W& T4 o
- ");4 g0 Z. G4 S" C! J. q/ i2 h9 {7 c- I
- 5 B/ N, U5 D1 M# ^+ K! N4 ~+ A
- struct sockaddr_in srv_addr;7 P2 J' m* o) k }) ^; h; Q
- memset(&srv_addr, 0, sizeof(srv_addr));9 [) E @$ F2 m. }
- srv_addr.sin_family = AF_INET;' Z X" U: k$ D$ z; s% g- M/ @
- srv_addr.sin_port = htons(8888);1 }, K3 Z2 b. k1 h/ ?4 h
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");( x+ h3 T4 b' H8 L
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
v3 _0 A: e* X) h9 C - {
/ k5 N- c3 x# t8 M - perror("connect");
2 s8 u9 n& \' G. I% N# t, {+ @ - return -1; //exit //pthread_exit J Q/ H0 a) A" p5 Q6 C
- }0 e9 G& w ?( L. p
- printf("connect.............." t+ h8 N' G* U( u+ q
- ");
' Y" b, @5 H: n: t. I$ B" R - char buf[100];
5 t. q) h- b$ w- L: A2 n - int ret;3 h5 x/ w- |% {& l, l: E1 Q
- while (1)0 r" z, V6 c" z. Q/ I
- {# z# b, z6 O. R* U9 y, h
- printf("send: ");
& ?. {, y8 e5 H" ~# s2 x5 t r - fgets(buf, sizeof(buf), stdin);
! r# \8 Z5 k$ z t3 D/ g) C& | - ret = write(sockfd, buf, sizeof(buf));
, f. B9 v+ L8 s9 L - if (ret < 0)
+ }& u1 T; w2 D v: i1 | - {
" v. M( }6 T2 Z& ]& E& [ - perror("write");# G# {1 }2 Z) ^5 u$ q/ j8 D
- break;- O: S( k# a9 F) r6 j4 g) j
- }
1 C. [ u! }0 `- X - if (strncmp(buf, "quit", 4) == 0)
! O: l' s. f: d, ~( j - break;
) a0 ?$ h/ ~7 z) F/ ]+ K - }
, F5 ]. q. ]4 @+ W1 X4 N7 S* S - close(sockfd);: h& S$ ?; M1 ~) W6 m! V: W
- return 0;2 y& a" s- m. M: k- g- z* n
- }
复制代码
: ~5 a; t+ u% Z" P; ]/ O( J6 s9 s9 A0 C; v: v
|
|