管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
+ H# C( Q4 F5 ]( [7 ^% f7 @ y! I: U" E' h
3 o+ @# |4 W+ t! e$ {8 S
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。( G* X. x( j9 d% l: v. `
, C' ]+ F2 ]# H$ ]' z/ L
. A% z- j+ }2 d0 H0 Z8 A/ ?7 N
TCP协议
) j! t! V1 K3 r p% H M1 O4 PTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。3 s0 \) t% k5 ^1 ]1 x: e
+ N) e% h4 R5 a% x+ W0 `2 X
( X+ y6 x3 k, L关键词:三次握手,可靠,基于字节流。
. q! h4 a) a0 X4 j
3 H2 M5 ~6 D3 x/ e: `9 `, ?. ~$ c2 [$ [% s9 ]' @) o7 i
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
3 v+ y R8 H/ i8 x1 q' t
4 X& r/ d5 c+ O
TCP服务器端和客户端的运行流程5 Q3 y t& a8 R- _
: r& @/ t8 ~* M& {8 k
# J* ^2 j+ f, A7 x0 d( Y如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
3 X: v9 \$ D# Z: o6 C
( A% m( X! Q+ V5 F
& E. k7 I/ p& U2 l6 A* A" n1.创建socket
- P9 m; o( p7 \% u& P socket是一个结构体,被创建在内核中
+ n6 T# B$ b9 ^2 H sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议4 o5 v" I* V O* A, _1 m1 M
; A9 z: D O$ B! C9 h' P* b) W% c' \
& L: A' [2 V- _" D( |
2.调用bind函数
" V$ j% O8 J" N+ A& `* i 将socket和地址(包括ip、port)绑定。8 b y0 s4 j o5 S0 t
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序, A/ a+ m# j: f6 j5 b) Z
struct sockaddr_in myaddr; //地址结构体
3 A6 {+ G* ^7 _ bind函数: D# m7 @# N4 v6 J
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))* L5 y) c% R2 {, T# `, q
2 [5 `8 }" W5 j' c
) a& g7 u5 O0 k* ]) u G) \3.listen监听,将接收到的客户端连接放入队列
$ S* A; J$ o5 E4 x* k listen(sockfd,8) //第二个参数是队列长度
8 L1 z: E; e) E9 a
" z1 U& Q8 `/ k {% z3 i9 J4 r
, x3 p% _. j0 R! f4 k6 g4.调用accept函数,从队列获取请求,返回socket描 述符% ]) A7 \, F/ S+ r& a+ o# h3 l: B
如果无请求,将会阻塞,直到获得连接; [* H0 W& a* K& f1 o% X. _
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数/ y; g, X% C0 K U# B
9 y8 c+ N, q) w0 e3 u
4 h7 K& [* R u& H5.调用read/write进行双向通信1 q$ m/ D* W+ F1 K* J' j$ |
! `4 l. W4 C0 |
- E0 u- p; @% m9 ]5 r6.关闭accept返回的socket
5 T# d( q) U7 F close(scokfd);
- \' t- o1 P8 w( a7 r% V$ A! I7 _0 p
) k3 ]4 l8 {' L: d# ]
5 E6 o+ d, G3 B. ^2 g$ a
- p8 j' s2 ^+ S+ }6 v下面放出完整代码8 P& R5 F, G- d- T) J# |# |
. Y$ Q2 ~: H6 u% L& H- /*服务器*/
: `: T" U! ]/ _ - #include <stdio.h>
7 n( X% E @) l4 q) L - #include <string.h>
$ q, f4 t2 B7 o- t4 z- a - #include <stdlib.h>
6 j- Y+ i! i0 I6 g* c - #include <strings.h>0 ^: }7 Y6 v- C, t4 V4 Z
- #include <sys/types.h>
2 c! ~( l/ I$ J6 E& I3 X! P9 c - #include <sys/socket.h>
- R' a2 f/ E. k" Z- B4 \! M0 W3 } - #include <arpa/inet.h>6 G8 N% w* s$ P+ X9 e
- #include <netinet/in.h>4 K5 A. I2 D0 U
- int main()
( P6 X+ W2 @, v# g+ S6 V - {# @( R2 z) t; B6 _' G
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
* F: o3 @, ]- ~* m - if (sockfd < 0)
# @' X( L9 \2 _* ]" o4 v5 c, ^( Z - {
# ~/ |# t2 q# A7 X" E' ?7 R3 e - perror("socket");& b5 Y6 ~4 m# s1 R! b+ h/ U
- return -1;! W% Q# g- y( ~1 t- V) b7 E0 ]' g
- } //创建失败的错误处理
3 |* \$ p* I! W5 q& `" B% t - printf("socket..............4 Z. [% k9 @) r" k# k
- "); //成功则打印“socket。。。。”( o% z/ m4 p, _8 o7 ~
-
* k1 _# \8 u! o# n! Z2 v i - struct sockaddr_in myaddr; //创建“我的地址”结构体9 V3 b# d4 @5 ?) s& |; N7 L
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
- f3 |! m& J3 S% L ] - myaddr.sin_family = AF_INET; //选择IPV4地址类型
' F" Y& w$ Y/ s7 \) O3 h$ s8 v - myaddr.sin_port = htons(8888); //选择端口号1 K% Z2 [6 E: ~. D9 n0 k8 J
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址8 i+ s- f% j& R$ r" T. `
- 3 b# t: j' _+ R# u* a' ^" @
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
( r( C% N" y( `! s& }0 X - {( r* E- U* d. g! ~" ], R* F
- perror("bind");( }3 {" u2 E9 ]" c8 A5 L3 ]
- return -1;
8 g! J) ]! t" U; v6 T" p. _+ E - }
. R# c3 }. N' ?, v - printf("bind..........3 N. R5 M' V( R% |# l( N/ I
- ");
/ c0 [) l# F% F - ( E0 s9 u ^( L9 a2 W# t" H
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
) u$ `: L5 w" u8 z - {$ d0 N/ j2 y( F/ b
- perror("listen");) e6 k% B# z5 p; [
- return -1;) i( I9 V/ {6 l
- }" G) E4 ^: s. c6 p0 W, m- e
- printf("listen............
- S8 A* Z4 ^. @8 d - ");
4 s2 l% S3 R8 A! o/ r6 k) z: L0 v( p B -
/ b- _( r @: T3 J4 `( W - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
0 K% d- `' f: v2 g. r* t" [ - if (connfd < 0)7 i2 _: @7 d" T) {+ T9 W+ H3 k
- {' A' H7 l. Y# F R/ \( P
- perror("accept");7 W9 p% B) u' ?8 o! \0 Z6 J
- return -1;
# w7 f+ F5 R) o! F - }5 z& _4 a, s3 s1 N$ ~
- printf("accept..............
" U- x" V; N7 c$ G. ^ - ");- D9 u" n$ E3 X3 L
- char buf[100];//定义一个数组用来存储接收到的数据* Y; Q. Q+ B, ^: C! c8 z
- int ret;
& \/ G$ [$ X* q3 e: H# o& r - while (1)% j% n/ |" w6 ^" T$ k! m- w) K
- {6 W1 h$ E) b$ r( y
- memset(buf, 0, sizeof(buf));+ q$ K8 M/ b: Z. ]. ^/ C R" C
- ret = read(connfd, buf, sizeof(buf));
7 n% \! B# N) O/ R4 n" @0 X4 C - if (0 > ret)
& }# ?; H$ I+ D9 _# P - {
8 b/ p9 E* v2 p; y0 C6 d; W - perror("read");
+ j5 v& O& O- {" A) c - break;
" B7 J/ Y) R' @ - }//执行while循环读取数据,当' {5 P1 h8 u% e7 i( P5 N' H/ f/ r
- else if (0 == ret)
+ }1 x# j: `" X0 R. I - {# C: q3 [: _" f1 v
- printf("write close!
5 U8 d. p2 u* B& y- v; { - ");
5 a2 W6 h0 K. S. N - break;
: N. b; [! c1 _; }2 o# z& A - }& Z3 G+ |! o, ~+ p7 a6 c0 ~
- printf("recv: ");7 r( m, y0 S. t! a0 k: s$ o* `
- fputs(buf, stdout);//打印接收到的数据
! _0 S! Z. }$ I% ]- v. K# L - }( I" }; \( \6 t! ?$ G* |
- close(sockfd);//关闭套接字; C5 j$ D# u9 |; D7 z+ o2 c
- close(connfd);//断开连接
; c! N, a+ p% y2 A5 y - return 0;
, d; u' |5 R6 @) ]: g2 u/ \3 d - }
复制代码 ' [7 `5 _3 n$ [5 H2 l% `3 Q
" q8 ~8 ~" c2 O& F
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
/ p+ C4 ]) x" u" B5 e) T# Q - #include <stdio.h>
" C4 l2 @! S `: q1 b, Q - #include <string.h>
: |2 A* W* @; B* p - #include <stdlib.h>1 n5 `; G9 v9 y0 z
- #include <strings.h>% z, ?8 z- i; m, A& Y
- #include <sys/types.h>5 t+ V0 V9 C. z- L6 ?3 z
- #include <sys/socket.h>/ d( d- Y' O& s. X0 K
- #include <netinet/in.h>
$ _3 d. Q: b! W - #include <arpa/inet.h>
2 D( m6 ^' v1 C, i4 i& h - int main()
5 x5 l s, z. Q. A+ b( } - {" _, ^3 O7 `; U1 ?6 Z: N
- int sockfd;
: `# Q& a4 k9 n+ y: y: w; Y - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))) t) p* u, h9 i. k1 W1 Y4 Z# v
- {. k: W2 j8 b0 L: s
- perror("socket");
N: b+ F1 {8 T1 e% y% T! Y - return -1; a) ]. W/ `* \2 Y6 C$ M N; N
- }
3 a) |6 ^3 c* l3 _, M0 @/ ~ - printf("socket...........* `$ }( D2 |9 k' t8 c7 s
- ");
D& ?/ u, f' z$ }/ K& U& v -
+ C1 o7 ~. j+ w8 W - struct sockaddr_in srv_addr;
& A: d) W1 `+ P" M6 y0 }8 K - memset(&srv_addr, 0, sizeof(srv_addr));
2 h/ G. ]+ Q! l9 g - srv_addr.sin_family = AF_INET;" e9 q g' v5 @7 \9 t
- srv_addr.sin_port = htons(8888);
2 E. T, r1 K3 }+ s - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
3 U8 I3 o+ S8 M$ ` - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))5 Z7 ~5 W6 ^! V( G4 d+ D$ E
- {. J5 U6 X' Z2 H; b$ @% z
- perror("connect");* ?2 A) D+ {/ g
- return -1; //exit //pthread_exit& j, m* Z4 P, I% Z7 H) s. b
- }. c. D- ^0 O; m; ?
- printf("connect..............) d1 |' `5 J$ G6 \
- ");" Q p7 q+ f9 a& C8 V5 h& t
- char buf[100];- d' k! g6 r* b; \# F5 |
- int ret;7 y7 a/ s$ n) V$ J/ P9 }7 }! y. l
- while (1)8 Z( k6 t0 ^4 P
- {, t) ]9 a. B. P; k, V" L) j |
- printf("send: ");$ X1 P+ o5 W `
- fgets(buf, sizeof(buf), stdin);/ U. w( S3 @! m. o- y$ H5 ]' b# W1 s
- ret = write(sockfd, buf, sizeof(buf));6 A' o5 C: {0 S; m" a: T9 V
- if (ret < 0)+ b( ^$ Y' P/ O
- {6 J0 ]# ^5 a3 l% s4 V/ [+ H
- perror("write");
- f; m" U8 U" O5 O0 P9 H - break;
# {& P' Q- _9 D; y* q - }
# b9 [# j3 b, j$ N - if (strncmp(buf, "quit", 4) == 0)
* A1 @& L9 g% A! x2 r$ E - break;
( O- D7 {" e# p1 ]- L c - }
/ p. l# a; R$ O+ F( C/ d - close(sockfd);
5 P) J* P# z" m" q6 T! C7 ?+ b - return 0;
0 Q6 x7 s4 S/ |4 y, u, [% j0 z - }
复制代码 , H% O, P" E" Z* u& R ?6 v/ r
7 r v- I' \, d# S, T+ \6 W |
|