管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。5 R# \1 T! X) J v! J u! [
/ D a3 T; S6 @: e: m; t9 Q5 a1 @
# g5 R0 w& r% X, \1 K7 F: dsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
2 K; b$ {- l, C/ s$ v; `- ~/ _# @1 A3 F6 H' ^/ C# T2 W
- ]2 s1 M2 |, ]) v0 D1 ^7 S
TCP协议
6 P1 C# B) E" D+ _' x: S) Y5 |TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
. {) x3 w" p: Y; c
8 b$ m7 ^. t3 E, f) `. V
7 A- X% P, k2 k, _关键词:三次握手,可靠,基于字节流。; R4 n5 v/ r7 F7 i6 T1 h. U- J
8 x! J; w' Q* u" G
# J6 G2 F% l6 g' `可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。3 U* e: T0 A8 {8 M/ W/ v
7 e8 g& h+ C7 `& a/ t) f( X
TCP服务器端和客户端的运行流程1 ]* ]1 s3 p% c$ n: Y( @
3 @0 L: S& V7 S& q7 K0 a
! R7 l) W6 h7 Y如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
! Z9 | w f5 e: F: s. F- T- j' Z9 ~. Y
" p6 x, g0 d+ T. k
1.创建socket9 [% x% {! T( h, Y
socket是一个结构体,被创建在内核中 r Y- N/ N6 [5 ?" R: b
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议% I4 _: n4 j' I/ r" q
2 g( w" I# e6 z. Z0 y A
& d0 {, ]$ f1 V& d* h& Z* t2 ?2.调用bind函数2 s6 H% g. |# ?7 ?5 y' i
将socket和地址(包括ip、port)绑定。& |! K+ J {+ R$ i0 ~) j
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
. O2 X2 e) `# M9 U3 z- s6 ]: x struct sockaddr_in myaddr; //地址结构体
% s a" f& ~ d# }6 ] bind函数
5 Z9 j* v0 |5 U& y8 }- i bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)), Z/ r' }/ L. @2 V
- a7 v* ~% s0 }* S8 { \$ ~
- f; C* G8 S( Z5 t& R- i3.listen监听,将接收到的客户端连接放入队列
& v C( Z5 y# @ listen(sockfd,8) //第二个参数是队列长度# F2 f- k( h) f9 o8 B
5 m$ h. r, e$ P: u- n$ f
# w, Q c2 K; n" s% ]: C( \5 k
4.调用accept函数,从队列获取请求,返回socket描 述符+ a% j' f6 w. j7 o" h
如果无请求,将会阻塞,直到获得连接 t( V; g. `+ f) |6 R) y9 y# o
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数5 k% k Z7 D/ t! k, a! S5 m
; e/ K6 x0 f5 `; ?: J$ g
1 M- D7 k0 t3 K# O \5.调用read/write进行双向通信
8 Z1 c( k( U {/ ?. B Q( P2 {# p
. v2 S6 r2 z4 N3 D: ^+ a6.关闭accept返回的socket
3 W7 n, Y0 E& c8 B close(scokfd);% |2 S9 ^$ K+ j
. E+ L% ~4 C* Z# m" I- G4 C* w4 [
7 d, X2 E7 r3 U% k2 O: A
/ R* z3 I f& B! y' w$ h" y" a4 W- J4 Z2 K1 V, d- U% v b
下面放出完整代码
9 h/ Y0 u' k2 g5 ]! r0 E9 h# P, r$ u/ F
- /*服务器*/
" v: Y/ @8 K/ N; @ - #include <stdio.h>+ E* ]+ r7 h' v D
- #include <string.h>4 F/ d m3 c! N" }% Z
- #include <stdlib.h>- i3 p9 h. I* I. @
- #include <strings.h>+ U' ^* B L, Z/ {- H2 d u
- #include <sys/types.h>$ w0 D/ p) R1 P, B5 m% Q9 J, ~% b
- #include <sys/socket.h>
' e2 L- M: y4 s! K: Q3 z - #include <arpa/inet.h>1 S4 K f1 D! F. H, \% |, r# X R
- #include <netinet/in.h>; T$ D' E1 h! k5 ]* y
- int main()4 A# P7 R* a2 ^, J) W0 s7 ~8 c
- {- K- o- D! B/ G8 P5 {$ c& \9 Y/ @' v9 E
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
7 [5 ]6 g9 M5 \. H: e - if (sockfd < 0)
# r6 d' l4 u" E# S/ O* O5 Q - {0 Q; O, m1 x+ j5 ]$ H u9 i5 h
- perror("socket");2 g% \- Z. \+ ^/ m9 ~
- return -1;
' R2 A3 A9 C5 m' r2 c* w4 ~2 x+ | - } //创建失败的错误处理! Q9 T' X5 S' ^
- printf("socket..............
6 t, f3 ^, W# D' P, |0 S: e4 a - "); //成功则打印“socket。。。。”4 q }8 U9 p1 I* g! n
- 5 x e! @- ?: v! c+ ]
- struct sockaddr_in myaddr; //创建“我的地址”结构体# J# y2 Q4 x) v2 m; P
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
' p7 U5 n& x1 T% _% w - myaddr.sin_family = AF_INET; //选择IPV4地址类型
- h' _7 m! s4 N$ A: y - myaddr.sin_port = htons(8888); //选择端口号6 z* o. u: c! {4 U
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
5 _# L. z9 |+ D - ) F1 r& x0 ^/ g9 T; j- I
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字$ b+ u' C" }# c5 W4 S+ n1 Z6 i7 d
- {& p& z9 P7 [/ w6 ]5 \
- perror("bind"); X9 ]) p6 Y2 z, N8 ]2 p
- return -1;
/ O( V& w( e5 U) t - }# c+ n8 E9 a& r
- printf("bind..........
# J: E% ^, ~7 X$ \' X - ");& }3 p" z# h4 A, w( j& s+ k/ l3 e& n) `
-
6 [! a. E4 J6 ^8 w - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
- r# h# O' E8 K - {7 l! I2 N+ G, `+ x
- perror("listen");* S+ f3 z, `/ K3 D. d E
- return -1;
) B4 O; g- w. S! m, |% m) _5 X - }
- {, C: |- f2 C, M3 @$ R; _7 ~ - printf("listen............$ ]2 U: \0 s9 Y" D8 U
- ");
- f0 r7 F6 [' K* ?+ y; Q -
) A5 h _( X0 z" Z, Q0 @4 y$ ` - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求5 A; Z; D" _' a; N3 l" ]7 x
- if (connfd < 0)
M4 ?: u- b! U! V - {
8 E: g9 W- m/ K* H9 v - perror("accept");8 K3 n; l* |+ C, h$ y
- return -1;
* f9 U$ W5 e. B. W/ _ - }+ V' @- B; J7 J: s: ?" h
- printf("accept..............
) q$ @; F6 z) @+ r* K - ");: y5 I) h6 @# h
- char buf[100];//定义一个数组用来存储接收到的数据' T$ ` Y& W- G9 q$ f
- int ret;
' Z. U B3 V. f5 T6 l( P - while (1)4 t( g6 Z, p6 }; G; E
- {
! Q" ?4 I$ @( G9 ~& e - memset(buf, 0, sizeof(buf));' S; ?% S o& p& g! Y% M
- ret = read(connfd, buf, sizeof(buf));
9 e# w% g9 [! D r+ J- p+ m - if (0 > ret)& X5 I, z1 f. a* X# X
- {
( X- S, l" i% q0 j - perror("read");
4 B1 S' J- U9 Q) G; r$ ?$ ~ - break;3 o/ w$ j& ~; T3 d8 Q+ n& w" ?; \- ~, G
- }//执行while循环读取数据,当
3 a. A" w' P5 K) @2 Y; q - else if (0 == ret)
7 S- S6 C7 v1 f% R9 [ - {. W& f: V8 B D, ?8 \9 [. y& S$ E
- printf("write close!8 z& P+ X; {& Z# g( `
- ");
+ p& g' g# b' ~; [ - break;9 e! X$ g6 `% N2 Q1 y6 F' a% }
- }) o" d8 j9 V- _
- printf("recv: ");
9 y6 i; H M* C3 { - fputs(buf, stdout);//打印接收到的数据
3 R. d8 |; w: m6 p' Q - }1 p6 _# |- ^' W; H2 M! M+ ~- n' h
- close(sockfd);//关闭套接字
+ X; p9 p* O4 I* Q - close(connfd);//断开连接
. T( A6 i2 |% ^& m) g - return 0;0 p- D: a6 d+ t& N$ r' o# j8 ?6 z
- }
复制代码 4 l1 O, A2 s& g
! X! e' d9 W8 z0 U3 M+ B: a
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
4 h# F' Y- T$ e. X: z - #include <stdio.h>
! v5 w ^+ S% q8 z9 s. | - #include <string.h>; Y0 e! Z+ ~6 v) v3 x
- #include <stdlib.h># b3 H3 x' R% X9 I; T" V
- #include <strings.h>
, {5 i9 f! P1 _5 l8 F - #include <sys/types.h>% F! t5 P8 |; s) M0 u' z
- #include <sys/socket.h>
6 K' w6 p, h6 |0 z1 @ - #include <netinet/in.h>
0 O' a- r& B2 Y& S+ X - #include <arpa/inet.h>
# m/ w' h! {' n! O; c - int main()! ~" G0 p5 F# i; B$ i7 N
- {
& b. G3 N; }3 @3 a, n0 B - int sockfd;
! H* F( h8 k5 R2 o - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
' N! ^- |# A6 O5 C& i - {/ m) z0 q! I+ x3 `5 o
- perror("socket");
/ V4 r3 J- l2 \" ^& J2 b, ?( p - return -1;
" _7 W% z5 d4 f5 t- [" Z. b3 g - }
, Y% v8 a0 G; V- {7 s - printf("socket...........
( ^" a5 A5 E! K1 q8 Y* G( B7 x) V - ");1 p/ z* n- b* c" P% D
- 3 I$ M$ _. E0 l" a/ L
- struct sockaddr_in srv_addr;
& r2 r, q' b n6 n ^ - memset(&srv_addr, 0, sizeof(srv_addr));
+ J# x+ ]8 d P' A - srv_addr.sin_family = AF_INET;
& i2 K7 |( W6 B3 V- J$ K6 A - srv_addr.sin_port = htons(8888);
0 V5 U/ o! Q" J) Y - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");8 c5 q, h; h1 L; A
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
& h8 }% M ^" q" _/ S8 Y - {/ T" ]# }# J' y: g! p7 h; ^
- perror("connect");7 T6 g* g( I O$ i: }% H4 \/ F
- return -1; //exit //pthread_exit
, @$ g. D4 D8 C0 o) u2 ]1 G5 O% Y - }
9 @3 O$ f# A' B - printf("connect..............% Y1 x) F( O! s. f) I
- ");6 J, t2 G6 k3 i- `3 M3 O
- char buf[100];* ^# ~/ }& Z9 V. N
- int ret;
: g- x+ c* y4 g' @2 [9 z/ I - while (1)6 n) Y& z7 G* k/ {
- {
: [% ?2 C+ l6 h; z, y - printf("send: ");
! I6 V0 I( j3 v5 `# R$ x2 R& E - fgets(buf, sizeof(buf), stdin);# p/ q. U0 N& a/ a v8 W
- ret = write(sockfd, buf, sizeof(buf));
# R' _! ~# G4 _8 C" V! c2 n! G - if (ret < 0)7 p. Y$ A) n5 I/ C3 ~+ k
- {
! f5 u P3 P, a# B$ d/ z2 W - perror("write");- I9 P/ K X s% A% L5 s
- break;
9 k& |9 e4 _, a - }
V! t; N9 Q. n$ ?/ K( c7 w" P$ Q - if (strncmp(buf, "quit", 4) == 0)4 Q# C) H6 A* o/ P0 f" O; j- i
- break;# Y% v; ~5 U% @
- }
5 C4 G% n$ L% ~- q' O* n - close(sockfd);+ k7 C8 r& C/ q: p8 M
- return 0;- L& P5 Q' W k% b0 n; P- o0 U
- }
复制代码 ( Y% g' _& @! [; k! q
: m! a X' F t9 `# G2 z |
|