管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。( N" L E& g) g# T! v' m7 }3 k8 F
+ y$ X8 J. g. ^3 p+ t1 X2 Y) C
) P4 }" x2 F+ L
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
. n% y5 M9 o) u: p, Z: A/ \' }
7 G3 U" h* S% c) y- g3 H+ ^; n+ h+ p; {# R" z0 Y
TCP协议; T, [/ U6 t' b4 `
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
4 J5 E; p) A. Y7 a! x5 p4 d
+ ? t* h; t5 a. y, _) P9 U; d9 v8 T* ~, v
关键词:三次握手,可靠,基于字节流。3 L* a; X3 {5 J! d i9 |
/ P# R l' ?2 m4 U h$ }' v
. Y! |% b$ O) J }可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。, `$ p D+ T4 R" R7 ~
! d& _# y7 r. t) ^
TCP服务器端和客户端的运行流程) @) R9 Y+ E3 v
& U7 x) P, V( R9 h% w e1 L+ `
* `- C/ |3 t" q+ u' o. w如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?! k' H/ w; O. A; T' o
: E! ~1 H* c* F$ l. D3 o
" r# i- o7 `9 M1.创建socket+ f: Z( f$ c" V e- M1 A% \
socket是一个结构体,被创建在内核中7 R: p3 R6 R2 V* _( A( Q
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议& q7 B3 T9 Q$ _# W
! q3 H3 j% Q, E( E0 R# w# v$ P6 T
2.调用bind函数) _% q- \% s; O! H$ X$ ?6 Y
将socket和地址(包括ip、port)绑定。
3 d4 V7 I% _& Q+ R9 ?3 W0 [& c 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序* ?' L/ v8 \; g
struct sockaddr_in myaddr; //地址结构体
" e- W0 y; I% E y6 x2 `, I' Y& ~- h bind函数* S$ V0 v' R9 ] ]" a$ A2 X* g
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))* d8 }' i6 l* w$ r" n% h. B+ o
0 G# D" i3 [0 e }( x( c* E2 j, ^9 q
3.listen监听,将接收到的客户端连接放入队列/ y$ w, O% k" a$ n! w- V
listen(sockfd,8) //第二个参数是队列长度
5 `$ n, U- I/ x( Y/ y. ~5 }* x. q+ Y9 M" P; L$ ?/ Q5 O
8 r) x3 e# I7 j9 R* @$ c
4.调用accept函数,从队列获取请求,返回socket描 述符 G6 ]( W0 N( w$ \2 V3 P
如果无请求,将会阻塞,直到获得连接
3 X; L- P: B% g4 w# N int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
- n z) {+ n6 x9 l3 H& I t, W; E0 X, d. ~1 a( g: v
% Y4 ?) z" W# I6 |6 y m- X6 d
5.调用read/write进行双向通信
# o9 Q9 x5 i7 x: K& ^
3 O- ?' b" K' H
2 z' P0 c# _& u* _, B) B' L. n6.关闭accept返回的socket* v2 R5 w6 s0 ^4 a: m* A
close(scokfd);8 }" Z" j& \) J
|9 ~3 O9 F- d. L2 m, n
) W) j7 k: y; Y- _ Q+ K. g* S$ _+ E: |: G1 w# U" n% m( {$ x; C% [' e
& x: |- l1 t8 a下面放出完整代码+ {+ p" t {5 X6 M; @6 U0 [& @
) [& g! a, x( u. L3 m( b- d
- /*服务器*/
6 h, q7 f+ H) @/ j- O - #include <stdio.h>/ B. F1 i# Y; x/ W( @
- #include <string.h>1 r$ t1 [8 \& k* x8 d
- #include <stdlib.h>
5 H& P% }+ E `4 W3 b) v - #include <strings.h>
1 \/ l. n. g1 o6 } - #include <sys/types.h>- G# ?, G5 c% ^/ A( @2 A% w
- #include <sys/socket.h>! z0 q, k! l* i/ G$ U2 Z! N; Z
- #include <arpa/inet.h>
" C. K" B" h; |9 e9 A& w U& T - #include <netinet/in.h>
" J$ ?5 a' e- W+ X4 | - int main()
% c# C7 O5 `- Y - {: y3 Y- G4 d8 b6 B1 `$ X: d6 O( W
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
/ F9 S! ` B) ~1 H% I - if (sockfd < 0)
; [! ~) V5 o# q5 Q. ]8 q, a- t8 s8 q9 d - {
7 }: D3 Z3 I5 u - perror("socket");- d" K" o3 C6 N9 j
- return -1;
) L y; g! N! R( {$ L' n! F% m; \# _ - } //创建失败的错误处理
0 c: h, q4 h8 F - printf("socket..............+ l C- [2 K. m4 n5 N
- "); //成功则打印“socket。。。。”
2 T% ~. `* H5 N! | - ( C7 l4 [: U1 P: s w
- struct sockaddr_in myaddr; //创建“我的地址”结构体% M- j& N0 y; o# ?0 j9 M0 i0 [* S
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
0 e+ y C5 p" o ]# |$ }8 [ - myaddr.sin_family = AF_INET; //选择IPV4地址类型. @) x( C2 D# c( I' o
- myaddr.sin_port = htons(8888); //选择端口号5 |3 H* ~) T2 o5 a* g2 {' w
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址$ ?" ^$ y, E4 W# L3 ^
-
- n6 @! ]# }" M/ p0 w e$ F - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字( f: }' E, _4 @) Y
- {' ~% t: w0 U. _# }
- perror("bind");
4 p* ?3 _7 u# _* H" { - return -1;
* B" h2 _+ ~: Z - }# Z8 K% C7 \5 i4 p! \$ T5 a0 ]
- printf("bind.........., U( V' n+ X: ?% |# F0 r% [9 V
- ");( w, Y7 [4 w, ~; [: y3 B3 b; i
- 2 K" g3 E$ a" b1 p) u7 B) l0 e
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听- B; [/ I" W/ K$ K
- {
+ A' C1 v) q2 V. v5 _ H6 i& T* ` - perror("listen");
/ p$ J7 M5 R/ [+ ]% L2 Q5 W - return -1;! @* n; T {/ q4 X2 k
- }
( r: P3 u( |+ O0 S4 F0 [4 o/ Q$ I - printf("listen............
h: @ v# R: r, L: ?! @ - ");5 A, r O; J5 V8 C4 U; V
-
5 ~8 m. N" E6 ~5 z* b1 [ - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求4 s, ]2 z& ?' B2 p: Y2 |6 }
- if (connfd < 0)' S* T) S5 l- i: @ ?, H, h9 J
- {
! W, n. B% O: D - perror("accept");5 ~; _, e0 ^- E( K. r. Q1 k: M
- return -1;
6 P a" ?: L+ R4 s! r- [ - }
$ [7 U! T" K) R& t* t* u5 a - printf("accept..............
) R4 v' H. n# Y- J. l - ");5 S6 m- `2 ^6 h5 L! F
- char buf[100];//定义一个数组用来存储接收到的数据
& c: q$ l8 o3 @# e+ k' [/ w' i( u - int ret;
% j( r* H7 r7 S$ C - while (1)
' [9 w1 i2 O6 i/ y - {" {9 a* [6 E; V h6 ]+ v8 K7 T
- memset(buf, 0, sizeof(buf));
7 l8 \( `% S. [ M9 O4 M. m# R0 p - ret = read(connfd, buf, sizeof(buf));2 o4 H' j. L1 o& n6 s; E( v
- if (0 > ret); K% f, Q% f& h0 M/ G% y6 _
- {
/ k* O8 V6 G+ I) @; }2 W' M - perror("read");7 B& z* |' ?& O# l, G# F4 s, N5 L% t
- break;
; W& ]( u: m, r - }//执行while循环读取数据,当
/ c# p1 n7 I. k: y6 \7 s - else if (0 == ret)
) {, ] X1 q6 Y. R# l; s! O - {
. S) U5 V: {4 J% q& K - printf("write close!
4 v9 P' S* l& S6 R( ^0 A - ");3 u# |, X% R" r, M3 d
- break;
- z) ^2 C }' w* _ Y4 s9 s* C - }# k8 ^* Q. K* W
- printf("recv: ");4 i! ^2 G/ v. s2 f: s8 u
- fputs(buf, stdout);//打印接收到的数据
* W: y/ Y, k" P0 Z: @ - }4 }8 t+ m& @2 q
- close(sockfd);//关闭套接字
$ ]0 K( B; J! q6 U( W5 c( T - close(connfd);//断开连接
+ R q/ |3 _/ N- n - return 0;
4 f- P5 l6 h. N' | - }
复制代码
. g( x0 C3 h! X5 Q5 e' M( f+ m( ~3 r8 I! v6 v4 p
- /*客户端*/(具体功能和服务器一样,所以不再加注释)/ L r* K" Y" u6 V
- #include <stdio.h>
0 C1 h9 g! Z X) I5 B- F/ ?1 Q, l - #include <string.h>
/ s5 y" s2 ^: R8 U V( b9 C1 G - #include <stdlib.h>
4 w N& \" v& F/ i) g# B( u - #include <strings.h>
0 z# V0 |# g6 q7 C8 n* a8 Q - #include <sys/types.h>& O9 t, |- g! }1 J- P& p% [
- #include <sys/socket.h>
. i1 M- a; L# ?; N - #include <netinet/in.h>/ J* \1 J. X0 s' P
- #include <arpa/inet.h>
2 J- o2 a8 U- s' S& w0 ] - int main()
9 J. H- W: D5 K) Q( }: l0 ^- t) O - {3 R( f1 ]( V+ H2 ^5 k
- int sockfd;! n( e9 z% y Z+ h) H6 Q
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
. t: G4 C7 J! ?9 r- t z n) w$ Q: Y( J2 A - {
5 o$ k8 [1 B7 W1 [: C4 V6 G8 k - perror("socket");
# \1 v/ J& r2 u+ }! r+ ~ - return -1;
; V4 `( I6 v m5 e; \7 k f. ^ - }8 O) h, }: G6 D7 e/ v* H8 G) i
- printf("socket...........- @1 g. [5 c& E& [; \
- ");. l* j. S' p8 q o' }2 a
-
/ m. ~& w n F" j& J+ T& K, Q - struct sockaddr_in srv_addr;0 k" x, j& G% c% }2 I2 e$ T
- memset(&srv_addr, 0, sizeof(srv_addr));! h _5 \+ H5 T5 L* J, u
- srv_addr.sin_family = AF_INET;+ f6 X3 y' U9 Q; X3 @) q
- srv_addr.sin_port = htons(8888);2 X. V* w' [8 Y. w* S
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");4 Y( g- ]* w D+ O" e+ U9 H
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
1 Z/ B, _* ]3 t2 i v3 R+ F - {
& n# k* H C* v/ D3 _' Y - perror("connect");
; `3 }. G _! l. G! V$ R - return -1; //exit //pthread_exit
: N& F/ F/ `& ^$ A3 ?8 `. s+ P9 g - }
& M( ]9 N6 C1 q6 \* r) o. i+ J+ h - printf("connect..............
2 v5 Z, ]+ n" o9 v+ {4 ? - ");( R" e+ C5 d% j A5 w
- char buf[100];
' C* n- n5 w/ }: @% m/ n: ]! t - int ret;
S0 k% o& `. y% t! X - while (1)
! b9 Q3 \6 ^. V+ a& I$ w* N - {! ^* W, b% _+ q
- printf("send: ");
: X6 I$ l% u# q' T% k$ E - fgets(buf, sizeof(buf), stdin);" n8 y' h8 H& ?2 K: }; Y9 J
- ret = write(sockfd, buf, sizeof(buf));
( r: S- l: j' q2 n& N. e0 | - if (ret < 0)/ i2 }& [9 u1 h( e
- {
, o+ a- [, a. f+ g - perror("write");9 @- K! W' ~! \$ L7 U5 O! `; f( k
- break;
0 t6 V2 t( q0 a! c - }9 u! O* ^( K! y4 p! Z$ y# L
- if (strncmp(buf, "quit", 4) == 0)
+ Q& s# h( ?# p3 F$ J4 n - break;3 L8 Y) |6 @ n L8 Y
- }; r& v6 N3 I# l
- close(sockfd);
4 |& }- }6 @! @% ~1 X - return 0;8 R7 }1 N( x, V Y8 W
- }
复制代码
5 p: c6 d _3 l7 _5 D5 k r( @4 }$ l7 m" y+ O" U% K7 S' _9 h0 K- P' C
|
|