管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
/ ^/ g7 z- |& Y& Q1 e
7 z4 F" ^4 f: g* S
, _; f0 ?" r) X* Esocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
( `' U; |3 N( t: n5 c; Z' e3 g7 J( U( `* ^4 m
% X" i6 ~) ? |* g% ?TCP协议7 r+ @; p) f+ n& z1 \: p9 S
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。& u( { F* p' {: g" c! F8 h
: F2 d, r0 w5 T* s$ G# K2 k
( m0 q2 Z: F' X( {$ A: H! j关键词:三次握手,可靠,基于字节流。
% y& E0 B; P$ w% t8 y& B3 M( |
! Q) ~( `' \7 ^" _' V) z5 f9 P) E) m/ F6 {/ t) m
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
& j, a( F; P3 n" D' |( A
. L* m4 p7 ~- m. xTCP服务器端和客户端的运行流程
2 N# x; @; ~! x2 C0 b( ]% X& W: B* w; b! o4 H
5 s. Y% P* s: _
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?5 w- B! I# R8 t; i8 _
5 }( L- {* T, \+ x1 K8 n# ^ P6 O# J8 t9 Q8 h7 x% s4 d$ d( M" b
1.创建socket' f% }( a2 G8 d. |3 w0 _: E
socket是一个结构体,被创建在内核中
# ^+ I5 O# [, G$ F) b sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议+ D. i: S2 Z; m2 s
! q* @2 O4 s# P3 S4 W
0 b N) Q2 h4 S0 S, O( |2.调用bind函数
k: i; M* ` x5 _ 将socket和地址(包括ip、port)绑定。
J, n. B& i. j. v3 Y: |4 P 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
3 g/ p$ d, h. R6 T5 o0 R1 @ struct sockaddr_in myaddr; //地址结构体
' j% p+ g! p% z D bind函数/ V" Q1 X5 H" s( z/ v
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))) f/ T# w/ `# J& F
}2 e$ N {8 ?5 K4 X+ G, ^. I {) B& D4 w' l# n
3.listen监听,将接收到的客户端连接放入队列: q' |& @& s! n
listen(sockfd,8) //第二个参数是队列长度! N! A* H- y9 k* J {1 h
7 [2 |, J. y/ D/ p. A! s
- z% s/ H1 ^8 u$ y; F! D4 Z4.调用accept函数,从队列获取请求,返回socket描 述符/ L6 ?5 j: Z/ S+ X( M
如果无请求,将会阻塞,直到获得连接
* w# k1 i; P' x g- v int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
! f0 V5 U5 ?* F$ d0 t$ F6 D" b. S8 d: h E! a# C
, v* f! _% E8 M7 G- `2 u
5.调用read/write进行双向通信5 s7 x1 P4 ^# d/ ]' n6 d N% R
* _7 w& F9 I& S% _2 g+ c7 |3 J# K. Q' C
6.关闭accept返回的socket
. a0 b. g6 x6 d6 w close(scokfd);
! E; b' E7 m( \ |. C$ `5 U
: ^" W5 Q5 t3 m X- Z8 ~% s! Y6 q5 l5 o, ?: W( H) y
# M4 ~, r6 E+ X+ L" x& X! z3 M, X3 c$ l- R9 u( O6 @
下面放出完整代码
) L7 B N1 S' R, {( V8 n! }* s: Y% o4 P$ E7 R
- /*服务器*/4 v( {3 z+ E% o. F" b% G- ]
- #include <stdio.h>6 L& r2 E/ A: A* b B0 P/ N
- #include <string.h>
% a- ~' G h) i' J n. Y - #include <stdlib.h>2 k! ]$ {$ u$ Y
- #include <strings.h>( Z# u' K" l8 l. n
- #include <sys/types.h>; P, }# J$ N4 j9 H3 Y2 @7 }
- #include <sys/socket.h>! M6 g8 n: }6 n- T) B$ T
- #include <arpa/inet.h>; R m5 Y! Q, x
- #include <netinet/in.h>6 e/ {& `7 @& X( r: T2 Y
- int main()7 f" {7 k8 W4 W5 N
- {
% x: ]7 G9 b* k# g/ R/ h - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
& _9 K; }5 k7 D1 N3 p# N- V - if (sockfd < 0)
6 e8 V2 E S" _: c! H# @ - {0 M4 a* ~0 _' y( b3 d0 M
- perror("socket");) f6 d9 M' K" n6 M1 t+ V8 F* s
- return -1;% ?' ]' Z: ?. l: [. S$ ]& {2 U
- } //创建失败的错误处理% U8 I: Z& a7 v$ W
- printf("socket..............
$ ^6 y& z$ K) H {7 j" S - "); //成功则打印“socket。。。。”; {9 c3 P& Y* o2 U" _$ F3 Y' c
- 4 Q& A: F0 R C$ s
- struct sockaddr_in myaddr; //创建“我的地址”结构体+ A5 g2 t0 J. {
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)3 b! q9 o$ ~1 \' \8 R4 ]# J
- myaddr.sin_family = AF_INET; //选择IPV4地址类型. b4 d) ~7 m8 Q* ^ V' c2 E
- myaddr.sin_port = htons(8888); //选择端口号/ \6 M$ O$ M1 G1 w
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
/ W y- s- _0 z. `; d+ b - - m1 N9 }3 k+ j
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
8 t4 j, Z% n& B9 a - {6 X4 x& x% R, U3 [" Q( L8 z7 n
- perror("bind");
" c. O u0 y9 M - return -1;
5 T/ ], _% B4 {! \& @2 H: K! C - }3 F, K+ e3 \( P6 x. n
- printf("bind..........
; F2 ?9 p1 \, f - ");* o5 p+ J Y2 r0 A0 `$ O2 }6 c) f
-
; a/ q4 a) e- Z' F - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听+ B- I7 R0 f/ r3 l9 X0 S: z
- {
* S3 L5 N/ w3 B7 y6 J* V( b% [ - perror("listen");
; ]9 O; x1 L5 H; K( l8 H - return -1;
) L: V, s9 i$ J7 J - }
) s+ }- X% J0 c$ S9 D. O! b - printf("listen............* J& y9 S4 i; p2 c/ o; Q$ i/ g% `
- ");: y0 v, A( J) {' W1 ?7 T
-
& v0 s- |1 @9 U& z# s+ k- b- d0 R' _ - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
5 P0 X" f' m0 a& U1 U2 ~ - if (connfd < 0)
! D9 Z4 f8 F# z) N. l$ q \6 }3 T - {$ R# \1 a6 Z9 ]; W6 c1 U
- perror("accept");
+ S5 j) k! B9 h2 G: K - return -1;5 V; B; @: s3 r R( q; ~% w! i
- }
- W* {5 W: w( \2 [. R+ v - printf("accept.............. p) B# L, m5 K2 l7 j7 `
- ");' k' K0 x: M, \& c% K+ S4 B. f
- char buf[100];//定义一个数组用来存储接收到的数据1 U, H/ |. p, f- {
- int ret;
% w! b* G6 `" b1 { - while (1)7 L2 X+ Q. d6 S+ k8 A- S3 b
- {; t1 \. l W% L" z/ R3 j8 v7 s
- memset(buf, 0, sizeof(buf));0 ]& u6 I/ X8 B( J5 ]" I6 u
- ret = read(connfd, buf, sizeof(buf));
; ]4 f& @5 R7 g' a+ q. Z' x - if (0 > ret)* B2 T" @& S0 a5 D A
- {
' _+ D6 i7 e$ ?$ X- x: V - perror("read");2 J% [9 k) j y9 `' e6 H/ z
- break;
$ g ~. ]" Y4 i; p9 `/ r6 ^; X - }//执行while循环读取数据,当
, ^: S7 [( B2 M6 ? - else if (0 == ret)1 v$ x$ k1 P/ @8 U9 }; a
- {
# }' J# R3 T2 t4 q - printf("write close!3 ^) p; h! b4 m0 ^9 d n! i3 A
- ");
! j% ]- S7 v% R8 r6 i8 N - break;
: [& z; c* A1 m - }
3 p6 e, `! P e* b# O- R - printf("recv: ");
- l, Y" C+ j Z, ]! s - fputs(buf, stdout);//打印接收到的数据" s! }1 `6 Q+ R/ F* O
- }
: H( l+ c @1 r i8 \0 [+ C - close(sockfd);//关闭套接字4 P. ~; w9 p. ]0 E6 f
- close(connfd);//断开连接
8 \+ `) c: ^& w* F" A - return 0;
- w6 }- p1 _; D5 C/ ^& @7 N9 { - }
复制代码 / _' D* ^3 y9 z1 g
y* Q" H$ @# T$ s4 s
- /*客户端*/(具体功能和服务器一样,所以不再加注释)# p+ ^* p5 P5 u
- #include <stdio.h>
8 o B8 n- h- s( `% \8 ?1 S - #include <string.h>
# `: y1 X5 v& j& ]7 [ - #include <stdlib.h>9 K' Y4 M2 o. i1 J; D
- #include <strings.h>
5 i# I6 s) f; }" x3 ? - #include <sys/types.h>7 }+ i5 l3 ?8 b
- #include <sys/socket.h>
p! \1 c5 o- k1 ] - #include <netinet/in.h>
: M3 D M# V3 P: `4 D8 j - #include <arpa/inet.h>
5 T2 B9 n, l9 L5 I( F - int main()
/ K/ |4 B9 v. |+ U& I! r - {: b8 a/ o% a; T- m, t# `
- int sockfd;0 j! \0 Z1 Q* ]( M) T
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
? f" Y# B2 u' J& [ - {. P; S! d! F6 Z* _
- perror("socket");0 q2 V7 ?/ L$ a G) E; l3 F2 u* k
- return -1;
) s% P- d; O. o7 q8 L/ y; t2 t3 L: o - }
- E- l0 S7 `8 b1 M7 Z, c3 C - printf("socket...........& A/ t y' v1 q7 s
- ");! ]# F) w2 a+ Y8 \/ m4 [
- ( R; t l: G* C6 \8 P# D
- struct sockaddr_in srv_addr;. M( o3 {$ B7 v2 r& E7 }" o
- memset(&srv_addr, 0, sizeof(srv_addr));0 \3 ^8 Q2 P0 x# b8 @/ x4 _
- srv_addr.sin_family = AF_INET;. _, z; ]7 n2 T$ d3 R0 w, h5 J* Z
- srv_addr.sin_port = htons(8888);3 r5 p4 T. x: p
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
0 x1 j) M9 M( Y; W - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
: L- y. j5 I/ H/ d4 F - {4 Z# ?+ g2 q; ?: O; L2 b: C
- perror("connect");8 p2 a$ L r3 ? F
- return -1; //exit //pthread_exit
$ V+ [9 S) G+ C8 j- F# x6 C - }" f" L. D% I1 N7 G* x# ?* j
- printf("connect..............5 k- V; }" s: i
- ");
% P8 T1 Q8 z# O0 I8 s$ O) { - char buf[100];3 x# S1 y5 `- o9 v7 B+ o4 ~* d
- int ret;. X9 k6 e* d+ b. F8 T$ K
- while (1)- |- Q4 `$ ]. V6 Q, K
- {; D7 [3 ]. k+ V; |4 n8 f2 {
- printf("send: ");
# D: V* d- s. N$ D. l - fgets(buf, sizeof(buf), stdin);
% J$ ` r' w; t0 x% D3 @ - ret = write(sockfd, buf, sizeof(buf));* x2 W4 B+ { y" W7 V5 S
- if (ret < 0)
o U. {& X$ a S4 k - {. q* t% p& ]6 A2 b7 Q
- perror("write");
' U, w" P+ a+ Z7 J4 u+ o% ]2 x" R - break;% N4 G- V, V/ a! N( O
- }- W$ {5 b8 L# q5 g n
- if (strncmp(buf, "quit", 4) == 0)1 m5 O6 `& D# B" G S
- break;
- Z% _& k/ z7 D t4 t - }
* m" l* L, h' e9 n1 L! v - close(sockfd);
; t5 e! [( D% `# r - return 0;, ~# r$ M, U! q7 v ~5 h# `9 I, R
- }
复制代码 # k. Y8 f% e V: r2 ^
; x1 F( m: u m' m8 H( M
|
|