管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
" @8 e' m" \; n/ B$ {0 H; w/ ^: J7 o, m# p! W$ n4 q2 N
) r- ]6 E6 S% |0 Z; n, z. ~
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。4 T, d9 ?# n0 ]& P
/ R( ~0 G1 r. @3 K1 Q% v/ H: d6 j/ }" @1 A% y! R: C# @
TCP协议9 Q4 {' I5 y, I1 h9 X H
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。5 u' v" v3 b3 j! u" n
% Q1 M- _7 F! z6 [( w5 z. v6 S. c( z
关键词:三次握手,可靠,基于字节流。
8 t! c; Z. A5 C6 E
, ?3 K0 V7 S7 _3 q. u/ _& i% T( c- x) E8 Q6 ~* i" s( C7 A- T
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
! u4 Q' l6 ]3 e
" g: ]- x7 y! p3 q3 @4 w$ b
TCP服务器端和客户端的运行流程
^8 v, q8 ?, y8 ]% `6 o9 H& E6 b- d/ Y' N! e$ v
, M, ?: t" _; I9 A# @2 p# m8 k如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?+ m5 z9 _+ Z- _+ f. J: A6 A. k' X
% ]; ~: o. d3 v) |
7 w4 L. n8 h3 M0 T+ Q9 b5 o
1.创建socket( J( J6 h+ F8 j2 r0 I. l
socket是一个结构体,被创建在内核中' k* i! P+ |9 S8 ~# W3 f3 L
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
) ?# b5 Z3 @- q2 m$ {' G+ a/ g$ r9 M
; ^# G: [: ~5 b) o! C. u1 l6 Q2.调用bind函数
_4 s8 d4 ] ]# q 将socket和地址(包括ip、port)绑定。; f0 P6 n7 M8 f# X
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序6 ^! e# y3 o( s O; h
struct sockaddr_in myaddr; //地址结构体2 x& ^" ~( ^/ z' h! x- p$ J7 j, h8 N
bind函数" z; O0 Q* |1 {# {( L, N
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))6 D) m+ ^/ E5 x3 S5 c0 q
0 l2 n- @* R$ M h% G) f4 \
+ q, L* L1 y. h: _' |+ Z4 F- M3.listen监听,将接收到的客户端连接放入队列. E9 B9 r0 g1 a4 s- H
listen(sockfd,8) //第二个参数是队列长度
- h, J6 m4 O8 B7 X# g8 g" h; c& L& e* k8 J% A
5 Q' B# c, e8 p5 Z4.调用accept函数,从队列获取请求,返回socket描 述符9 H' H0 d, x: \' V* j6 M
如果无请求,将会阻塞,直到获得连接' B( p: _( ^& S5 C$ k* U! b4 E
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数1 L# d* K P. W, K$ q
1 v2 _ H/ J" c/ h; g
( \/ x' J9 r! J9 n0 O
5.调用read/write进行双向通信+ `2 W( U5 [( B8 ~- W/ P
+ v% S j! T0 \! a1 C5 D2 z) I; L: L% D8 [
6.关闭accept返回的socket
{8 [9 K( y i close(scokfd);
/ U- Z& P% Q9 T+ C
# ]9 }, [& F, }$ u3 M8 @, ]
5 E+ v2 D4 F" f: \2 ~, R. W9 W/ u- {- e
4 F" y: ]) u. I7 {- i' X; o下面放出完整代码& W; H( e* {. _1 v
% s1 w" K( q0 ~9 S
- /*服务器*/
! I0 J1 q+ @" H; }! V - #include <stdio.h>8 |7 x7 X4 |, l* t
- #include <string.h>
w. L- N* {! g. a - #include <stdlib.h>. ?7 O8 L' C( z, `0 v
- #include <strings.h>6 E: w# }5 e7 M" Z( @$ U( G* q
- #include <sys/types.h>5 M9 b l9 X5 m
- #include <sys/socket.h>
- ~ q0 X2 s* y+ @% S1 K - #include <arpa/inet.h>
# S( m/ u! U- [4 @4 e2 a, t4 ]: ` - #include <netinet/in.h>
- ^7 E8 R3 w, t$ | K; M4 t - int main()
l" S6 R, ~8 R, x; c8 { F - {
0 H/ J$ ]5 O6 ^ - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字 |0 o* {' P. W0 `) V8 t
- if (sockfd < 0)
' }# r( q. s c$ y, ? - {
5 i& q) J; r/ P" b: ]+ | - perror("socket");
2 ?* x% A7 b) N0 Y4 y! |! y! j; [ - return -1;; }% c( i& R2 t% z2 K. B- q1 c9 o9 g
- } //创建失败的错误处理
# g1 D0 W7 V" X4 J- I0 v - printf("socket..............& Y# M/ j: N# }4 E% i
- "); //成功则打印“socket。。。。”
' s; I2 \8 F2 r5 p -
: j9 T4 i( M" U2 O0 } - struct sockaddr_in myaddr; //创建“我的地址”结构体
: X3 i# f. w. w V$ P1 l9 `8 }. q* L - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)5 M* f z/ N4 Q9 ]9 \
- myaddr.sin_family = AF_INET; //选择IPV4地址类型1 E. Q1 Q8 @, O7 K
- myaddr.sin_port = htons(8888); //选择端口号
- U n$ y6 b7 N# \$ ]. { - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
) ^/ V2 A) S6 `- y -
& Y9 N- j, Y4 a# f0 X6 S - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字+ k) C- \: P$ |9 l3 x
- {
" @9 x, g* z$ j; Z* d - perror("bind");
' D: Q) I. X) M4 g l# Y - return -1;
5 W% C" Z% J6 p/ d y- O% i - }
+ A$ G; j4 j* M1 J5 C - printf("bind..........0 M+ b% \& }& A" `1 X0 ?& x
- ");6 g. |1 E3 J: G1 z) W1 v+ H, N
- + e R9 c/ {/ E. T& n5 l# i# H0 p8 \
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听! b1 H. @! V- g6 X% @, z0 Z9 _
- {
4 q5 p2 d1 x7 g1 P) w - perror("listen");
) J# n9 O |/ W( c$ w' y3 \ - return -1;0 W: \& ~1 T' `3 J
- }1 i& |* u! F% y5 o# G: m. [
- printf("listen............5 @& K% ^+ C: V5 R! _& ~
- ");
5 m/ {% J+ b- {8 b -
. I( l/ i5 ^3 x) V- C$ H( y% x* F - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求. I8 M+ e0 r# A# o7 {* D
- if (connfd < 0)- Q- B1 \7 F/ s* B
- {: r8 p. t9 J7 x: `
- perror("accept");
2 i( g. e' Y) ] M, g. _% x - return -1;- j: M0 _9 X {2 z# a
- }
2 @1 w9 [ o) U8 S - printf("accept..............7 v, s( Y a8 D4 G; s
- ");8 T6 ?% K8 m# t( o& s/ W* J: z+ _
- char buf[100];//定义一个数组用来存储接收到的数据+ ~$ r6 X! f" ]/ t% X
- int ret;
4 p. [$ p# C% V - while (1)8 x% J( `% h" f, ^0 p
- {
5 V, u/ B4 V; [* Y, n0 _- v5 h - memset(buf, 0, sizeof(buf));: D/ E3 f+ S# j' @" @
- ret = read(connfd, buf, sizeof(buf));
8 f$ x4 o4 [2 p$ r5 b. y, O - if (0 > ret)
, |1 ?' u; u) K - {. H: v% o. ]0 @( X9 `
- perror("read");
, H- O# a2 [% J( k" \ - break;
3 i$ K p, t: L+ \3 g+ t - }//执行while循环读取数据,当
4 e- q' g; ]( Z' j1 \: r* `! G# n - else if (0 == ret)- O4 `, n. k8 r2 |
- {- T9 r1 s3 D4 [ X) K* k5 R# a& o
- printf("write close!& v# T, b8 K$ e0 ]/ {& R4 z
- ");
8 z) |4 ]2 A- J4 s - break;
! R( x; u6 {/ Z2 H! W( ]; T% k - }! W: @6 g# H2 E4 V( C5 l/ w
- printf("recv: ");
3 Q, d9 a3 L% J- [* m6 t* V - fputs(buf, stdout);//打印接收到的数据
7 \ K2 Q0 w9 [ - }
/ m% [1 v s* d! k! v - close(sockfd);//关闭套接字- [2 k* e3 {1 A. t6 e
- close(connfd);//断开连接0 V3 j, d( \; d
- return 0;
8 x# s' I/ K0 Z, T3 r7 i - }
复制代码 ! H3 G! t4 ^! B! N
, b1 F4 q7 P# k8 P- /*客户端*/(具体功能和服务器一样,所以不再加注释)
; V" ~" L0 A; h6 Z - #include <stdio.h>/ @* G- J Z& `
- #include <string.h>. d7 T4 M. G9 N
- #include <stdlib.h>
! i1 F" p! w5 T. I5 M8 r4 M - #include <strings.h>
% z/ t7 K1 x% ?8 [4 X( A - #include <sys/types.h>
9 k0 G" \. p; v4 ?. n& E' @+ C - #include <sys/socket.h>
' t g# } U2 |' y8 F3 P6 _; ` - #include <netinet/in.h>
; a( P+ i& J2 v - #include <arpa/inet.h>6 L% I1 I2 [- _3 O: L/ `* ]
- int main()& d' m- F' d* ~* o5 q
- {
' ?0 h9 D. Y; a! h1 J - int sockfd;# f1 M- J6 O. j! g; o3 @# e7 t! X
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))3 G3 f" y% I$ N% ^' U( D
- {4 g! O4 y5 T) I/ r) U% c* W3 g
- perror("socket");% j8 g5 D0 s6 r+ x8 t( v5 h
- return -1;! Z* @5 S, i0 j5 k3 V( W. h5 b0 H
- }7 `9 T2 I8 o7 m) Y( E1 ?0 R& x
- printf("socket...........
) W" A( h' o- H( h3 c5 W! C2 Z8 a8 ~: l - ");7 I3 T& l- D) [: r+ f0 w; k& q/ F0 F" @
-
3 H7 S( m* ]/ U! t4 P1 G, c - struct sockaddr_in srv_addr;4 Y" h) N1 s7 K
- memset(&srv_addr, 0, sizeof(srv_addr));5 C. x0 k6 v' W0 q
- srv_addr.sin_family = AF_INET;
+ M \- U. M2 ]* z; c3 A4 _ - srv_addr.sin_port = htons(8888);
Q# ]: s( k \+ K: i' j - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
% l3 ~" f6 j: g0 K - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))), X% i) \# m6 N! ~6 T5 z
- {% K' ?' b4 v' o$ Q; ^# D
- perror("connect");
7 e% `8 S A# \% O - return -1; //exit //pthread_exit
) p% q) J) I( Q4 [8 { - }4 Q4 P7 R! c `9 L b8 O& @7 v
- printf("connect..............
3 ^- o7 A4 N0 Z- N, N$ f0 T" `- w( Y - ");
# U* H! I0 E5 R5 v# O - char buf[100];
6 q- Q+ `/ s5 J7 a9 l1 ]( ^ - int ret;0 _( i! w j% u5 T, k- b# p) L
- while (1)2 L9 U2 I$ i7 o- _! M& N3 V
- {2 A9 c5 V. q% V. T* b
- printf("send: ");3 h }! O0 Q# j8 C" W4 _ Y
- fgets(buf, sizeof(buf), stdin);9 B! p- C6 {1 C+ R: ^* n
- ret = write(sockfd, buf, sizeof(buf));( a" _% ~( f' V+ {) h+ B7 x
- if (ret < 0)% g4 {. y O# M. i
- {- D S! ]+ X1 H/ m$ m9 h; h0 |' [
- perror("write");
1 p% I+ _4 f/ \% m# h6 \5 U - break;0 ]' u; }" i( ~& B7 V
- }: s+ o# W$ P% ?$ b% a& {7 F: x
- if (strncmp(buf, "quit", 4) == 0)
2 D7 m, z' ~; o& l - break;- U0 Z, [6 m0 C6 ?7 e
- }
" P# W. z# L! o, T" ~* ^9 ^ - close(sockfd);
# V& t0 W0 ~5 l# I2 U2 B" z$ x( O - return 0;( g" s' Y1 E4 O
- }
复制代码
( _) ^9 M+ |% n7 F5 T: [% e# r/ ?0 w
|
|