管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。7 R3 n; i# q7 K7 `" P( s5 \
6 c P. R. F7 {: U
3 w f* L3 A( t0 d& t2 Z% y) qsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。8 @3 p1 f" l$ B: d
* p4 C$ C1 o( I6 k3 N J; N
6 k. Z: G2 |4 u$ S( _% tTCP协议
9 G' r+ X) p% e/ aTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。2 Z- b5 J8 a; P) s
. ^! I- H2 Y' v s& i B% c; H9 y/ X
关键词:三次握手,可靠,基于字节流。/ M+ Y A4 y3 V. p
: x5 V, V$ d, M* U) c6 r
( m" M: N" p; P; x4 m可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。& z% C; B' t2 d
# A/ C7 o8 Q- w* {. q0 h) XTCP服务器端和客户端的运行流程
- u6 e. W' W' m8 x( m% `' ]
9 {4 t6 O9 x2 V! l/ p: E- S. w
+ o9 Y6 R* k4 x* G9 h如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
0 R/ ]; M- V& X2 C3 S, V
g, k7 @% e a& c2 ]% P X
$ s0 Z" R3 m3 u: W% j1.创建socket& W' J5 ?1 q8 z" b
socket是一个结构体,被创建在内核中
$ o6 l4 ?5 l5 R9 y sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
& y2 @$ l2 v: K' r: i- K
/ n1 n% y4 j1 w1 W1 O
+ y' O0 W# M6 @: A2.调用bind函数( A( Q* T/ P+ z" c0 Z$ c
将socket和地址(包括ip、port)绑定。
+ ^% C( X# L) e8 T3 J 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序' G" Z! B% w6 F) T! ~5 Z, t* q$ X
struct sockaddr_in myaddr; //地址结构体
' M2 T4 R) V8 N1 x bind函数0 M: V" x; n9 _. Y
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))1 c Q6 Y u2 y8 y6 o! T1 m
; v) N+ |5 R, d% p0 h3 ~0 g
; T! U3 a" e! A/ l1 P: p3.listen监听,将接收到的客户端连接放入队列6 O) S% V7 I. J7 M; F1 \/ }
listen(sockfd,8) //第二个参数是队列长度2 K1 [ q7 w/ y
; d4 z6 ], B: I K: @0 G# m, z! a8 O' p1 t( N8 g/ [+ R
4.调用accept函数,从队列获取请求,返回socket描 述符
|5 Y$ k, N- \ 如果无请求,将会阻塞,直到获得连接3 ~8 U, {* D. g5 O5 a
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
S7 k- ]( F" z% w, L7 T: H2 y+ z" p# u+ _/ ?, E; d+ I6 b' }' K1 Z, N
9 z' S$ o a6 f* u6 ]6 J3 H6 b
5.调用read/write进行双向通信
* n9 ~. }* x4 q# o- [/ Y+ u
4 t, r, \3 C' {! E
* W9 t( M2 W5 {3 y6 t. p6.关闭accept返回的socket% s6 A7 O4 j+ V
close(scokfd);; n2 V m Q8 P3 H* r0 g0 b
2 z3 O# D2 Y. d; t5 Z' U# p* J
7 g8 ? p' @' s: v+ l, ?5 D
4 D7 c" M3 u9 M) c' t1 R
0 A2 m) r5 i! L7 d下面放出完整代码
* o; X/ J+ Q0 X2 @! G' C
+ ^4 ~9 m5 i$ w- /*服务器*/. S6 K8 [. z$ e/ A; j; C
- #include <stdio.h>& e& g! k& d5 c" _) w ]. {! L' M
- #include <string.h>
) {' q p, n$ T) D! N) x% b' v - #include <stdlib.h>, p/ h r9 S; N3 T% @! g
- #include <strings.h>9 F* r! O( p! J
- #include <sys/types.h>. T- I2 k' [* B7 d
- #include <sys/socket.h>
0 M1 {% F$ C5 w; v* U% K. h - #include <arpa/inet.h>
' w" W! O6 T- E+ Z! s - #include <netinet/in.h>
. u3 r4 E7 r6 ~: d - int main()
% f' U* C- k% A+ u3 a - {1 a* e. X' H1 S
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
3 e1 E' i9 H/ ^! r# f# D) _9 l - if (sockfd < 0)( X" _ s1 m+ V5 W; d2 K+ h
- {3 k6 Y# U1 {$ O9 l6 F$ I% H, B- e5 N
- perror("socket");- \+ s% M& n' J4 b
- return -1;
9 @7 M0 n; p! r/ _ - } //创建失败的错误处理( p: n( Z" y+ x# B. Y' H2 E; q
- printf("socket..............
7 [ u0 g0 A0 V8 {2 U - "); //成功则打印“socket。。。。”
( O* u3 S: C- X F3 o - 2 Y4 {/ y7 @4 A9 q( J
- struct sockaddr_in myaddr; //创建“我的地址”结构体- L) G' y1 m, R6 C/ t
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见) H4 {: ]' W. b! M; S
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
6 n& |# n6 a0 x3 c: t: q - myaddr.sin_port = htons(8888); //选择端口号
/ ?8 K% |6 B: Y8 @ W0 ]( w1 R6 H - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
" \2 f# H. |; V2 F7 F: L+ Y -
7 d% l2 f* r" D9 u4 ~, j; c% r) G - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字" N1 r6 f! u- h: o% l) d) t
- {
# f0 Y7 @) A" }; _ - perror("bind"); b* N2 a4 I- h; {; x- V* [9 Z6 u2 D
- return -1;
1 C, `% \7 j' }# W5 J3 m - }* A) N% u5 p" P$ l# E
- printf("bind..........
9 a6 {" p! D4 K1 | e+ d3 u - ");$ h- p. g8 ^# Y: v% J: g% [& K
- + f$ O3 G7 G# Y$ y+ t, Q/ h
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听. j8 r2 I4 y2 W$ y
- {5 ?, D$ i+ y+ i+ j1 D+ D
- perror("listen");
* p, k) o1 x" N( `" ?1 k - return -1;
3 P# @/ w+ j, s& R, y. H - }- O- Z# x7 F" A9 M' v {; a
- printf("listen............6 i( R7 c% _4 S% b6 D
- ");) }/ \3 A" V% W) ^) C
- * a4 @0 A- f/ h& F0 g, ~+ B1 ]
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求7 J m; D7 g3 Y' |! \+ T8 x
- if (connfd < 0)
- Y( K, W8 h$ x( j, D ~$ [# K6 C' C - {
4 S+ O- L- @! K% b9 { - perror("accept");- C2 V, j: F3 A( ?# x: I" e% r
- return -1;
3 g+ g- ?* Q9 L& f2 W$ L! ^. u& i0 F - }
3 r, y# C( u1 v. m. i* s - printf("accept..............2 P: O, G+ k. ]9 F# N) K+ G
- ");3 ~4 r$ p) d7 n' ?( l* v0 j. E8 c
- char buf[100];//定义一个数组用来存储接收到的数据
* _) a# ]# K9 m5 y f! p$ T - int ret;
: X- |: ?7 H% U; Z, j& e0 r - while (1)
9 R( S* U( f+ s - {
; G' l/ d _4 L+ F, t - memset(buf, 0, sizeof(buf));+ {2 H* b3 X& y- x
- ret = read(connfd, buf, sizeof(buf));9 T9 o; K% D' \7 M
- if (0 > ret), P$ R$ A8 f5 U7 C! E' {
- {
. N1 m: o/ y9 k% O i3 p - perror("read");! V8 D( K# q; v8 Z. `
- break;
4 {5 a" Z/ D0 \/ V - }//执行while循环读取数据,当- M; N2 I0 d6 s R! y
- else if (0 == ret)3 [8 M2 \0 b( H; ^5 H6 V: N
- {6 e, m0 E! M& S8 g5 e8 K! x
- printf("write close!2 B- Y) A' m! R# s( l# j; x: v
- ");: k r6 l" ?/ V# h% q
- break;
! r4 B4 ~ f5 K) K C. G - }
, C: j h9 l" a - printf("recv: ");
/ }- g$ m" a& P9 h# ]* h$ f - fputs(buf, stdout);//打印接收到的数据8 C4 y4 E- x8 Q8 l, f5 R# w
- }
1 `, u: _6 @. a" r - close(sockfd);//关闭套接字
1 z' u8 Z7 K. n! s0 V7 m0 s - close(connfd);//断开连接
! m6 V T5 o! H - return 0;
* ]+ v& I' u! N- z5 u6 d - }
复制代码
: o% |' @$ `7 s% t
* h2 W0 o7 R0 D- h- /*客户端*/(具体功能和服务器一样,所以不再加注释)
+ ~, k4 q- h2 T+ ]9 I- S. S - #include <stdio.h>
$ k+ ]6 c7 X& P& B& e" j" i: M - #include <string.h>2 X1 p, M6 s' x* W
- #include <stdlib.h>! U4 a( r7 p! O) x" O
- #include <strings.h>
6 ?- F& q, i- _( Z5 W2 e9 G& Y4 V - #include <sys/types.h>
7 B$ d# T5 [: b) J0 ^3 ]$ n - #include <sys/socket.h>
& j! [' Y9 o' N' t; ? - #include <netinet/in.h>3 s9 ]. d8 }. `0 `! ^4 L
- #include <arpa/inet.h>
! t4 f% f" i$ i3 U8 }/ }- I6 Z2 `4 ^ - int main()
; f, s ] ]! ]- \$ _" v6 \+ G - {
1 Q) V6 D- S/ t& p - int sockfd;
8 ?6 C( F3 W o/ `% E0 m - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
2 C% e; g: S; v5 k; s) [ - {
8 b `% ]' \* \% P, M* W1 Z: |! K - perror("socket");: W1 _& d0 r+ h! ~( q
- return -1;6 _* [) G0 @8 c [3 a, t/ K
- }
* r4 T4 B( X. g2 w" r) c - printf("socket...........4 J* g% J u3 Q$ J% c$ i% `0 P
- ");2 _7 P0 p. b5 y
- ' l' W1 }6 x/ I3 D: J. n
- struct sockaddr_in srv_addr;5 |1 b4 u* U# h( ~* R" x. t6 C( z
- memset(&srv_addr, 0, sizeof(srv_addr));
! x$ o7 ]6 D5 r6 l8 ?. s - srv_addr.sin_family = AF_INET;
) y+ `) @+ ^8 f$ d2 F - srv_addr.sin_port = htons(8888);# F3 O. Z5 X. A2 S0 A! d9 ]
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");: |, e4 I" R/ X# }5 X
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))9 t8 U7 T8 p' {* j
- {
' F4 k$ H$ w) W2 ?4 w' q - perror("connect");
n% W8 W' F- u0 ` - return -1; //exit //pthread_exit% r! @- J2 p9 S1 R+ V
- }4 I; a' n% j1 }5 e/ o. t0 B! A
- printf("connect..............
) H; Y5 X7 M9 B( ] - ");
% d2 e. |! Q3 g U. L - char buf[100];! Z3 c% g# Y% a7 W
- int ret;
. z$ k1 |3 d9 ]- V! B. _ - while (1)
' U& c" F; a8 q3 ]1 F+ ]8 D: ?3 A - {- V9 a' T4 V- l- Y( p8 U5 M- J
- printf("send: ");
; I1 D2 c! a# o$ ~( \ - fgets(buf, sizeof(buf), stdin);
: _! B7 K' b3 y - ret = write(sockfd, buf, sizeof(buf));: W% L8 z0 K- A5 s
- if (ret < 0)
; C0 k% J* U$ ?: Y$ Y - {+ w: U' C* r- Y* Z, V: V3 i) O
- perror("write");
; N/ L4 ]. ]0 `* W5 H4 b: \0 \* f - break;
" q4 g( |) V0 S6 A/ Z - }
8 A2 U1 Q2 r1 X) r7 ] - if (strncmp(buf, "quit", 4) == 0)
# w- ]% c+ u# A* u$ ?& q - break;
2 K( |2 m6 G7 S8 ?$ l L7 t6 T1 l - }
. r/ L3 Y0 Z1 y, b - close(sockfd);
3 u P6 u. c! V' R - return 0;5 [$ z$ D& _3 @; ` N; W5 Z" V/ D
- }
复制代码
6 X: U9 V; c$ c
- V* I8 Z" D/ b9 M* ]% D |
|