管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。/ ^5 @" E& L) C, J$ F1 v$ y9 x
1 O1 p2 E/ u0 S$ J& ~
' e( n! T7 S# O, q# csocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
+ C: D z9 @1 f2 G+ j; v2 K
8 Y/ q- J' w% V
8 Q( N) ~8 @& m) oTCP协议3 |. ^. w2 D$ k5 |3 a
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
3 s1 l' U* A) ~# d: j1 R7 j* S& M7 ?/ F" X5 ~2 B! W! E
0 R( R3 U3 Z* A* a$ v
关键词:三次握手,可靠,基于字节流。
+ S& t; S0 V4 V: X: X( `7 T. O5 r. K
" f! a& y0 i1 h4 j
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。4 }( C* y" f% P" T" w
# M2 R$ N0 [8 j( W2 v6 A5 e6 {TCP服务器端和客户端的运行流程
6 [( L& P3 c) G' a: e
% q* y0 K! \4 X7 ]- L
+ u* x5 f. O+ C: o5 G) o( y. }如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
& W. e8 ^- z: B) o6 g+ Z, V+ s+ z; n, }. }9 J+ i
. f' Y$ N A5 f" N, P+ ^
1.创建socket
# R7 P4 q4 T* j: i7 x socket是一个结构体,被创建在内核中
^8 W' r) m% d3 V; U1 q; q sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议3 `$ ~; s1 s( o
( h" \8 G/ m; R( r8 y) B% A& `2 d6 ~( }- T( J0 D
2.调用bind函数
8 L5 y/ K6 ~1 G% Z0 u$ ^. C: m 将socket和地址(包括ip、port)绑定。
$ |+ {; X/ [) i9 e$ u6 u 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序6 `4 m& K3 E5 F4 z
struct sockaddr_in myaddr; //地址结构体
$ D% P: \! j" o6 w' f bind函数
) R4 x3 k" i1 l0 T bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% q5 _$ i E+ Y
; B8 X; X: [- ~3 n* ]
! M, Z, b7 }4 K4 s) y& e2 v3.listen监听,将接收到的客户端连接放入队列
$ ]/ B% h8 x9 W* |! n3 o listen(sockfd,8) //第二个参数是队列长度, u: `( k" ]2 ?6 ]1 b0 C: i
% x. } K$ D+ q4 T
% O4 x: A% p Y9 y+ b6 s+ i4.调用accept函数,从队列获取请求,返回socket描 述符- d, _( n6 w, y$ v; d% e
如果无请求,将会阻塞,直到获得连接
- `. J7 ~& U# _1 f. I( U1 { int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
) w7 I) E3 V9 o7 J
+ n0 ?* x7 u& l7 ]: t# i( `4 N& ^* I- f6 ?2 @
5.调用read/write进行双向通信
! }' L+ x" I( B! c: u: J- z: I7 a1 p% A6 n
- x/ t7 T( ~5 d# ~+ a
6.关闭accept返回的socket( p) o9 o2 g) f4 l' X3 o
close(scokfd);
1 x* G) c7 j2 p4 [; g6 M; @6 G: a. K( g) y0 n
) a# j: S+ k9 F7 l9 s, t4 B
5 x0 l! K, p+ U' ^/ i1 x. ~3 c& l
1 E! X& U! q* p: Q; |下面放出完整代码
# e0 N* {! F! N% E- Y" A- w
& w% B6 o3 u8 w. [6 M0 Z j) f! W- /*服务器*/
3 Z( b" n- m% p3 a$ M' M$ J* ~ - #include <stdio.h>& }$ P( P' F# \- H& X
- #include <string.h>
' r1 x* m3 N4 d8 f - #include <stdlib.h>. A% M: [* x3 w# Y4 p
- #include <strings.h>! H0 _7 F: w5 v" i# q2 h/ \; x
- #include <sys/types.h>
5 [8 d) P8 X& d. b* \7 \, Q - #include <sys/socket.h>/ b b5 F" x1 e4 n; K4 }% |% k
- #include <arpa/inet.h>1 ]! R0 H! M" B3 _5 M
- #include <netinet/in.h>
5 {1 P- {5 {/ z0 F P - int main()' a- ~9 q0 G; n- M5 J, Q& D
- {
3 V, e; t3 l$ N) T7 t0 P* N- K% s5 [ - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字1 `$ z" q& f! [ C+ |1 ^. z
- if (sockfd < 0), @% \: O. o9 q' M5 R
- {
( N4 D0 A4 \' E9 ? - perror("socket");
! |# t: d3 e* Y/ t6 O6 g) O - return -1;! v, {# z: [" u; R1 S( D( t+ o
- } //创建失败的错误处理3 g% p6 V# \5 ] ]4 U
- printf("socket..............
q4 P# C, j* v: h4 F9 O8 |% H# ? - "); //成功则打印“socket。。。。”0 f% j% P% q. C+ h, K0 ~2 H
-
- f4 S8 n8 `2 @+ P! M7 S - struct sockaddr_in myaddr; //创建“我的地址”结构体# J" w9 ^, {3 z# `; T; {
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)$ P, Q' L+ F2 I! m* ?6 Y( J+ b. p( ~
- myaddr.sin_family = AF_INET; //选择IPV4地址类型- r2 H5 V% p3 s1 a+ |& ^
- myaddr.sin_port = htons(8888); //选择端口号
; n# {7 i6 F) i8 ?7 G, F - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址3 o6 _: o( \ d4 u: S
-
% D* q; c0 h X2 A3 J) g - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字4 g3 q+ i/ C5 z1 ?8 u
- {
( ^+ a1 `/ Z1 y& V1 { - perror("bind");* M# k' \6 U e& t! F( u
- return -1;
/ a: h( b5 |1 Z1 u/ }9 s - }
: W& U0 p& l) d+ R - printf("bind..........
4 D( E. P% F1 D {: {6 `6 O0 \ - ");
5 L" N2 X& P1 b4 {* N" S -
# M* }7 d3 f0 q9 ^+ I - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听/ s; }. o& o8 |8 V" x
- {0 }) P2 j5 z7 F4 h4 i {! N' `
- perror("listen"); E" l# e2 I' d: y) L( w
- return -1;
7 i3 W8 c {% p2 I; d: p0 E - }
5 d* |" T! c$ o - printf("listen............
1 V$ J$ v' j6 Y$ W9 g4 a - ");" O K5 @. L7 C2 a4 _8 D' p
- " z3 d8 y5 B* v9 }) |" S) a
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求' m/ D7 Z8 K* F/ V k5 y* I6 n' m
- if (connfd < 0), c9 V# @) W+ G( H/ N! P2 N
- {' p6 J$ @, M [9 w* F
- perror("accept");7 v) L- b x5 R- J/ h6 r
- return -1;
4 U+ M4 r; W$ l8 W U4 W$ B' | - }0 s# I9 u9 t i a- O1 T2 v. I
- printf("accept............../ ]- W, `% l* n3 A! j2 Y
- ");
& X7 ]; b: T" x) ^1 r - char buf[100];//定义一个数组用来存储接收到的数据! \7 Z( T7 {" A2 M" t* m
- int ret;7 x& C l, d) z# O+ Y1 @5 G5 k
- while (1)
& K: F* [7 a" l' f8 ^ - {
b' s* d! D. Z - memset(buf, 0, sizeof(buf));4 V1 Q) V( ~/ g+ E2 G& k
- ret = read(connfd, buf, sizeof(buf));
5 y9 N7 L+ S {% Z - if (0 > ret) C$ ~; F, ~# `$ k% O
- {
0 a! p3 |, H6 W7 i# g0 L - perror("read");$ ^: h4 _& c2 ?/ s
- break;( z/ O' E, P: x1 u6 }: J
- }//执行while循环读取数据,当8 v# u6 g2 E( p# w: M
- else if (0 == ret)
! P8 @% @+ G4 @6 E2 r) { - {8 a, E+ \- X0 A3 b2 I1 L9 O2 P
- printf("write close!
1 V- Y- g% _4 F/ {" g: |% @ - ");( _3 }9 ?* E) F& E
- break;8 E2 D3 }8 b) D% u2 [
- }* J2 j! b' A8 T- H
- printf("recv: ");
0 x) \, r j% ?( O: L) p( W, } - fputs(buf, stdout);//打印接收到的数据
! H5 E- G/ K# l - }& m. _5 p4 T, Q p- S
- close(sockfd);//关闭套接字
* d( D/ @# W0 s - close(connfd);//断开连接( Q) g( M9 N5 C* B9 l
- return 0;8 l7 M9 Y( a) }9 q
- }
复制代码 8 s4 ~+ e. t! |
& n7 V5 y* N" u/ P- /*客户端*/(具体功能和服务器一样,所以不再加注释)
2 n" s) B- W" s/ q2 J - #include <stdio.h>
% f, ? d3 b" X/ } - #include <string.h># z1 `6 s7 D3 h# c% w% w
- #include <stdlib.h>6 r% j3 s: R! m' _
- #include <strings.h>( M" ~7 m7 g, H. i% M. f
- #include <sys/types.h>
, l: j4 q( \2 k( y - #include <sys/socket.h>9 f1 {9 F$ L6 h% D3 a% `
- #include <netinet/in.h>2 Y3 _/ x0 m1 g2 s1 y
- #include <arpa/inet.h>
& G: c4 o! s& ]9 [8 c: y - int main(): ? o# x8 W% K9 S7 Q. w7 T+ y
- {2 r: C8 {! o$ s: v+ c
- int sockfd;
6 t b: q) ]1 y1 r% Z' B9 W& _4 S. D D - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))( F0 n3 J4 Y1 _# p7 i
- {! U5 H' l! V; Q N
- perror("socket");
/ B* J# @% D9 \% S. u$ D - return -1; D6 N# {+ E# v: G# W6 p! W
- }
9 X: g* ^7 c% u9 R/ Q - printf("socket...........
% X: l: ?2 i6 b$ B! {2 M4 o - ");
8 A; c$ d4 E8 F6 H% `& K/ ` - ) M$ z* U! o4 h, ?
- struct sockaddr_in srv_addr;
" G* G8 m4 a$ g" ~ i2 l x - memset(&srv_addr, 0, sizeof(srv_addr));& I `2 Z p! o. j3 A# E4 i
- srv_addr.sin_family = AF_INET;6 l; s! ?0 D8 \
- srv_addr.sin_port = htons(8888);
2 K9 O" Z8 r P h/ ~ - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");; v4 g- H! K' ?' ^# ^ F
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
, [' x6 ~! h# x" J - {
9 @" F! C6 {! H4 V" z( a- k( C - perror("connect");
4 x3 Q0 ]+ h; J- X4 l- I - return -1; //exit //pthread_exit
/ ]$ k" X+ D1 S" b0 q5 u - }
# ?+ ~% |" I6 Q3 y/ n, n - printf("connect............... C4 w' K/ R7 o/ r, X R! d! Q, B
- ");8 B% w1 p: j+ ~5 Y2 Q& T2 J
- char buf[100];
( c+ M8 m- L3 I6 L% t- u - int ret;2 @! K8 s! v/ J: J r; u
- while (1); C8 { |- F4 }9 M
- {- e# `; u0 t9 K! i
- printf("send: ");
5 N9 j$ B, ]' }2 c3 L0 v& y - fgets(buf, sizeof(buf), stdin);1 t' h9 ^7 d9 ~# `0 ?3 J
- ret = write(sockfd, buf, sizeof(buf));6 O! y3 b. a# a" k/ r! H7 ^
- if (ret < 0). s O E+ s; G4 X" i$ p+ w1 |
- {
8 h1 @3 p5 w# `4 V7 @4 [: `5 `& ~ - perror("write");; O& K, o/ K7 R9 @
- break;; M* E" _8 t2 N G# ^+ z: L
- }: t( U) p( W9 {2 u% ^+ v
- if (strncmp(buf, "quit", 4) == 0)
7 g+ n) E4 w2 U - break;! o; a* Y! n( s( W4 p0 B
- } H1 ? ]5 R, D* X7 g0 d7 c5 c0 d& X
- close(sockfd);
. p9 W$ X6 \% h' D - return 0;. O" U: S2 ?6 }/ o/ u2 b
- }
复制代码
; h! u2 ?- {4 J1 c9 z/ s! G _: g$ g
& h# u2 w9 P5 w! ? |
|