管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
- M+ ]) o2 Y. ?! \0 v5 y" I! u. w( T" \/ I
" K3 F( z0 I- Q1 i: Fsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
' S+ s+ C/ K* G; B- @$ b" i. @; N7 P0 f, F! |4 _) k
4 Z4 y* H0 x/ j; PTCP协议, u/ o: H5 s( `
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
: b! G& x, b+ a8 N" u+ R' v
& L$ z( W2 v& y, y" s; Q3 `3 w7 w7 D2 o
关键词:三次握手,可靠,基于字节流。
7 e, c: d- Y% G6 D( E- I* N$ M) G- `
5 H; J( G3 k9 w. c$ ~9 b2 L
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。+ [; Q8 d. R$ g/ [! @
: s/ G4 N" N' w) Q0 ? D9 Y; XTCP服务器端和客户端的运行流程
! k6 \# p" i; J* `4 D( @; D+ v
7 t# H+ K! h8 f$ Q* q/ A5 w
; t4 l0 T! O) G4 ^: ^% q1 @如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?4 _3 v7 o% _1 O
0 N& d, r8 q6 I
% k4 Y5 M7 u" i' k) G9 d
1.创建socket4 d& t& q1 ]" A; A
socket是一个结构体,被创建在内核中; R% ?2 H& t/ ?9 I9 n% ]
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议" w- ?- ?$ g) S! K; r0 q
# m& ^4 }: X4 j: E: @4 p6 q4 \
! x3 }* [- c, A4 ?2.调用bind函数
: n5 j' T# l4 M& n* E 将socket和地址(包括ip、port)绑定。
* q5 a) z7 s3 S6 _ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
) f( r$ y3 ~6 G$ r* H0 T: c struct sockaddr_in myaddr; //地址结构体
! l- m1 f& p( }4 n2 j5 ^$ E bind函数
$ I) C7 a! i3 p5 i W5 ] bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)) @% D6 B" p2 a% h( U' s2 _
0 @- |4 l, B$ W/ n# \
- X' \' V% N" s" z3.listen监听,将接收到的客户端连接放入队列
: {) G& G/ d: z- N+ ]% l5 E listen(sockfd,8) //第二个参数是队列长度
5 D0 N% K+ s. P9 }
J. {& q' m! u, N$ {& f6 H# D" A! N2 D G
4.调用accept函数,从队列获取请求,返回socket描 述符
5 {' q0 y: R* J* {6 |( _ 如果无请求,将会阻塞,直到获得连接
# |8 o- t8 I8 C/ v( O d int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
* S. k6 y6 f9 y. k) U& }2 h7 C! C" R) |" n6 i- p; v: M, }4 V- W' X, d3 L1 A
" [3 h; ^2 z8 @: L1 G* D) R
5.调用read/write进行双向通信
/ t- b( q# B1 C- Z
9 R0 I' m) F/ ~+ ?
* l, \* L, w$ a% V. o6.关闭accept返回的socket
. m2 V3 |) f3 V" N' g. R4 X8 m close(scokfd);
9 L9 K) |$ {- I- Y8 s4 m* w3 w+ @: A6 F u, ~$ X
8 K2 a! E0 n7 x, S7 _, b
( |. h1 p) f" Z/ [9 H
- g0 l$ l! W! Z; ^1 C f* h6 v下面放出完整代码
2 h/ v) R% J) `6 R6 a
4 ?* B2 Q k% k* ]( I% m& y- /*服务器*/: v, x5 q/ D" V( N' N
- #include <stdio.h>5 A* e4 T/ {3 y1 Y( f
- #include <string.h>
; k1 u; y" ^ i" p! Q+ s - #include <stdlib.h>9 u/ c9 Z( n# g% `
- #include <strings.h>/ x5 T4 B, i- w. u2 }
- #include <sys/types.h>
* q" V/ S k+ S0 b - #include <sys/socket.h>+ J3 Y9 L' w+ E6 g
- #include <arpa/inet.h>& V- _: e6 H& c; _+ i
- #include <netinet/in.h>: q ^6 K) l& ]( k
- int main()
- r1 Q$ w1 @: S, `3 D9 d- ` - {/ q T/ l4 X" v$ L7 {" X& s6 H
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字3 G* ^7 R. Z. d; o2 f
- if (sockfd < 0)
4 o* K+ j4 h9 v - {
. o# ?) @2 j# e7 l! H/ k - perror("socket");# |/ k/ R- m$ H F" s9 p, A
- return -1;
8 E2 g0 W% v& h2 `- A' a. T4 x - } //创建失败的错误处理$ b9 ^. [9 `( D
- printf("socket..............
8 b7 H2 Y V% V& y& t) ?6 y - "); //成功则打印“socket。。。。”
& C, H; d( i( }; g4 A* D - 1 @, q; [3 J( e! Q2 B2 p, L% t
- struct sockaddr_in myaddr; //创建“我的地址”结构体
3 a" j: M( a) D! N: \0 p) h - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
- g; D& o ]% {! ~ - myaddr.sin_family = AF_INET; //选择IPV4地址类型/ A( H1 Y* L. k4 h0 ]
- myaddr.sin_port = htons(8888); //选择端口号& l2 l- J' ~; X8 ^* f. J4 ?
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
5 W7 d; U/ i. a" u" k! }& h - ! z# D/ |; w2 E3 o1 @% Q
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字- `- _ _" [. Y: c& n" K
- {
% M8 H, V. ?, w! C - perror("bind");- \5 Q2 O# ~" A6 ~$ N+ t
- return -1;
4 B" N. j9 a7 N, M r - }
7 j7 {$ F$ d# c9 l6 [9 {! M - printf("bind..........
( ^3 a; O$ U0 {1 K& U: c4 ] - ");
" Y; g/ ]( H$ s$ w -
( L, |5 Y6 N2 I& t2 L( [& M - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听( p9 c, c3 r1 ?* t9 {6 R7 R9 _7 f
- {
- w7 F8 G0 m* i" i) `4 m0 A: [, K8 } - perror("listen");
/ M2 V& n! G; c - return -1;
# y- _& I0 Y2 B& A' p - }) W. x" m/ c3 D: ?: G: B. w1 }9 @
- printf("listen............
9 v( e) m1 V0 n. H - ");& T& J [6 u1 p# E+ P' j: D
- : r& G( f2 j9 G4 \ u) G" A
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求, f1 r5 ?, X1 n" Q" j! F
- if (connfd < 0), t6 K1 ~- o! S4 _2 R ]
- {2 A+ C/ O& U: S6 ?
- perror("accept");! |2 X3 K' B% x+ w( `
- return -1;" f+ l# r- s8 Q% L% _
- }& [: B5 d# x1 N: _8 M8 h+ s
- printf("accept..............
! [ Y& F% v5 W* j- T- H! e - ");
5 f, ?( _9 [) _# B/ A# @ - char buf[100];//定义一个数组用来存储接收到的数据+ V/ b+ f. W2 i! _' o
- int ret;
& m: }. [! o7 i D4 E) l& v - while (1)3 ^! t3 l* r3 ~' m! _" q2 f; |
- {
! _! o! X* P: S: b4 S - memset(buf, 0, sizeof(buf));& `6 m- i7 S# h1 A/ `5 k( g1 v1 Z
- ret = read(connfd, buf, sizeof(buf));
; c$ O9 V" b! j0 q4 e" @ - if (0 > ret), c' ^+ [4 G2 x ^4 f/ w& a
- {
8 I4 M/ {" D. Y& u% @) w. U G! q6 O - perror("read");
7 t! g" G: l2 I: q - break;1 L9 M3 I3 }' c" L) W
- }//执行while循环读取数据,当" z* l2 S' K6 g7 p" l# Y3 d
- else if (0 == ret)
9 U$ E+ d6 G0 V7 O) @3 J - {5 \$ w5 u/ i* i2 |) o, c( s
- printf("write close!
6 R1 e- f7 T, O6 ~1 P! [ - ");) B1 p3 m8 F, @7 H# T
- break;
/ ~; [# ?7 ]! s' M6 k - }
5 D! J2 H8 S) ` - printf("recv: ");. W( W% ?( {, q# Q; f! E( a: F
- fputs(buf, stdout);//打印接收到的数据8 o: T: e/ s3 z
- }6 {6 @" o. }) v2 N1 P4 \9 w
- close(sockfd);//关闭套接字# b2 v2 v' [8 L! _& A* s2 x' d, F1 X* s9 ^
- close(connfd);//断开连接
" h+ i6 ^% N, c' k1 ] g! j- { - return 0;
/ V* R) x6 x& [ - }
复制代码 8 Z! U, m! T' V/ ?1 {# U+ L- M
6 j8 B( I: j- p. J- /*客户端*/(具体功能和服务器一样,所以不再加注释)
" d0 f" W% {' b# s - #include <stdio.h>
( K% b! _4 }/ H4 t) Q - #include <string.h>( I" d! m) P" ^' ]8 m: d! w4 M1 ?
- #include <stdlib.h>. N: g7 q4 S7 t6 L7 }( y
- #include <strings.h>
& [( H( D+ ]5 e2 ~" |. M - #include <sys/types.h>
( N& ^9 h: a: y I, h+ L - #include <sys/socket.h>6 {; c# C+ `# p0 c# {9 i
- #include <netinet/in.h>
* U5 A* R3 B7 q' Z1 f7 e - #include <arpa/inet.h>) U6 J9 ]& |% w6 E9 C- l; g' r
- int main()7 t4 e1 N0 E: _% K# s% m3 a
- {
& O) z; P2 |2 J) W- u& J - int sockfd;
3 l% X. w; W3 d - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))' Q1 J8 }. F. o, b
- {
4 h% k% T2 y0 J6 @2 q j0 X! l+ J2 u - perror("socket");. Z! C+ K8 g7 ]6 U
- return -1;- o a. C4 I+ F2 } }/ }9 g
- }& L. q, {8 o$ r+ P, b' e8 n4 R4 ?
- printf("socket...........
( y$ m' W$ ~2 Q5 X: P3 S5 z" k - ");# X$ i5 h+ `5 x/ _4 J, H
- 2 ]7 c2 F Z* @6 k5 w+ l2 s
- struct sockaddr_in srv_addr;
. u4 F- g" Y' }3 D# |5 r - memset(&srv_addr, 0, sizeof(srv_addr));; r$ Z$ a. L4 E! p
- srv_addr.sin_family = AF_INET;; I; G: v4 E# C
- srv_addr.sin_port = htons(8888);4 ` t( ^* P: _ W b
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
+ d, u6 }% c# `& I- e$ d8 u+ c - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))' R6 o# L+ k9 Z% q) Z7 z+ p" m2 _
- {2 K$ `% c: W: _
- perror("connect");
+ G" G6 ^7 s/ D3 K3 F- N: y8 K6 G! T - return -1; //exit //pthread_exit
% X0 b1 E M: O6 b7 S - }3 K! T+ M |: R7 K4 Q! t8 G% `
- printf("connect..............- e- ]! r7 W) ~* h+ A5 Z
- ");- |0 ?5 @5 m7 K
- char buf[100];
* k( _+ [+ l' w" q - int ret;3 X( D: h% j/ U
- while (1)+ G: C0 W: h' @' b
- {
' L, J/ e3 U+ z" k - printf("send: ");: l: a7 k# Y* {, u- v
- fgets(buf, sizeof(buf), stdin);
, |2 M2 ^7 B$ N" l( @ - ret = write(sockfd, buf, sizeof(buf));
/ s. ^3 C2 b! x - if (ret < 0). y8 N6 t1 V6 Y9 u6 t
- {+ `! R, w3 w0 Y" b9 D# m* m# p6 G
- perror("write");
6 o2 l! v- e" L" U% k - break;# m a# Z# Z$ @, g
- }/ K0 n4 t% @7 q/ J5 V2 V+ s
- if (strncmp(buf, "quit", 4) == 0)
9 Z3 |5 r, K/ k3 P p7 n | - break;- c7 Q; @7 u: b+ l) l; g
- }
& M7 O5 {" f: T6 j - close(sockfd);
# b8 M' Q1 U6 E0 y5 a- y; ] - return 0;
9 ?- W9 i( r0 O. ?) z% G( ^ - }
复制代码
; D& k7 o5 G1 ` ^2 u+ g1 q$ i2 _, F# R) l( k$ S- G6 S3 p
|
|