管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
! t+ {* O$ a5 ^6 G" c, L; G2 Z+ o% h" W2 l9 I* ]; R! U# w3 X1 ~
8 P2 Q: ^3 C( z. ]$ O0 _5 ^socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
3 d+ @) b5 Q1 l8 Q7 u2 f# ?8 ^( D. N4 C9 \) i* e# Y$ T
; [7 y: E' o6 s& K3 K7 m( i- X! K
TCP协议
6 ?3 K1 ~: J0 s+ G$ uTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
& m7 l0 f, F: V& S- k3 W
, m# c0 r7 `" M" J1 W# F8 ~2 C1 Y1 ]! t* g8 g9 Z, l! s- K: \
关键词:三次握手,可靠,基于字节流。% c; |- Y" U) j# U5 K4 ^
- j: J! m) @: {$ ]6 w7 E. [7 D8 W% A4 O8 a% S# [
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。+ F! u8 R; U: L6 |# [2 |$ i
- S- H( }* n) p. l0 P& A0 S0 i, qTCP服务器端和客户端的运行流程
{- X% \( N6 p/ S( i+ S% u& B, K/ z; `' C* k
% p* p& u3 S P$ h/ ^# A
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
8 y6 w! u# Q3 y' t, e
7 h% L4 u; D. A, V/ l3 W
" C- Q8 ]- q4 [9 A! v: i1.创建socket; ^% g4 d) A# q1 a
socket是一个结构体,被创建在内核中+ s9 b: s, }' V& i. H+ C! Q2 c
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议$ A3 g0 Z0 D# @4 V2 P+ c/ ^4 h
+ t( l0 e) M$ B, H* ~0 z: E {3 X; g- e2 T9 x, T; A
2.调用bind函数: |! {6 N. Y- z3 C: A# D, f0 b% E
将socket和地址(包括ip、port)绑定。
. M f. D6 @) b/ n! U! O* p 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序# a+ u' P# H( y
struct sockaddr_in myaddr; //地址结构体$ ?6 K6 g7 b! V& d0 c7 R# A
bind函数
: D+ z! n# n$ J! W. w7 L3 u bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)): g$ s: e2 R, I
, C$ W8 f! e- m x! [* A
# B6 O& B2 T2 z( J4 T% p& H& ^3.listen监听,将接收到的客户端连接放入队列3 f0 \: J# N8 H! N9 N8 w
listen(sockfd,8) //第二个参数是队列长度
% N) \, y0 f2 Z9 j0 Q- K/ B
5 ~6 Q, _- V% ]$ T: P# E# X
; |8 r: b/ \( b% C8 A& o4.调用accept函数,从队列获取请求,返回socket描 述符0 m! R' R, Q6 W; H
如果无请求,将会阻塞,直到获得连接- }4 `' x. k6 a9 q4 B* X Q# L I' \
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
) C$ C5 J k3 f9 ?' K7 Z+ i# r$ a- ^+ W" _. E# Q2 k. O/ s
, k9 d: w. h [, `, \! T" h2 V. P: @. p. }
5.调用read/write进行双向通信6 d5 ~6 h7 {: ~1 y+ @; P$ U* f
- D( [- J0 J* Z1 ~
+ W( G# E7 a3 k# l6.关闭accept返回的socket; s+ @7 O) h8 a b; x
close(scokfd);- `9 W% j2 i( m0 Q, o7 c
4 a& v+ x6 S8 q, N7 I0 a6 w' r* E1 i" r( t
|. H' w( l1 D
3 o" Y1 H5 |! \- T下面放出完整代码+ f4 O2 n1 {; U5 J
6 f! S8 I/ m2 X( g% z- /*服务器*/. I2 V! [9 @& A0 N' n5 v( h& _
- #include <stdio.h>8 l5 i2 u6 q+ b/ K3 l/ J
- #include <string.h>
' v" |$ M; c3 b) D. d) l/ a! I - #include <stdlib.h>
5 _8 e9 [ t$ w - #include <strings.h>
+ L, h( B6 g0 @/ |" E$ L0 \. N - #include <sys/types.h>
5 `$ o: ^4 n* j3 Q - #include <sys/socket.h>7 s+ n1 d* p5 d; g1 V# u
- #include <arpa/inet.h>
0 k. D& f: ~& w5 b4 t/ ] - #include <netinet/in.h>
6 i5 z6 p+ S- i+ v8 [# S& P' I - int main()
0 |. i# L4 B( ~& g$ ^) ], |5 Q3 M - {
( V, N$ F1 s: G$ g4 c - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字" ?$ d% K. u) m7 U' g
- if (sockfd < 0)
: Y9 T9 q# m/ A6 D1 Z - {& H# @# E+ B$ [/ Z5 b/ E
- perror("socket");
* f; F# s( z) l - return -1;5 ^+ Z3 c; \ ?" I5 N+ u6 ]
- } //创建失败的错误处理( k9 H# E N4 Q/ R7 O
- printf("socket..............) B$ S- L- _7 A# x! ]) |
- "); //成功则打印“socket。。。。”
2 N# L( I; B! Y8 I -
5 X t( `' j9 E F0 f9 E8 t6 z& o - struct sockaddr_in myaddr; //创建“我的地址”结构体4 a$ F/ |- S; y/ m; B) ~2 G8 d
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
1 l3 r+ ?/ ^ m) v7 T0 I0 b - myaddr.sin_family = AF_INET; //选择IPV4地址类型" D8 q8 M, f8 j+ B$ s ?8 @
- myaddr.sin_port = htons(8888); //选择端口号
, a! S9 d1 m4 P( l5 P( \$ N - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
- \% N* k% w4 |7 T5 | -
$ V# G a1 E w+ t( ` - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字 C& e: g$ { V* v) @ n8 o2 R
- {. Q6 C1 P& ^+ z/ ^7 P8 K8 t; N" w
- perror("bind");
, T8 R! m0 { p9 L: R6 n+ R+ ] - return -1;
' P; f6 c# _" O9 ?' Z - }
! g& p. D6 K# C% x9 @5 Q: j - printf("bind..........
7 O. I+ G" b. O - ");
5 E8 i$ @8 b. ?% v; N* l0 X -
" Z+ u' v' A" K- x4 Z - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听4 m: b9 ], N( j
- {, }1 f: X9 K8 |
- perror("listen");0 T3 T5 `/ t: F# D: _ P/ r1 B& K h
- return -1;
' K4 e+ W8 m0 Y% O - }; N5 G! _+ Q5 z9 \
- printf("listen............
/ t# W0 E& q4 ^7 B+ T; [ - ");
7 J4 Z" ]9 ^) U( y# H - ) p+ l) ^/ L7 ]/ V# F
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求, s! @0 `( s3 G3 t) e+ O5 R W6 L3 w
- if (connfd < 0)0 ~ }% c N7 d3 S2 }8 }5 z5 X
- {
4 w, R1 K6 Q; ~) E1 S - perror("accept");
3 k' F5 V4 q9 E' P7 ^ - return -1;
# L0 P L5 m$ K" V& X. a& x - }
# H, O) C* [, h' H7 f - printf("accept..............+ |! j; B% L# Z5 F* m5 l
- ");
! h: g9 V% ]8 {4 P1 l* p6 [ - char buf[100];//定义一个数组用来存储接收到的数据, g8 k/ ~* o" N. i* ^
- int ret;! |; d4 v2 M/ p& r4 I% ~( e
- while (1)
2 a2 P0 D5 z5 J: T: C - {% J, K/ d1 ?7 F+ l- K2 S: U
- memset(buf, 0, sizeof(buf));' m' o' F. n+ J) I( E
- ret = read(connfd, buf, sizeof(buf));6 V+ o* D# E: X1 N/ h
- if (0 > ret)
, x' ?! [+ e. `! K - {: [0 b9 j- E, _: I# U
- perror("read");2 N. Y1 e; [3 \! s
- break;6 ~( o0 a1 \1 N, p! S) @
- }//执行while循环读取数据,当
" w; B- I. W( p; ]9 p8 N( h - else if (0 == ret)
% j/ l/ f8 G! z( }! s2 l! F - {6 [9 i6 ?. T5 ]* Z2 W
- printf("write close!
$ ]* d2 q4 ?! I }! _3 J - ");
6 ?8 I; \! E9 }- w( t b. Z - break;7 n7 ~' i, {# R+ [1 E
- }
" |# u4 U2 \6 I4 j+ a3 X7 i - printf("recv: ");) M% ~3 Z$ `( l9 M2 O5 c
- fputs(buf, stdout);//打印接收到的数据
! L& O/ w( [7 G+ ^# } - }. c" H' Q9 P8 Q8 H: n3 O
- close(sockfd);//关闭套接字4 s2 [! N; u" R# R$ r
- close(connfd);//断开连接4 i& [5 K/ i# [9 a4 U3 M# y
- return 0;
8 a( Z) B: D& I8 L4 ~ - }
复制代码
$ B" }5 m3 i5 v. F9 X
4 C5 b9 c( _% Q4 l( B) A$ U- /*客户端*/(具体功能和服务器一样,所以不再加注释)
6 @8 h) F* a' D% |9 D( r# x K; t - #include <stdio.h>' p9 \$ R6 h+ c/ S% y
- #include <string.h>
6 y' L! b$ a6 `# k- t* b - #include <stdlib.h>: S& `; W/ M4 t$ v( A6 z. ~
- #include <strings.h>
$ G5 g5 B, f* I+ i# u9 e - #include <sys/types.h>
( k* G; n7 ]1 R3 p) ]" k+ z; R - #include <sys/socket.h>
/ O9 @- [! g5 B& b0 \ - #include <netinet/in.h>
3 A+ E" H9 M' r2 S6 J& U - #include <arpa/inet.h># ^+ {6 W( }% ~+ N" d
- int main()
' M" ?* l( u0 g5 @7 { - {
$ g a2 g8 I, r. h - int sockfd;
+ o0 s9 i: K) }( Y4 k - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
) m8 k' c( H5 o: q8 l - {0 A$ w% ?. w k6 s7 _! [6 \
- perror("socket");$ s& a F( k6 c0 X* ?
- return -1;. x" G1 g" v) N$ k2 R8 r# D
- }
. S& F; q8 e- @; }! E; Z) i' F# e - printf("socket...........
/ m% Z* Z7 O1 D( ]" t; K, Z - ");2 l0 M# f2 S9 q) i/ ?
-
7 ]% s! ?' @7 b4 a2 Y$ k3 W - struct sockaddr_in srv_addr;5 \: B: B4 a% n. A6 I/ `( L% p
- memset(&srv_addr, 0, sizeof(srv_addr));
& s4 j9 {( s5 Q% m6 v - srv_addr.sin_family = AF_INET;; B- B; J( V2 X( G
- srv_addr.sin_port = htons(8888);- s9 ~) v0 J2 Z% u, q
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
( p6 {' p: _! l: p - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))9 Y; P2 |# c* g: k7 N7 g
- {
, s) O2 {1 i% \2 K! L- n# s8 b - perror("connect");
1 q' t* W9 L: Q" W3 `( C - return -1; //exit //pthread_exit
/ ~' J. M* n- y S0 X - } r) X8 Z+ ^; H+ v
- printf("connect..............
3 N. h+ k8 t" J5 q. }+ b+ Y+ k - ");1 J( E$ W% q; s4 U. ?" N
- char buf[100];- k2 i" O; i G5 L- u+ ?0 u. `
- int ret;
) a3 B R/ z, Z% R& a2 e - while (1)
0 r. [) Y4 U W k7 ] - {9 S+ e" l. L1 t' W3 M+ Z
- printf("send: ");
- p/ t1 ]7 V: x - fgets(buf, sizeof(buf), stdin);6 @' d' d. v' V- r0 o
- ret = write(sockfd, buf, sizeof(buf));6 K3 r# ?1 g# D! r, U
- if (ret < 0)
- e6 R0 z. r9 t. j O- A - {- e6 N+ S, V- A9 a2 F
- perror("write");4 u- k) c1 R; ^9 w' v2 O
- break;
5 }* u3 n/ [2 ?! @8 B8 h2 _: @ - }! q1 C' s; y) z, @. H! t2 ^3 H2 p
- if (strncmp(buf, "quit", 4) == 0)' F T3 N6 x1 v. N* R+ F v
- break;) F5 B! r' D* S2 f j* `! W
- }
5 ?5 w# B. W7 }% Z% g3 ~1 U - close(sockfd);& }& b' G$ ~1 s: F' e3 B0 ], m
- return 0;
) I0 V" n! @: l* G! n. n' S - }
复制代码
8 W- _% ~: K1 @3 h
! b8 q6 Q, s2 x2 o: C( [ |
|