管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
. L+ v, ]2 X2 Y6 ~0 {1 ]& s
" h4 f' J5 U! j# r9 K% H S9 ^! `5 H, s0 i9 {1 s' E
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
- b$ H# X7 n S! o( ] i8 Z3 c# g' H N; D5 G* R
3 r- m8 ^5 u2 w- o. I" o
TCP协议
. |; D8 \( M3 c: R. `, B" ATCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。; U4 _9 H& Y- h% @
0 K" t2 H) f+ t8 u
+ W! c: v. S3 o0 h关键词:三次握手,可靠,基于字节流。
- {# U( T" V Z. F" ?1 E9 b4 v [5 J: k; K. d/ H: g
4 ]4 n& S0 @+ T# U可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。$ x" m( ]6 ?! V8 ^ f
Q7 n% X5 ^! J1 x* NTCP服务器端和客户端的运行流程
/ `/ ?( H: X0 Y6 d
+ @+ G! z0 [+ {9 o) K$ F5 U. ]8 ]3 F" }5 F
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?% W( a0 }: I. |
) E( K! q" B3 w
6 i; x! _" W2 X3 `+ B! V6 P1.创建socket
. C( X% d2 D/ l4 X socket是一个结构体,被创建在内核中
( A7 X, X0 i! Q0 S sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
* a, l! G- b' B) @( G; h( W5 O U0 ~" G8 F
# q9 A( _5 W& k+ \3 i7 x0 d2.调用bind函数 J; M/ c( L0 ? j4 l/ L+ {
将socket和地址(包括ip、port)绑定。4 {/ K5 `) k1 @0 X d7 l& x( @, g
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
* ` B, k/ L0 U! D' @& L struct sockaddr_in myaddr; //地址结构体
( R V! I3 i( Y6 ?& @) P5 ~% z bind函数
" K' H$ ^3 G2 W2 E8 R, v4 i1 y bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
7 e# D3 Y/ I1 W% H+ H. x s o1 w% c5 K' I( Z& b9 V: U
# c) T7 h8 H* _" J
3.listen监听,将接收到的客户端连接放入队列
7 h* K% F6 U' q* B: \, _ listen(sockfd,8) //第二个参数是队列长度
; v4 B# e7 [2 c- a# y# j8 V8 _- F8 p& v
( ~, b- L; _; ^3 Y3 V
4.调用accept函数,从队列获取请求,返回socket描 述符
# u6 y3 _5 b _ 如果无请求,将会阻塞,直到获得连接7 G: w" o! v( y
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
8 g+ \4 q$ E1 M6 d" ?" b
8 `7 x5 z, ?% X7 }, o5 S# l c2 D4 f8 U3 P
5.调用read/write进行双向通信 d+ ?* q! b+ u4 C5 y1 |8 Y/ B, s; D
9 Y% }" }# O" S1 |0 V( f d9 G7 A5 \$ b
& A0 J6 j) e% ?8 \/ T6.关闭accept返回的socket
) h$ J# H& G4 z4 }7 a) g8 [7 |( e$ o close(scokfd);
0 e: j4 O& }1 T% M F9 u# m
; g+ c3 D1 ^% f' O4 O2 k u7 I5 j3 ~/ h" _" x
7 J1 T2 S/ \- u9 E: h* X
4 X/ ~& P% Y6 f
下面放出完整代码( M# O; a8 U% D$ A# A$ U9 } f/ q
( s( F- w+ r9 J/ u% ]
- /*服务器*/1 f. Z: ]) ?8 g8 s& F: f
- #include <stdio.h>
8 f% ?2 ~( E6 W( M# _3 Q, [ - #include <string.h>
/ T2 y5 n( T# R) }2 }9 X5 d# h - #include <stdlib.h>% w* T% D7 |7 o5 G
- #include <strings.h>
# Z+ t- q: |2 p( z5 V6 D! j - #include <sys/types.h>
6 g" d1 ~/ R; m9 k* n0 Y9 V2 c- V - #include <sys/socket.h>
: ?4 d6 @" o4 x" J( ~- @' V - #include <arpa/inet.h>' C- M8 R/ b! L$ i, c, U
- #include <netinet/in.h>+ q3 J# k: V! @5 y/ E+ |; w- D
- int main()9 c }0 h! L! w. ^, T
- {7 }9 M4 [' O- q _. H, k
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字$ o4 d& O' z- v
- if (sockfd < 0)
2 d+ q$ q. L# m* z - {
2 p' E! `- I& b+ A! {3 o - perror("socket");4 ~2 W/ r) }4 u9 E# g
- return -1;
5 S+ M- b0 K# C1 j, n9 B - } //创建失败的错误处理
5 c9 R. ]9 B; Z. J$ j' B t0 k$ E - printf("socket..............* r4 Q1 z. Y U; v3 o
- "); //成功则打印“socket。。。。”
4 U- F- S7 t9 @0 j! U - " S8 ?! j( w L. n3 A2 ?
- struct sockaddr_in myaddr; //创建“我的地址”结构体. a) O5 m6 d O/ F" K
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
- \. m) W5 v4 T9 g, M - myaddr.sin_family = AF_INET; //选择IPV4地址类型8 v9 I3 u" n5 P' J6 X3 D
- myaddr.sin_port = htons(8888); //选择端口号$ g( P9 _% K4 R4 G" Q+ i' ?
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址! K% @. O+ J$ z" Y
-
6 A& Y( ^0 g- z# W6 ^- q+ s% u) m - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字& |$ i5 m! r+ f# ^/ F$ P! F" u
- {; w; g+ l1 K0 i, ?! q# B8 O. h
- perror("bind");
$ k. h" c4 j7 S- e7 X! D - return -1;5 n/ _, e: W/ b2 w% ?6 q4 K
- }1 H5 U! W+ ^2 h6 V1 J( I. S
- printf("bind..........
. B5 h/ i( }+ B5 a9 Z1 l - ");
9 ?* t; V" u" S5 F- ]$ B -
( ]. D' n5 G) `5 \. U a - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听# {4 Y7 j' G& M: n( k7 v
- {3 J1 S1 r4 r; w8 I6 J* v
- perror("listen");
8 R$ z" n& y7 [ - return -1;
0 S) u u! \6 _5 M! w; c - }
8 I0 ^" D! G$ z) z3 \ - printf("listen............
6 D( e% Q4 x( H6 X& L+ d) b - ");. U- M5 L1 L; A' A# J& Y
- 5 ^3 q, G* y2 n: I+ o8 o
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求5 Z& q: L2 Z, o, P6 k
- if (connfd < 0). x. R" ?( j/ Y4 |
- {. C2 w/ B7 v7 U' i, x1 L
- perror("accept");
- t' X6 i- s5 `# E - return -1;
}. v }- \2 S - }( l6 Y# ?# N$ P' ]) m7 R0 o; e* Y
- printf("accept..............4 j" _- V0 o8 ?4 y. F: R0 @4 s4 s, q
- ");1 \) A, ~( {" d+ N; J/ M
- char buf[100];//定义一个数组用来存储接收到的数据
4 j3 ~7 ]. o1 x - int ret;$ W/ j1 \" l# k/ p. T- Z4 q6 v& f4 D
- while (1)
5 A# |! O1 @1 k- i8 L6 H3 e - {
* Y3 H4 G4 A+ I3 u - memset(buf, 0, sizeof(buf));( ]6 s# H* C- {- P- M! C" P( q
- ret = read(connfd, buf, sizeof(buf));
& {5 k2 c& d6 m - if (0 > ret)
2 p6 G0 | W+ [3 w/ X - {$ ^" h' H" x! v* {# O
- perror("read");+ y9 g4 `, G$ w5 ^6 D
- break;
1 q4 T; {: `# p/ P4 A- C" m - }//执行while循环读取数据,当 e3 e& ?+ Q( N) F
- else if (0 == ret)6 R# Q% U% W: s
- {7 u! j. b4 ]5 g5 v
- printf("write close!
9 r/ T4 G3 U9 K# u5 g8 I, c$ f/ H - ");8 Y" |; |+ I! E8 j& Y3 l
- break;
; U: _. @$ g7 S( K+ f - }2 Z- k. I9 f2 |/ H2 L. p. ?
- printf("recv: ");
& W8 R7 ?* R2 G4 v# |& L2 n - fputs(buf, stdout);//打印接收到的数据' V6 r; x) {# B1 o/ }" y5 h. M
- }
* u7 R |. D3 ~ - close(sockfd);//关闭套接字
) r! y5 a4 j9 y) K0 |& g, P1 j; D - close(connfd);//断开连接. T# {) t; Y4 Q) ^
- return 0;
( @5 i, H+ x. h- Z - }
复制代码
& h$ F9 e# X( D+ S- g5 k& f# ~- W1 q3 I4 }& D N& w0 j
- /*客户端*/(具体功能和服务器一样,所以不再加注释)6 Q% Z* a! ]% }
- #include <stdio.h>
+ S, k& A- e6 c- C" y& C0 f - #include <string.h>
4 Z# f: w3 P, u2 H/ H; q - #include <stdlib.h>/ A6 r, X2 S6 l
- #include <strings.h>
5 I1 Q4 f3 \, Y0 B! p/ i d - #include <sys/types.h>9 ]$ y8 f" R4 p7 e5 f) [5 A
- #include <sys/socket.h>
2 W3 {% v/ t$ f& _8 F( s1 D - #include <netinet/in.h>7 j9 {5 M' D4 t! `/ }/ N# p
- #include <arpa/inet.h>
, ~/ @" o8 I: Y; F+ K \+ O; \ - int main(): x( V& u6 C S; v: {: v
- {
2 D1 ]9 D; C; O! o. R - int sockfd;. C- e8 o6 \+ v' K( n9 k
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
) k: G1 ]7 _' R7 d% r6 F! j - {
1 Q0 ^0 _. e% f P* g - perror("socket");$ G- V; z- [# ~$ R/ P3 z. i3 s
- return -1;$ E! J7 j6 W9 L7 \* T# T, y& p
- }
) i; ]- k1 d1 Z# s- _& ] - printf("socket...........! K# _& i H, w$ L$ l
- ");+ U) C4 E5 u; ~8 e& n2 f4 g/ L4 A
- 2 B6 V( S# U2 ]2 ^( U. {; c F% A
- struct sockaddr_in srv_addr;
9 B5 w0 T" J7 u$ R6 G+ a( i. N - memset(&srv_addr, 0, sizeof(srv_addr));! ]8 t; g+ f3 y$ b; N8 |
- srv_addr.sin_family = AF_INET; i) M( j* u# z/ x' W2 }( O
- srv_addr.sin_port = htons(8888);8 M7 U. Y" B2 v6 [; e! V6 ^ X8 J
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
' p6 }( E4 B6 E( `* C - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
1 p# @7 [5 h, E9 a- x, S/ j5 s% G - {
# A2 ~2 m+ v3 X. }- t8 G: y- m - perror("connect");) L' f- f5 L! I& w2 }/ q
- return -1; //exit //pthread_exit
# Q9 C3 M6 n) J - }8 B' R: V- A) q0 _8 _
- printf("connect..............- _* _# ~( `' u
- ");" q. w8 }& \% f* G! |6 v( w, d* s
- char buf[100];& P9 M' h8 B7 U& ~; _8 U
- int ret;
7 }( S7 ]! N$ U- X8 E3 \ - while (1)! X1 S$ V0 j/ L( n: [
- {
% Y. L6 q1 Z4 e6 g" B4 L - printf("send: ");
5 D7 n. l @! p - fgets(buf, sizeof(buf), stdin);
, S% A# B# T8 R - ret = write(sockfd, buf, sizeof(buf));
4 x3 ` k7 Z( {2 B2 { - if (ret < 0)
, _) H: E0 f9 N - {1 r3 y# x* l. ^1 w( I# L% A
- perror("write");
& H$ k# R$ {/ s+ l - break;0 h3 i0 }4 o: }( q
- }1 B r3 S j9 N# S- A0 ]# S/ l0 |
- if (strncmp(buf, "quit", 4) == 0)' t; a" W3 k% s/ c/ p/ i
- break;
" }1 V% X0 k* M* `+ z5 s; | - }6 P3 v) W4 I2 I9 }
- close(sockfd);" d/ E7 M( Y r2 q ]: P* O6 ?( y
- return 0;
( m8 d1 f- w! m6 @8 z# l! x - }
复制代码 2 O O9 ^" E! |) @
+ J% U0 Z* `7 O
|
|