管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
( I) G6 }4 K: ?) M" O8 S8 j
& P& _0 Y" h/ t+ G2 ~
c7 w1 e# E6 S! i! m; R1 \socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
* {0 ^' h/ \# J- z
6 j1 ^0 X2 k0 U% c, K( q( Z/ I6 K5 Z- U2 s' H$ l5 n
TCP协议7 |; Q3 f; x* z; n {7 S3 v) @
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。; m' [% c! \$ x! J& V
4 k, r3 [* u) M/ T7 {, t Q7 l
: O0 z* X# E- ~) N# ~8 G关键词:三次握手,可靠,基于字节流。
9 y) w( a" G% E3 {; J4 l" y
* t3 f0 c& _3 g$ b" {
7 m& K8 X3 I y. @可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
7 L% k9 l; ] u5 V. d, i/ i0 h
+ H1 u5 A' Q; h; ^. cTCP服务器端和客户端的运行流程
5 P/ w& e1 \7 m/ `4 ~, V8 f J. M, k+ G
( m3 e4 u' ~$ M9 x
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
, T2 S6 t: ^( H: i" h& r+ s: C: D
6 d5 q7 y* |& z! R) I
' m0 W4 _& l" @8 {' e, b& \+ C1.创建socket8 {$ r1 E* Z0 }: f% S" [1 b! u! [
socket是一个结构体,被创建在内核中5 [9 ~7 q; e- M9 _; X
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议8 ^. x+ W. S' q G* S& m. ^ l
, b' y' P$ {2 |
7 W: i1 A5 N( x2 o! p0 ?* O1 J$ f" V2.调用bind函数
) ~, W9 E3 i: } 将socket和地址(包括ip、port)绑定。
7 V* T. N% F" o. \: L. `1 } 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序( j. O& J: m7 I5 c& p8 i, F
struct sockaddr_in myaddr; //地址结构体
: d6 `( {0 D' [* t bind函数2 ~$ n: _* b) g h! S* d* y, Y
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
. n* Q/ X; L0 M( {$ e1 {! T5 Y, C4 g. S9 P
* V% l! Q2 U5 }8 C% Y* {8 _
3.listen监听,将接收到的客户端连接放入队列0 @6 F/ D1 b) J( S
listen(sockfd,8) //第二个参数是队列长度* ~+ I0 Y& w: j4 [5 X
5 l7 C' D8 a& s: z( Q6 L
" z& S H% ^( p& v s: v4.调用accept函数,从队列获取请求,返回socket描 述符0 |- P$ h0 e) J% C
如果无请求,将会阻塞,直到获得连接1 R; C V. G* R! Z; L, X {
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数. d6 ]) @0 _8 H& r1 X+ R5 j0 }9 v
; z% A+ i$ A& e' m7 i7 ?7 J. t; n$ t( k# T6 h* ~$ f* Z8 K6 g
5.调用read/write进行双向通信
; D7 D8 F3 a" j6 `' ?6 _8 v% K7 r5 B9 r$ G8 Y7 ?" e
& D" z" E3 K- y2 ?% _7 ]6.关闭accept返回的socket& ]0 ?! L, k6 _9 z" \ r
close(scokfd);
/ G1 u4 h- Q% H! x3 ?
5 `+ d. C4 o- J* b
' o, D+ f. r' R* a4 O, B; x: b5 B0 d" j" A, f
+ P0 m8 v, H0 A2 a$ i9 k
下面放出完整代码
' g6 W& u& Q$ e. o% G
1 h! T2 [$ k9 m" K L5 T- /*服务器*/
6 F( K o3 q2 u- z o2 I) t1 v - #include <stdio.h># a+ B* p. u1 o/ ]( u: l9 h5 U
- #include <string.h>7 |! Q& O' \4 {5 ^/ X2 N3 X l8 Q
- #include <stdlib.h>& ~# H- C( R4 D8 j$ t
- #include <strings.h>6 ~+ j: |( [: x% |$ P8 a
- #include <sys/types.h>
, b2 P$ l) F' E" s0 Z/ \- |. Y - #include <sys/socket.h>7 ^. H5 u; S$ t# S$ k1 x
- #include <arpa/inet.h>9 G) m( \8 m" k3 j
- #include <netinet/in.h>0 f, h) n; O# ~/ q. t* s8 C
- int main()
4 t& _ A) g6 ~6 _) H! |! N - {
5 x( p$ Q+ ~; R - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
+ `5 [! w1 v j' }- L( l% `8 r* u5 X - if (sockfd < 0)
- i. h4 A9 j, i- K$ d5 o; f: N1 p - {! q$ r. A& c- u; s
- perror("socket");
/ m( r4 s' T! a, Z$ L - return -1;- t* h+ b6 W" {8 N, V# J
- } //创建失败的错误处理
) |4 D$ [; y! Y% U - printf("socket..............
$ M) j: m$ h0 F2 _$ w. j3 S1 P% T3 } - "); //成功则打印“socket。。。。”
: A* O2 G0 ] U3 p - 0 y0 Q. b0 b: ^2 d8 p7 e$ N0 h
- struct sockaddr_in myaddr; //创建“我的地址”结构体
3 S _* W* ~# n' F# E3 _! E, F - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
0 B5 }4 c, z! H' d' C% J+ S - myaddr.sin_family = AF_INET; //选择IPV4地址类型/ C7 I$ J! j: x$ ?. T. B
- myaddr.sin_port = htons(8888); //选择端口号
: C, C7 f6 x6 @0 N2 g! g - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址9 n" R/ [7 H5 Q2 D: l+ _& w% ~
- 7 z# ?2 S) s4 x( N
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字7 z+ [9 k9 N5 n9 P
- {
6 L0 s* Q+ p3 u- o - perror("bind");6 o5 E7 X! u# m/ b( y
- return -1;
: \1 P& E/ ?$ p - }' N- D# B* _: F o0 F/ B
- printf("bind..........
# o* O: X8 I, X6 U! l3 b, p - ");; w& t0 o( F5 q N% e W; d
- 4 K7 J1 b4 {& E+ _
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听) k7 X! x8 c9 e1 |- S0 [
- {$ h3 f( M5 [6 u- y
- perror("listen");
{0 J, j$ U/ Y/ K, o( T - return -1;1 |% ?2 w7 O( r* T8 B+ G! U' g
- }
6 E7 O+ f4 ^( m' ^ h - printf("listen............ S) }8 d7 G0 Z+ u- I7 V9 `5 J7 a
- ");
q8 l; `6 e! R6 k -
7 [& j" |) p+ [/ n% V" S) X: r; G( Q - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
0 | `% a2 B4 S( S - if (connfd < 0)
$ Q2 `) c4 V& g, P! N0 A - {
& s& u$ l" b9 v - perror("accept");
7 @2 C; V7 p; Z; ^0 s+ | - return -1;5 e; U, Y) [" S* z
- }% O/ p; k% S$ N+ ~& l+ n' o3 |8 U
- printf("accept..............; S2 D: e6 Y( T
- ");
* G9 f: e7 k3 t! w9 w - char buf[100];//定义一个数组用来存储接收到的数据# ^/ u; w" v( i* Z# I
- int ret;- ]8 V$ \ `7 z. r! _
- while (1)4 |% E- i. n$ c7 k3 J& x
- {1 W2 E: X$ U$ M( b& e4 v. A
- memset(buf, 0, sizeof(buf));
- Z( l+ q; B+ k; `# J& k/ } - ret = read(connfd, buf, sizeof(buf));# n+ L4 ^4 a0 O
- if (0 > ret)! Q+ w5 [- d2 \ ^" @
- {; v# M [& D9 Z: w" [
- perror("read");
: r# m. [8 T, b q$ R - break;# T( Z R" D9 y! n# D# [
- }//执行while循环读取数据,当$ w0 i# k9 r2 d! r0 X6 h
- else if (0 == ret)- w) n' z) E% l' i
- { w8 J- D8 o$ R A3 `
- printf("write close!! o% K( X/ l) m( ]9 z8 a
- ");
! z# W1 t% N% P1 I - break;1 j8 Y. j B& ?7 l2 d/ M; N
- }8 F: T* o; P3 f' b8 R
- printf("recv: ");
4 L! N" J5 c7 N( i& P - fputs(buf, stdout);//打印接收到的数据3 e2 [9 M9 M( d2 |8 q8 L7 b' B4 ?
- }
4 h4 f( b5 u5 H( i. |* h - close(sockfd);//关闭套接字2 u% }( Y9 }) F+ s% T/ Y* ^
- close(connfd);//断开连接
1 U* y0 H: K/ ~, k z, i! L. f - return 0;
6 B$ A! ^ N- o) L0 |, j - }
复制代码 6 ~6 q: Z) U) G1 J# `/ a
: l, C* a( A% O
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
: E/ V& M, u0 i) a - #include <stdio.h>
, W6 f# s- _% n, a' e: Y. S - #include <string.h>3 j! S9 o3 g3 Z
- #include <stdlib.h>
! A8 @) k/ l: i" G @- |9 W - #include <strings.h> A# ^. t! c# h; R! @
- #include <sys/types.h>
/ X5 i3 r ?6 j' E( a' |- M2 a( T - #include <sys/socket.h>- c1 {1 c. Z5 N/ S7 E! ^
- #include <netinet/in.h>- R H, t' N) i4 K- \
- #include <arpa/inet.h>
) z8 h6 u0 ~9 D - int main()8 g$ e/ f0 ?0 J7 D
- {6 r" j3 H/ o& }1 t/ r. V4 T
- int sockfd;' N1 a: Z& U1 s8 v* a3 ?
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
( F2 n2 V1 n1 ] - {
1 @/ \) h+ \& A ~2 U( C# b - perror("socket");
3 D# s, i8 M8 v - return -1;
5 E2 j" F% `" A, c, G/ Y# F - }
1 \- [: V) ^ ^# u- l! @3 \ - printf("socket..........., F/ D8 C* U* f% a; S7 _( m4 A8 k
- ");. g- S3 O+ P0 E) j5 d
-
2 V l4 j( W0 B6 l% `+ O* ^ - struct sockaddr_in srv_addr;+ ~$ z4 U( K& Y# R, x7 k, d
- memset(&srv_addr, 0, sizeof(srv_addr));6 s* G, n) B2 ~& a P) o
- srv_addr.sin_family = AF_INET;6 h# g. _. r/ j/ x3 ~
- srv_addr.sin_port = htons(8888);$ d M1 r3 C }% c0 Z
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");% `" R3 s# t' a# M* I$ z: O; @5 i
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
4 ?3 j0 V8 ~/ u8 Y, V; g, S - {
9 j% {+ ]# y3 a# O9 n5 q9 S/ }8 Z - perror("connect");
# z2 I6 f# `1 V" }# L - return -1; //exit //pthread_exit! y$ Z+ j; Q) ~; [( j' J1 r
- }9 T4 F+ r" f. H5 S, p: U
- printf("connect..............; y+ N% ~4 W z3 C8 m% T. X
- ");' B: a6 n7 ~$ K
- char buf[100];* z4 {) _" w5 S0 A
- int ret;
( Z$ g& D4 ^& t4 W( h - while (1)
' L* ~1 @6 ~& V4 u% Q; S: G - {$ \; v. F3 w3 o2 a' H7 |- y' m; |
- printf("send: ");# ~* D! w5 E( Y$ l7 q! }
- fgets(buf, sizeof(buf), stdin);3 v0 X5 q9 [' A7 V8 i1 x6 m0 y- ]
- ret = write(sockfd, buf, sizeof(buf));
5 R( f# I" S Q5 }) V3 e - if (ret < 0)
4 _2 K' i/ s/ d5 A$ J - {5 f% ^. h& m: G& z
- perror("write");) }- S8 y) L3 T5 x( i" c+ ], {% `3 r
- break;8 I% \& V3 M! b3 A: d8 J \
- }' a {- `! S) | g* ^- ^5 v
- if (strncmp(buf, "quit", 4) == 0)
W+ a2 G q3 T2 L- M# j - break;: d# }1 b& w& J' q% F
- }9 L; G5 i7 N6 Z* a
- close(sockfd);/ |, N5 ^) Z0 u! J3 J1 r$ X
- return 0;
+ @4 P2 T2 a$ d - }
复制代码 " [- j; Q) Z) r* ?$ k( }/ x
5 b2 z" C, D: p |9 p6 I
|
|