管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
% p: P6 f' E% |/ @( y# [
6 F- ? B0 H% p O
0 _6 \$ S3 j v8 N3 |8 q( B M, tsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
! I% V- B; r0 Q; j7 U4 t
- G# ?4 g+ k% E; X1 ~
. R0 Q* f U, a ^) LTCP协议
5 [0 {: f3 B. d5 Z2 w. z7 [TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。( v$ u& B) B" L1 G9 V) q
9 S/ {2 G( o' o% x. \( s, |1 C
9 E& C' @: k" S关键词:三次握手,可靠,基于字节流。7 D6 P( u& n" X
9 b% `$ {" p, A: M" Z; W0 o* C# Z- E4 [: n
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
, n! |8 n( f+ g/ Y' l/ V k
* \6 }% K) L( k' v
TCP服务器端和客户端的运行流程" e- o' I, a$ E7 k& @4 V, `5 r, h& g
I7 V5 a( I+ K+ Y9 _4 _ ]* A+ h" X8 C
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
6 b. ^- w A. }# m2 H
$ G2 V; u. y( U3 f/ Q# H$ W5 F3 _, r- {5 u6 ~) L4 {
1.创建socket$ |) Q" B/ T9 q) F+ s0 K2 E6 c
socket是一个结构体,被创建在内核中* J, C. e! p2 `. l0 R
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议3 C3 j- U9 N4 Y+ I. i
' Q7 \) _8 l* q- A# Q# d \: j' D
( Q/ F9 h+ F3 o1 s4 a+ X
2.调用bind函数
2 ]: B5 [- `' C5 R 将socket和地址(包括ip、port)绑定。, d* n; g7 N1 q0 B- ?. U
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序/ U- Z6 v6 r# H# N& T+ ^0 V
struct sockaddr_in myaddr; //地址结构体. _9 I$ W9 l" I7 P L$ h$ @
bind函数! ^- C5 g8 Z( V0 b7 K- l( e
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))6 l) Y0 }" i. ?, A& d. G; T
! D. x1 k, F' ^$ n% W& _4 |
' L; p, o2 w; t
3.listen监听,将接收到的客户端连接放入队列. r a: B* Y/ y1 W# Q
listen(sockfd,8) //第二个参数是队列长度+ x7 W4 k1 j) k1 v" G4 ^9 k5 U
; _; B3 `! H, ^+ U& b% v2 H2 g
0 ~1 S" R/ c1 S; |. J4.调用accept函数,从队列获取请求,返回socket描 述符5 g; d5 R3 |+ t# {
如果无请求,将会阻塞,直到获得连接' m' G J: }/ g: u/ l' c9 o( I
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
6 s# ]- @7 W. ]. L9 G5 A* E+ s2 F. f3 n; F; i( v9 c" ~
& @* B7 L% b; F! M8 H5.调用read/write进行双向通信; E' P& A$ f) o, d; s
* |. W6 C0 D5 h( d. b$ H: I
" p8 j3 n: B J- y- a$ `6.关闭accept返回的socket) ~, L0 U5 M' `7 C3 [3 ?5 p: s
close(scokfd);- [0 m: s, {) P
[; O. m& Z$ j ?4 D+ c$ ~! ?' N, U' n2 l! q' `1 `
1 ~7 o! t N/ W; A
: j% i8 H; w' j g- a2 F下面放出完整代码) ?* n6 F M0 D' t) X3 V( S& S
; `) P0 W. i( U9 Q7 J) L) [4 h6 V- /*服务器*/1 z; c. ]! F# N
- #include <stdio.h>
' ^' U' B* P+ M4 z- @ - #include <string.h>8 h; \- V' b3 o4 B
- #include <stdlib.h>9 r d7 E) A2 |% R" W( y
- #include <strings.h>
' b& v9 l$ I7 _% m - #include <sys/types.h>
* U, v, I7 l! f - #include <sys/socket.h>0 c9 C5 H* a8 q# M( Z9 s
- #include <arpa/inet.h>
. x% P8 Y/ l& l* F- `0 T - #include <netinet/in.h>! ?; r9 Z" o2 r$ K6 F; K: M9 M
- int main()
& ]1 j5 N. D/ c+ \3 I - {3 p$ ~/ B2 h2 e, ^
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字+ Q2 j# K6 q! M7 i, I1 D
- if (sockfd < 0); C5 ^5 W8 @% d$ p& x
- {
$ o5 C! ^5 g* ? - perror("socket");( M$ N, B2 ]$ P7 k9 Q9 N+ y' }
- return -1;
0 G$ f3 Z' S/ Z+ [/ V6 O; c5 V - } //创建失败的错误处理
; m( a7 y5 p& f. c3 L - printf("socket.............., n8 \( i; Y( r$ g. R
- "); //成功则打印“socket。。。。”
0 m( V* m1 d: K( \; `! F1 k - 6 B7 }8 X0 D, u* ~! `% ~, t" S9 _
- struct sockaddr_in myaddr; //创建“我的地址”结构体
# h( t, Y) a1 s0 V4 g! `/ k - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)( j* E+ O Z, @
- myaddr.sin_family = AF_INET; //选择IPV4地址类型* S) e% B# d7 q+ ? a2 Z
- myaddr.sin_port = htons(8888); //选择端口号
3 l! {' |+ q1 |; j+ j( h! R8 y$ ^+ C - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
: }8 q; C2 i c# L5 A* R" J, Y - $ }& [! M" w& x R
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字8 c& b0 S. |- I) k+ e6 Z; U2 T/ Z( E
- {, u/ `! b( a+ r3 E6 m' L
- perror("bind");
2 [, w& ~9 q+ ]6 b5 M - return -1;+ e- U3 p$ M. {+ h
- }
/ b" v6 \; k- a8 j" W, H2 H, ? - printf("bind..........
# d3 s: x$ z& m% n) N( @ - ");4 O, O0 a% \- f) g2 N! v
-
- h& d2 I! O& {2 j$ Q( | - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听9 f' I% \% A D$ u' s# O
- {
' d! T6 d0 ]6 G% z1 y/ E - perror("listen");/ E/ Q _7 T: l8 Z* @5 \- U
- return -1;4 |5 d5 P& ]+ F! W" V9 n
- }
% X a8 ]# ]; w- Z5 X \7 L - printf("listen............% ?% D- R/ o! Z# f9 l
- ");9 S$ g& F' Q9 ^2 b$ N. b4 L1 f
-
; [" N; w) r/ M' d+ s( k - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求' e- \& B4 P- q$ c1 e6 W- I
- if (connfd < 0) U: K& K$ O( S+ F' X
- {
9 ^- T8 C* u/ Z' |% l8 K - perror("accept");
; O. Y/ R4 {' }5 O - return -1;- |2 z% d. K/ t4 s; k: W8 d
- }) |4 P7 f% y1 B0 G6 }
- printf("accept..............
2 Z9 T1 d) m; i0 l" _ - ");" n3 B i2 U, q$ `
- char buf[100];//定义一个数组用来存储接收到的数据
" v$ E z$ s1 w% ~2 o - int ret;
3 ^8 X) p8 n3 { s4 y - while (1)
5 B$ k X- v9 @- a7 u/ f+ } - {9 M4 a _1 E" r8 g4 o
- memset(buf, 0, sizeof(buf));
& d& t6 N% a$ X5 R, O - ret = read(connfd, buf, sizeof(buf));
5 N1 _( i7 e# \& { h5 ?& J/ | - if (0 > ret)+ _: r. b1 b9 r* Q8 \
- {1 Z1 e& s. k# S# Y- }
- perror("read");
1 O( _4 i, k% x {" ~ - break;8 [, S" }6 }" L& w% p$ K
- }//执行while循环读取数据,当8 \: q8 i4 x3 k, F+ @. L
- else if (0 == ret), B. W0 v9 B7 `
- {1 K* z! {/ M& o x/ b* ?
- printf("write close!
) A; ]) n) {+ W - ");# m. S- {3 R% \, V- ~0 U
- break;$ o" O- T% q$ }
- }3 n7 V5 O' C- F" X, P( B4 Q
- printf("recv: ");; Z6 H( U" V* }
- fputs(buf, stdout);//打印接收到的数据
5 ]8 B% b$ ~0 L2 w6 z7 _) v - }
0 g$ r3 L0 j( c+ }& t - close(sockfd);//关闭套接字2 g1 R5 o. }5 N K
- close(connfd);//断开连接, a7 a6 V% y+ F- n) s* ~0 R8 K- ^$ K# ^
- return 0;
9 e- m. |" c4 @6 i5 C# w - }
复制代码
) a5 b& ]8 O' @- U, F6 z2 W) ]: |* i( I# U
- /*客户端*/(具体功能和服务器一样,所以不再加注释): P+ t% a5 v- P. M
- #include <stdio.h>1 [1 e$ t. B2 P# o( J
- #include <string.h>
1 f& U7 H F% w0 E - #include <stdlib.h> ~+ l) ?0 k5 E) z& @
- #include <strings.h>
0 t5 j; Z) g5 [& n0 w, o - #include <sys/types.h>
4 r4 d% U5 u: E# |3 ? q - #include <sys/socket.h>
$ G( W5 ^) Y! ? - #include <netinet/in.h>
; S* A3 ]. G6 }. r - #include <arpa/inet.h>
; W3 S2 f* r/ Z: b/ v6 [ S - int main()" b+ F4 N T9 L0 |: x
- {/ r$ b0 ]: j# ?5 P; J. Q
- int sockfd;
h! b9 P% A* F# Y( I - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
- D2 [6 ^+ J% x) N. w7 q$ [: G - {* {" A, e( v- x) _. H
- perror("socket");
! r% c+ E6 m: `' p$ b1 h - return -1; c3 l/ ^4 [6 l. r$ V! t+ D- |
- }! O7 A `; _, }. z1 [
- printf("socket.........../ L; _; R+ q- X* c5 \" z, ^
- ");
% I6 P! ]$ E: [- s0 {; V- [4 U7 b( g - ) u* s+ N& q+ G N. D' a6 t0 O! D
- struct sockaddr_in srv_addr;
4 O+ N1 S2 J! c) \9 X- i - memset(&srv_addr, 0, sizeof(srv_addr));1 V4 U) e# X+ E5 K+ Q1 C5 F( A
- srv_addr.sin_family = AF_INET;) I2 B* s q# _ { G1 w
- srv_addr.sin_port = htons(8888);. G& _, t" i2 U. A5 c+ ~7 ]2 _
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
3 c% r% m6 C3 o7 a" ^: Q - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
8 c. T' `. w) a4 ]1 k - {
4 `; p3 _* c, J' U - perror("connect");. t. R! ]7 F; f+ e1 ]- U
- return -1; //exit //pthread_exit
6 }% D6 T# w- s8 c - }
% o! a* L! p; r7 ?% S) Y+ | - printf("connect..............4 } r5 e7 \* B
- ");) Q/ E+ r- m; G4 g8 R- `
- char buf[100];: F/ i! a! v1 b0 Z) g
- int ret;7 C9 j$ [$ I- G1 _4 d
- while (1)1 D( T/ K; M% p! S
- {
% C% A6 ]- g5 }8 i/ h2 o, P1 i - printf("send: ");
$ d" \, X( D( H - fgets(buf, sizeof(buf), stdin);
6 S1 I( W! V' A - ret = write(sockfd, buf, sizeof(buf));
5 d5 s! u( t w6 T( V& t - if (ret < 0)
8 {0 F0 t- e! g* _8 u% k8 S. o4 z( M4 Q - {
" D" v3 i. q# U2 R - perror("write");
Q! R u8 ?) P7 h @& p; Q - break;5 k) t; \- E+ @9 W' ~6 {
- }9 Z t- y, e6 f# p, J3 O' U
- if (strncmp(buf, "quit", 4) == 0)- G, ~' V$ D! D3 s7 J
- break;
Z; S" W4 H4 @: Z9 p5 ?, e- U - }6 i" z* X' }4 Z* m3 ]% R' K' y
- close(sockfd);
; E0 r% u+ s$ } J- j* Z - return 0;. k `0 v9 ?! \) H9 t
- }
复制代码 6 ?$ S( K j" y1 Q3 H( j+ ^0 O
. L: P: g5 j$ ?, ^
|
|