管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。& q' u! u- P% h
, C+ p& S% [2 J$ {4 r0 M; L; N9 i& p* @: Y' F7 ? Q
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。# ^/ s" n1 z$ f" h) ?4 \7 |' K
I5 I8 N* }5 U0 D3 J# d
1 }% _$ y3 g& y5 k# G1 K/ a+ ^TCP协议6 ]6 ]! `6 x& ?3 [
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。- V6 p! p0 u: I6 B
: p. J X6 M( y5 @! p- D, U2 s3 O2 |2 m- X. v3 ~
关键词:三次握手,可靠,基于字节流。8 V1 ` g4 u& p! |
! A0 e. Z; S0 {( i4 C% I
4 C# H6 v$ x9 R5 `; {
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。# x: x) p+ p( n, z z$ ^
& q* U% b. W; ?5 N$ ~
TCP服务器端和客户端的运行流程
9 O, m3 {5 K. f3 s* l6 E( g( _
" @" h: F1 `& j/ X% c; ?0 R' P# O/ h$ W8 p/ _& _1 q4 [' N
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
, e4 R* E% B% d8 U5 U- Y0 l% j3 Z; O; U- K7 h' y6 t
5 v0 I+ Z5 I; I; @3 c0 Q$ s1.创建socket4 P5 A: D# ]5 a( L! w, |
socket是一个结构体,被创建在内核中/ R: C- o5 [1 u3 O8 b f. O
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
6 j1 @- r2 {8 m
* d# z( g0 k+ x5 t2 F+ q1 s& g% Y. p1 i; j
2.调用bind函数
/ y: L8 f( ?' a4 T, c7 q 将socket和地址(包括ip、port)绑定。
2 V9 C' Y1 k; b2 c: [: v- x+ F 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
3 w# @- R. V$ t9 {; t V* k) [+ j, C struct sockaddr_in myaddr; //地址结构体3 u; B8 I# j8 \/ D. a% J
bind函数
% f* z+ J, n( J4 U1 Z bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))0 V+ I9 \7 z4 v [9 C7 v# O
! A; @5 F: @! U) ?6 c6 r
7 l2 _( y0 J m" `0 S3 ~' t& Q7 M3.listen监听,将接收到的客户端连接放入队列0 @- j% Z3 @4 d2 @2 ^" v
listen(sockfd,8) //第二个参数是队列长度
$ p) z, A: K: J$ R
: s4 t) `% O9 U7 |( x5 F7 \1 `0 O! q& F8 y8 U
4.调用accept函数,从队列获取请求,返回socket描 述符
5 Y; b4 S6 y* ^2 u+ _6 P9 t 如果无请求,将会阻塞,直到获得连接
. ~# V9 P: S" ]" p int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
8 d4 z) t L) f# f3 Y) f+ Q% F u+ v {% C
/ ~1 w6 O2 ]) p- w+ y, n
5.调用read/write进行双向通信- Y0 r ?% U, x2 m m
2 {( E% @$ r% l
- b7 I! {! e. E6.关闭accept返回的socket
9 F6 l2 {! `- K: K" Z close(scokfd);' o0 q4 K/ Z6 f- t1 {$ r7 J! N4 k$ S
$ u! d! M8 O! i3 U# s
& t& A6 a6 M. M% B& O6 \$ r
* }/ {1 L0 J+ d4 a' n z
( b7 {3 L2 \: y3 Y下面放出完整代码
, @$ C3 x; H* M/ k
; L# Y: h7 Q0 k- ~, y$ J) Y- /*服务器*/' [3 U: Y, [* G' O, p1 P U
- #include <stdio.h>4 I* A% L* Z8 i8 `/ q1 _
- #include <string.h>/ B: ^; o, Q7 Z( M
- #include <stdlib.h>
* w- N7 R& Q8 N: d8 _1 T - #include <strings.h>7 `7 w, p4 ^* H( }
- #include <sys/types.h>0 I) `6 `3 h* F$ r5 \6 n
- #include <sys/socket.h>0 D, h q& d$ g% K7 i3 ^: P! v. B( D
- #include <arpa/inet.h>
$ C8 B y( z2 | M) A! r5 e8 L - #include <netinet/in.h>
% R6 V1 C" C% b! Y/ m; b$ Z I - int main()
. {% r; d0 k W) j i9 {) l - { a3 t% F, _, s# u% H( s
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字1 |3 A7 Q7 [) w- o
- if (sockfd < 0)& u# A! w+ [ o% a% C! v1 Y" c
- {+ g! {9 f+ W2 h4 X# y2 H* D
- perror("socket");
: V( e q" P+ z( S1 R - return -1;, M# B/ }* T' K
- } //创建失败的错误处理- E3 r+ ?: C/ @- p/ v; c
- printf("socket..............$ X! P3 f- \) q- ?
- "); //成功则打印“socket。。。。”
; u G$ {& G' H j2 J- [2 V$ S; ` -
' p! t5 h* i7 u$ ?* w+ ` - struct sockaddr_in myaddr; //创建“我的地址”结构体
/ o9 a6 B% l9 N! f* g. r - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)5 Y9 g. d# t, n% i+ W6 d' J
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
4 y- k5 X& C' r) { l& U - myaddr.sin_port = htons(8888); //选择端口号
- y& `( B: w$ n: @$ w - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
$ {- G5 [7 c$ `- O, P -
! `: Z8 U' k' Q m4 l - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字" \" A$ `$ ~5 q1 O7 ] D; v% D
- {, q+ U: v2 `0 X, C% v6 t3 S
- perror("bind");2 @! Q% W. i* [- k- y0 E- H3 f
- return -1;# l( F, Z1 T* i/ R$ h
- }
* s+ B" P7 Q. x$ b) b- u/ r0 a! Q% [ - printf("bind..........
8 Q$ Y! y5 h/ O6 W- {, V3 d; c - ");; c# ?! J) d, u6 t, R) d6 v
-
+ G% M; y; i. ~: i7 \8 T" }/ a" T - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
' |3 \7 S6 P) V1 ] S - {
3 V S* o( `; O/ j" u, O" v - perror("listen");
* ^& k0 \( R4 w/ k. {3 M9 x+ f - return -1;
- E& K3 l6 \% [( Y1 O8 ?: @+ X - }
* {' @* Q' Y# h7 i - printf("listen............
, O2 n( W8 z5 q. a# ` - ");5 n v# P0 }1 _# j) E3 S
-
5 f- P+ M3 X4 X, H - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求5 w5 Z9 `9 T7 E2 G
- if (connfd < 0)
, }7 v" \4 |* S7 \; M o9 G# m% x2 m - {
8 S6 Y& T1 r) r4 u- B' l) r' X - perror("accept");5 v6 L, V. X5 u- z' ^- N7 m
- return -1;9 c- Z1 Z& }; Z* V- I
- }
+ t* c/ k3 {7 {* s _8 R6 u - printf("accept..............
1 v1 P2 }, o9 ]0 I, O/ y+ D - ");
' n( o* R: U1 n: v5 | - char buf[100];//定义一个数组用来存储接收到的数据
/ D W/ K& V; j" P1 a - int ret;" c4 O" a6 F6 l) p4 K5 \, c
- while (1)
& C: E) e0 l# |! C/ _ - {
# t. _6 J$ ^' Z# N% E4 z0 a2 G - memset(buf, 0, sizeof(buf));, X% ?3 I% B0 A3 i: ]& D' ~
- ret = read(connfd, buf, sizeof(buf));! W0 I; @: d0 ~! ]$ e% a2 V
- if (0 > ret): t+ E* ~1 Z `2 e6 H: \
- {! r( a) [! L# o* x4 D; l2 A
- perror("read");
! w0 D' B" X* l0 g7 t$ X - break;, @2 F# P/ f$ e# z) S( x0 k
- }//执行while循环读取数据,当 Q( U) v: E/ i0 v2 E
- else if (0 == ret)
9 U* l0 G& s* W2 p - {
& }; ^# o4 d8 c: M - printf("write close!, d' G5 F: z5 c$ U
- ");
8 r" q8 b, a k9 @ - break;
8 R8 B y1 u! |8 k - }8 n5 w2 g/ F* d5 O( w" s" Y" `: I. l( M
- printf("recv: ");
1 U n+ I9 {0 m6 \ - fputs(buf, stdout);//打印接收到的数据
; Y) L5 {; X) F - }
0 i& Y. c+ @8 S p& X2 J1 \. G$ [ - close(sockfd);//关闭套接字
x+ c& A: E& M& T$ W - close(connfd);//断开连接
: ]7 W- ]7 K9 Y6 ^4 C - return 0;
) F9 S) F3 n- A - }
复制代码 5 [6 ]. Y% x0 Y5 e
$ _+ N! r0 S( y
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
* w# g, k2 N0 x2 X - #include <stdio.h>7 E6 F" G# V& n+ F8 H
- #include <string.h>4 T; M2 u/ c- y% F& h$ x& M! p
- #include <stdlib.h>
! M4 y& [/ ]4 m% }# p: } - #include <strings.h>% n& t9 x' G2 b9 t1 i
- #include <sys/types.h>
0 i0 L# L/ N5 ~0 c; R+ I! X2 c# e+ j3 o - #include <sys/socket.h>/ b, m! F @! g- J
- #include <netinet/in.h>
# L8 w E$ n% Z - #include <arpa/inet.h>
; f3 W L! N" j4 J& [ - int main()6 S; i5 G! ]1 P7 E! p, w" O
- {, T% l1 G3 [5 O8 X# O6 V& x
- int sockfd;
; H# u6 f& L1 [ - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
- g1 [% V* t- E5 K) d2 h% f; @ - {, U8 ], V- h" S5 G
- perror("socket");
( ~2 F7 o6 Z, p9 S1 ` - return -1;! S, V6 V( \8 r2 s; `5 k# x
- }
3 O7 d3 E9 b2 r+ t: B2 S# L - printf("socket...........
' [/ i% l" ~; F# \8 X5 y - ");) \9 h, a* v; R% o8 s f
- 1 L7 y! O' ^8 W& R: m
- struct sockaddr_in srv_addr;
3 h/ X/ R7 d E o/ Z - memset(&srv_addr, 0, sizeof(srv_addr));6 C& z) t# W7 V3 a! U
- srv_addr.sin_family = AF_INET;
) }) u% L9 L* m! d8 L - srv_addr.sin_port = htons(8888);
- _$ j# l( D" M0 R$ E - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
3 I: D6 _; f$ ]5 A- D - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
F2 Y( L" w, M; K) O0 a - {, k3 ?% n/ k/ _& y; M4 \( G3 S* m
- perror("connect");& Q0 `% c. [4 N/ o2 d4 s' s- i
- return -1; //exit //pthread_exit& u8 L T2 D% p
- }
, _ i2 `. E0 X. V7 [6 ]: R1 O/ A; _ - printf("connect..............2 d E- K4 W6 Q1 {2 R
- ");+ R+ _5 F+ }9 }+ O
- char buf[100];
9 C3 ]/ M, X3 Z: l% Z6 g/ D/ @6 y - int ret;( L7 F, R% E4 L0 \" ]) M7 R4 R- z
- while (1)
2 H$ `9 A( h) u* B/ j R9 |+ q - {
0 v- J! U, ?0 s/ J( @. P - printf("send: ");
; M0 H( p L- b - fgets(buf, sizeof(buf), stdin);- Y3 [9 s% p0 \5 M: q
- ret = write(sockfd, buf, sizeof(buf));
; R) i$ {5 U( {4 [ - if (ret < 0)
/ j& e2 h) u: |7 Z% v; q - {: s- {- z' [) L! f- }9 t+ w$ h4 V- o. P
- perror("write");
/ U; s0 Y$ `% r7 d - break;# n$ d2 y0 w2 J0 G4 |) n
- } y* T1 k w/ i8 v# W& L% U
- if (strncmp(buf, "quit", 4) == 0)3 v4 B1 y3 v9 x w! J" F- R0 s
- break;7 ^9 @# z3 S5 L/ N/ Y" u
- }
. v- z, J0 {$ ], s3 W7 d. k - close(sockfd);2 Y4 M; B7 L; J6 ~- O
- return 0;2 w7 O- ?2 {* o6 e+ j: b* A
- }
复制代码
/ q2 G9 O: v0 ]% U/ r4 P6 f! _- J3 s% p: g0 ]9 P7 f
|
|