管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
0 a- H& m) m1 T" \8 y3 A! N1 m- g6 M. R
" p) d1 o. d5 J/ V- Y5 N
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。7 h& q1 {' o& z! o6 j
) i5 C4 G2 O4 k. \/ \/ r
1 `5 {- S0 y" m/ o1 ?TCP协议
% F r0 Z$ f* w$ w' D" s2 X0 r8 bTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。5 w0 P& T. l" z
* d9 e% l7 t/ ]; k S& Q
6 {3 O8 h6 u+ ]% G
关键词:三次握手,可靠,基于字节流。
* c u$ q" U' `2 B3 P a2 r6 O+ c8 c& A! z9 `, A0 t/ N
, I+ {# E7 }/ H( R6 U* c% h9 n4 i
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
( _- a# V9 F2 B2 h. X
$ }9 O- l6 C2 d2 Z
TCP服务器端和客户端的运行流程4 u( z. a6 Y" P+ S
) |5 Y' k: M" u( g- X2 q' T' E
3 Q# h3 L4 x! [% {- W. j8 L
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
+ Z/ Y! _$ ]7 L% K4 }- p* f Y5 ?
) k, e8 v; R- f V: k) G7 B9 R) J1 M. t3 p- v
1.创建socket
; r/ D7 ~& P8 e1 q2 l% T3 G9 `0 y socket是一个结构体,被创建在内核中
( D: e" d: H% ~) t# M9 q- f sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议3 F& o" ~& X8 s( ?) D! E$ @- i- d
( i: U6 h" u5 o* E+ \1 Q$ t
4 {8 C K. X! I& n+ J" ?
2.调用bind函数! i/ {5 {8 A5 ~8 g8 d- l4 \
将socket和地址(包括ip、port)绑定。7 s$ ~+ u, y/ _
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
- P' r5 T; q8 \2 W/ e struct sockaddr_in myaddr; //地址结构体
9 u& [6 W3 W; [4 i7 h8 C: v# L- d bind函数2 s" z( a4 o5 S* C# ~4 {
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
% ]4 m# S0 A1 \8 f- B
& n8 ^2 U T+ [, ^9 _$ W0 f4 q* c' b' b
3.listen监听,将接收到的客户端连接放入队列
9 C3 P& W0 C6 B3 u5 \( ?) \ listen(sockfd,8) //第二个参数是队列长度
" k& R& N$ T3 x5 e1 [6 V
- e% F/ U s& w4 D2 I+ n) ?, T2 a8 M( I6 c( o! S! r
4.调用accept函数,从队列获取请求,返回socket描 述符1 U/ x5 E: o1 H9 I
如果无请求,将会阻塞,直到获得连接* L# M c/ v' a
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数, D2 r1 K& m3 T/ s! q
% X6 S5 r% D9 S/ t) @7 L$ E. \; l2 w3 F* A8 l& L" S0 e" y
5.调用read/write进行双向通信
" J3 Q4 j2 ~: F* ~' J
9 _: e" [7 t4 {9 z" G8 Q% E5 W9 K( a) Z5 D3 h* ?$ j- p3 @, Q
6.关闭accept返回的socket7 r! f0 X* b! m5 u$ R# d0 l( i
close(scokfd);# o9 z- P2 R4 p: ?+ M- _5 A3 q/ U
/ ]( Z$ R9 c& v# S
2 M" U$ Z( g# g5 b' G
3 q. c* L! V0 X9 w2 I! ~0 D/ Y' a( P/ }) h$ l/ W
下面放出完整代码, v6 `4 ]! ]: J' q4 |, F* R6 X7 W
, ?. C. l) X+ X0 [( E- /*服务器*/
0 c# N$ n) l! V j - #include <stdio.h>
* I/ }7 h1 L% u" N - #include <string.h>
P& s5 a2 e% j7 G( v. D - #include <stdlib.h>
. e, n& o# l, Y9 g7 X h - #include <strings.h>
- q5 W- r% l4 y- u( _! ?! }% ? - #include <sys/types.h>
% V% B( R" r% d" F' b! g - #include <sys/socket.h>
1 I& z6 V* O; ?0 A% f8 g/ Z5 m; ] - #include <arpa/inet.h>
; b" s( L6 l/ T7 x - #include <netinet/in.h>& y s2 z$ d) t1 f7 q' d+ d! Y
- int main()! J8 X7 E. O/ X7 N, h
- {
: S3 T6 z5 E5 ^% V) D - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
0 |, y# L- V: K9 I% N6 x8 o+ ~' I - if (sockfd < 0)
- D2 Y* `( L9 g8 H! Q& R - {/ M% q/ s5 v* b! \/ R7 r; s
- perror("socket");) ~' @+ c9 a+ D% `3 I" m7 \
- return -1;; _! D9 } v2 s7 Q8 t
- } //创建失败的错误处理
- R( _3 Z! m) B - printf("socket..............$ p$ F, O+ g" p, T$ O3 J( m
- "); //成功则打印“socket。。。。”
5 ?0 j, B8 Y( ~5 l0 g$ R# D6 m+ F - + K% i: d# _$ F* V9 r
- struct sockaddr_in myaddr; //创建“我的地址”结构体. V, g7 [7 l! i; U6 \
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
( w1 k: r6 d! j* t/ N& q1 }' } - myaddr.sin_family = AF_INET; //选择IPV4地址类型0 f& ^ D2 d8 R4 J0 ^
- myaddr.sin_port = htons(8888); //选择端口号
3 K' p+ ^$ {7 T5 ^* M4 i - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址7 p, F, ^0 a2 M3 ~$ n
-
# m2 V# @( d7 [) A3 N - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
2 P8 X2 v* o4 I% G7 @( k) k - {6 y; ~) v% l/ {2 m, L0 ?
- perror("bind");. p! G6 ^* }5 |. p+ x
- return -1;
( j( z* y H& ~& t* O( M: l - }
( }$ \$ `3 K$ d" x' e% V: Z - printf("bind..........8 u) d! U& f5 Q, S' `
- ");
2 ~7 e! ]. A+ `5 T; L- K -
% ^' I# {5 z$ H& b5 o. b9 s - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听7 Z4 T1 M% b2 l) d
- {
* a& N5 O0 T$ `' K" C5 U2 n# \ - perror("listen");
5 b. F$ U* z3 L0 a! D- f# Y6 S! k, j$ j - return -1;) G* A1 j3 _& W( ]# |, Q
- }
8 W0 S, C6 Y) d - printf("listen............5 Y# b" s ~, W' T
- ");
, D5 x; N3 }) m2 H7 I* u% P4 ^ - s/ k9 @2 {) J8 j$ F
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求% }" i) L: h W! y# P* l& Q
- if (connfd < 0)& |3 ` L# `2 I# q7 ]8 L% x6 g" H
- {2 E0 h5 Y* |" h6 y! w
- perror("accept");( ?" ?+ S) |' K l/ w' g6 a' [
- return -1;+ C" F. T C5 W+ c1 e( X: q' ~ h
- }6 t9 H2 J) c e+ d& f( z
- printf("accept..............
' c: L7 ?8 h8 @; A+ w8 D - ");! x6 z1 y+ ]% ?1 m
- char buf[100];//定义一个数组用来存储接收到的数据$ B; h$ x8 M5 I. o; b4 k$ {2 W
- int ret;+ O2 Z; x; L5 @/ h# d0 L
- while (1)% X4 j8 q/ H$ h9 t
- {
" a9 L) A9 |/ j g) X; S; s - memset(buf, 0, sizeof(buf));- _* R4 M! a6 }- n2 k
- ret = read(connfd, buf, sizeof(buf));7 W/ W5 m/ |) c( t a) G
- if (0 > ret)4 i7 {8 C% l7 Y
- {" R/ E1 R- S, @( J, b# F
- perror("read");
' }5 S; P. a/ s" K N- l - break;1 ^) E* x1 J4 I, p8 v
- }//执行while循环读取数据,当( t5 Z+ A; `7 D1 y! E5 N
- else if (0 == ret)
" c7 h! o$ s e - {2 q# L E& J# V% j# [9 |
- printf("write close!
5 C4 w6 }, u4 H1 w& L* ?/ ` - ");
' p' P1 v" @) H. d$ y U% W - break;
/ @+ i8 @2 z# u7 u6 y - }( m5 C" Y3 {2 N- H4 V ?# w# \$ k+ M
- printf("recv: ");8 v' y$ b2 f! ^
- fputs(buf, stdout);//打印接收到的数据
& k S- ^; A. N5 J - }: o/ q; \3 o' { [2 E( \; Y
- close(sockfd);//关闭套接字
: J4 q5 M+ s" U - close(connfd);//断开连接
0 D% @. C8 Q- |& @" P1 a - return 0;+ K4 j3 N8 j- U, \( y4 u
- }
复制代码 ! Z6 q0 C9 M! ?3 F- N+ l
5 U+ N" y; \; b- /*客户端*/(具体功能和服务器一样,所以不再加注释)
& ~2 T+ e$ {! M( m# n& K7 A( S9 G - #include <stdio.h>- H* Q6 Y* F5 C
- #include <string.h>
6 I( a1 M- j2 g+ x2 ^2 a6 C d2 A. m, @ - #include <stdlib.h>
% G/ E- Z2 P2 [ a. T - #include <strings.h>
5 h) C3 g7 u+ Y - #include <sys/types.h>7 X& y# M# l2 X0 g( G3 H
- #include <sys/socket.h>
9 Z' }" n1 p: k2 V* R. W2 x. L: X - #include <netinet/in.h>
- f4 m5 ?# A; I4 J0 b+ U - #include <arpa/inet.h>
! X& b6 A7 c. p- f8 O7 `$ z - int main(). x, P2 X0 Q' j1 b/ t
- {
0 r m' E1 A0 b' T4 E; s6 C - int sockfd;
4 v4 X; {; c; ` - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))" `& T! B; N' z' M# M
- {
5 i& ?& J) O0 o' S - perror("socket");
% l! {; p- S% Q; P. j - return -1;
8 v9 U8 q; e! b E8 l) b, U8 s - }9 @ q7 L4 B4 L3 S
- printf("socket...........# O: k: r5 ^8 I, q. }0 h
- ");
1 ^! i0 R8 E# M8 k: I! [ - * a: ]$ a: n2 L# d# W7 L% A
- struct sockaddr_in srv_addr;
0 Z4 H- b q% j$ s- q* o2 q - memset(&srv_addr, 0, sizeof(srv_addr));$ l3 p( o9 A3 n3 J
- srv_addr.sin_family = AF_INET;9 w. S$ W+ ~ H5 ^
- srv_addr.sin_port = htons(8888);
' C* m; P/ `6 D3 j - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
6 {) D' i" h% z; ]( W - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))1 l* |" y. D$ Q/ I
- {
- d+ F. b/ r9 Q, t - perror("connect");
' {1 x$ R' t. h3 P0 o2 j4 e - return -1; //exit //pthread_exit) n/ ]/ Y" P( g0 `1 `" ?3 t
- }
" ]% b: a9 }: R7 l f' K9 w& i - printf("connect..............) T* `1 L0 I1 \& C7 b
- ");
- ?* H/ I( u+ y7 L y' T2 E' G! @ - char buf[100];* L+ O# m: O1 _+ M: K, L
- int ret;
- X n2 @# S3 F' _5 ?0 [ - while (1)+ k4 ^8 a/ @! L3 E% N: C
- {* V; X' W$ {5 E
- printf("send: ");
) x, S. @' [5 B. \% I5 d - fgets(buf, sizeof(buf), stdin);! H& K! G. g# }; m
- ret = write(sockfd, buf, sizeof(buf));
4 N {* m+ a4 _6 R% U$ n& T - if (ret < 0)' z0 B3 |8 }5 d% A& i
- {
; C$ A) l3 Y. K+ M- E% [( j - perror("write");
0 ~4 d- U; {5 q, Y! B3 y2 q - break;, | Y2 x& [ m: F7 b& \
- }: J$ p" L$ W4 c
- if (strncmp(buf, "quit", 4) == 0)$ k1 l' @! o4 G" F( ^3 j0 K
- break;
_/ i) r. L. p - }- M% r6 K& z2 S, C0 ?4 D7 C+ Q
- close(sockfd);/ j4 r( c4 n* Z) @' l4 U
- return 0;2 X; J5 V: z$ I
- }
复制代码
7 F& q2 f( M! Y! \+ V. _2 Q# `0 ~ u7 R& N, Z/ s
|
|