管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。' [2 l1 m6 w d. E( Y( t
0 l: d0 k$ U7 n: T
8 q9 D0 g2 u, u, asocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。$ x; w$ `0 `6 ]1 _: L
2 e' s* w a$ Y
* D% J5 _# P3 C7 OTCP协议& ?( z3 {! G3 h9 h- ~; U) G
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
4 a+ M6 E# _# c" k& Q) a4 r6 D B4 w/ e9 r& a( L* w. F
, ^1 q" z# g: B
关键词:三次握手,可靠,基于字节流。3 x3 Y9 \' Z4 U! w* X
& @& D i7 f. Y" H
: w' i' o) v( n n. D: m7 S1 @2 u3 d可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
. [, m# W; f" C b+ {* `7 V
/ n3 o9 T$ H5 K( `9 H4 \
TCP服务器端和客户端的运行流程 M4 N4 y' q# r9 b" r Q
8 t. }$ D* m4 Z2 j! I+ }+ d) g
4 f, f' C: q7 z4 v1 E. n如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?, a8 q4 d% A0 e: ]
; h9 K' w* _, v6 ]. Q: F
B' x5 w* [! r2 h" q5 y1.创建socket+ L/ W$ d% K0 \: F! h; r
socket是一个结构体,被创建在内核中
' N0 k) a- ]7 ~9 k7 Y sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
. V1 z3 }6 M% }1 }7 K* n w: Y. k, j# ~- x4 g, A
, j2 r- ]: M% M- B: v, C
2.调用bind函数
' W2 V, A; q9 z/ f7 \1 ]% d! z 将socket和地址(包括ip、port)绑定。
" n; i! h( _( y1 U% j: \ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序$ e* o; A( i4 ~+ D7 T8 M' L
struct sockaddr_in myaddr; //地址结构体
* o4 h* v2 t7 ?1 L; r1 K' ] bind函数
6 j8 i9 i! _) C6 j# O0 X- I- b bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% N$ H+ r2 t$ F5 ~2 `9 ]' i& f
! G4 c" c( c$ N' |' C! \9 p/ u
( N: ~- m5 x1 L1 j
3.listen监听,将接收到的客户端连接放入队列
4 R, g! h0 A x, N$ R listen(sockfd,8) //第二个参数是队列长度/ \% H8 p$ ^, q* W- G% ^+ x
* }2 E4 m- _5 s& \& [9 x" Y# b. r" B( N& q3 X6 Q
4.调用accept函数,从队列获取请求,返回socket描 述符4 G! ]. j h8 w/ H
如果无请求,将会阻塞,直到获得连接) \& p9 @. G% u/ l
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
/ B! K3 D4 ? m* Y1 q; B8 o! r
6 y# H$ X9 G i1 A {
" e6 y* K# e7 u$ `5.调用read/write进行双向通信7 C; R4 x3 E. D, F) k! `/ p
, r$ M8 j. p9 X ]$ N4 {. z1 j1 E8 ]% k; D z0 {$ g1 H
6.关闭accept返回的socket- J; @$ X4 @' l3 d- Q3 q
close(scokfd);& D3 ]5 j# c: x: c! _- `
* d" [' c% P3 R5 _# a7 P# a/ o: W/ y
. K3 C4 s4 O f; F; @
4 C7 d/ ~4 S/ m. k5 K% A* F. o% \8 T
下面放出完整代码! _1 @6 K% B) d1 e5 m! _
) ~& F, N0 B$ c( {1 m X- /*服务器*/
B" R, \' w6 S' V+ K* Y6 ] - #include <stdio.h>" z# J# H: O1 w6 P( i
- #include <string.h>. f7 } K# i8 e9 _1 d8 t
- #include <stdlib.h>8 X8 Z) g* s7 B$ {& {, G
- #include <strings.h>
4 Z+ T' c6 i, p. M - #include <sys/types.h>
0 G" e9 T# P( E - #include <sys/socket.h>
% @7 i6 \$ H5 R$ k) w - #include <arpa/inet.h>4 n6 y: u/ A4 K# z+ E/ @
- #include <netinet/in.h>
" ?& M% e8 b% G% @! O - int main()6 H3 K* L4 q I, L/ L
- {3 F4 J9 U! a l& |/ `
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
' @1 }6 ?* ~) b( ~ - if (sockfd < 0)
% x* R& I4 ^ a5 q0 G) t - {
* E- }$ A+ j i% e: J9 d5 A - perror("socket");
! K5 D/ @0 C' L- G0 ^ - return -1;& y% N! z( c+ d0 y! L4 M+ a0 a
- } //创建失败的错误处理
6 D" A* T R- F% M# [ - printf("socket..............; m9 [" d% N1 q/ `6 b
- "); //成功则打印“socket。。。。” y1 y! `# a* o" h# K2 t
- " f: y) ?3 ?; z% g2 H
- struct sockaddr_in myaddr; //创建“我的地址”结构体+ K2 Z, |( J( m& s: G3 R) S
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
- d# d) g+ B9 q9 Y+ x5 T+ R: a - myaddr.sin_family = AF_INET; //选择IPV4地址类型
' `0 r. {$ \( E8 l2 y. `/ y - myaddr.sin_port = htons(8888); //选择端口号( H. ^" b8 e; \) C
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址- Y0 W# z, p. R1 S7 v. m2 m
-
6 O+ D' Z0 T6 x. K+ ]& s - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字3 t% E, Y: H) ] f4 K+ O
- {" P2 ?1 J$ V6 g+ a9 m* S
- perror("bind");0 x1 Z9 h! M4 Q% l3 L+ V
- return -1;
* p+ V( ^0 ~4 k0 `8 H* B - }, {% W) s% c% [3 M: ]6 o
- printf("bind..........
' i; O0 ]7 k/ A# H, m$ | - ");
! [# G [) U7 b8 Y- q; S -
) {) T& V3 R$ c$ ?9 C5 l% k# i4 R - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听' }! _0 N7 X* g( L
- {+ z$ l( z {* n ^ _% O! X4 L. [
- perror("listen");" f+ k- r( P6 {% n
- return -1;* S4 c _+ s5 e2 V+ g' U5 u
- }
# r( q& v, s6 O# o/ j - printf("listen............% _0 l# }! ?2 _7 g% S* t" W. \
- ");
" f( v b6 v% T4 }: Q - ( r( H) H. a3 d1 n% M* u- z
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
( ^8 w- t% J; u, l) E - if (connfd < 0)3 s$ |$ h( r5 t0 C1 r* o' k
- {
: l T4 m% j3 h- U2 H/ X3 Z - perror("accept");
( H. C' A& p$ c7 Y9 j/ B4 D - return -1;7 h1 p2 \5 |: [+ {
- }
9 _- u* i$ b* W. L5 v# i; u - printf("accept..............
6 U5 T) A) j/ A5 q' T: K9 f - ");8 L4 R* J0 z7 N) I: `3 @& J
- char buf[100];//定义一个数组用来存储接收到的数据, x; o( w% l" p1 S& z G
- int ret;8 V$ f4 i: @0 Q$ o0 Q# N
- while (1)- X) X- n3 s2 e5 m0 ]& q7 P x5 P% y Y
- {2 L% m: x" [4 Z! s* l5 B6 _
- memset(buf, 0, sizeof(buf));. C/ _2 f& a) ^' |+ N% d' b
- ret = read(connfd, buf, sizeof(buf));
4 ^# i0 ~" o8 q, n - if (0 > ret)
* u" o) l7 D8 q+ | b - {
$ G0 B$ W$ ]% i% w+ J - perror("read");
4 X- ^1 x& J8 D* n6 b - break;0 c3 X& N i3 J$ d% `' Y$ M
- }//执行while循环读取数据,当# i7 `+ R8 T* X" k. T$ p% n' M
- else if (0 == ret)% D. R, w+ D- ~4 K! i! u9 [
- {
/ w) X4 X1 l5 J( ^- s( h - printf("write close!! o9 E4 h2 B" s# }- V5 [
- "); y6 h0 o# @1 Q& v& y" C9 S
- break;2 e9 s2 A' y0 D1 C9 w
- }3 |& F( k) \+ k2 R) x
- printf("recv: ");; `4 [! e4 Z4 U# `* H
- fputs(buf, stdout);//打印接收到的数据
" o, X3 `$ y- @5 g - }6 m# c3 ^3 M: c
- close(sockfd);//关闭套接字
1 T) t9 C( J Q8 f2 ^0 I0 y - close(connfd);//断开连接" K8 m) m! F4 f; @
- return 0;6 I5 s2 O# `% G. I ?' t5 o
- }
复制代码
' K; f$ u* Y7 N4 ]3 s h* {6 x1 L' f# F
- /*客户端*/(具体功能和服务器一样,所以不再加注释): O' U' t# H5 U1 s) y! x: v
- #include <stdio.h>
! C: W9 f% z5 t# j2 z1 f. d - #include <string.h>
" |6 e. g7 m) I. w - #include <stdlib.h>! W. |! M H6 ~5 X( h
- #include <strings.h>
# [; d# G/ d) X - #include <sys/types.h>
9 e/ L* K. O0 o - #include <sys/socket.h>
0 T+ N0 p5 ^3 B k% r; _8 j m0 B9 T - #include <netinet/in.h>
P+ [# ]& q! x) s1 W - #include <arpa/inet.h>1 H7 Y* T, i- Q0 w9 X6 I# B1 z
- int main()
. r0 O$ j- @ ^% j0 j - {
8 C8 I1 f- B$ P' { - int sockfd;3 t: B3 L9 p$ _- @
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))! `, [& F2 w D8 f9 A3 `
- {! d) }1 k7 w) J. b9 s) [
- perror("socket");
8 l1 ]5 a3 O( Y/ x2 w q% o+ @ - return -1; R6 p& x3 w6 o. O; y. Q4 E
- }2 W* h* C$ D+ _( }; v
- printf("socket...........
) @- a/ Q% J2 y- G! q. W7 v/ k - ");
3 W( v$ d. Q* \6 O - 2 R) v5 O2 [& u2 S: J& a$ w
- struct sockaddr_in srv_addr;; V9 m' q) k: i1 O
- memset(&srv_addr, 0, sizeof(srv_addr));8 N$ K7 T% H0 ]1 Q
- srv_addr.sin_family = AF_INET;- k1 z# P4 o4 ^2 x0 a
- srv_addr.sin_port = htons(8888);' b( f. s* L$ o8 s+ u- c4 a
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");& t1 K' z4 z; o& X) E
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
) J- x. t) z0 ^2 E4 Y - {0 X/ W6 x& A% J2 ], a1 N+ P. m
- perror("connect");) I% V6 O' g0 c7 u, o" \4 d0 d
- return -1; //exit //pthread_exit
+ t+ `+ z. f7 T - }$ v2 H- b0 b, ]# I& R1 v" z
- printf("connect..............5 Z7 t/ A) E6 c8 ~) q4 n K3 \
- ");' p$ _( F9 L: \8 w& p: y. U
- char buf[100];5 W: [0 c1 Y6 z1 B% i$ P+ ^8 C: \
- int ret;/ m6 V8 D$ b2 ?0 q1 E6 h
- while (1)
; Y% u: f L1 b1 d; k - {
! R) Q( t$ m& s. u9 z- O - printf("send: ");" l2 a0 J# a1 @: }0 c+ Y4 x
- fgets(buf, sizeof(buf), stdin);
! o& W, y' R& m+ ~ - ret = write(sockfd, buf, sizeof(buf));
$ @5 o9 z2 z, @* B0 c5 u) H - if (ret < 0)
- U. e$ w7 X- I7 C+ ^6 F8 K - {8 F' s! C( ~" l
- perror("write");
, f) h* W4 o' B* e/ p) ] - break;
5 Z6 h5 w2 ]6 { - }3 M7 Y3 [, p6 H1 C) C
- if (strncmp(buf, "quit", 4) == 0); \ h1 D( T0 Y" v
- break;( g' v0 ~; B, Z! j# C0 K
- }
% @3 F+ J3 m; U2 g" A1 b - close(sockfd);
& K- e& _$ F" S) k - return 0;
6 g$ k0 p5 j2 f/ E - }
复制代码 4 c/ r1 Y o1 \. B* e
$ M# F+ r# y. y$ C1 q
|
|