管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
$ u( Y/ l3 l: a; c
: |% v- c& C3 p6 b+ F* j" o/ Q; s r9 t1 D/ E% I, N1 d" q6 j
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。2 v" D# p8 @/ \' D% g" e$ M
4 i, y6 ?0 X/ A$ ^2 p0 I+ {$ r( f8 l/ B8 z9 ~8 z5 S, u; y
TCP协议
0 I* I9 ~. c5 r: ]TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。" T3 U" l. `, M! Y
( ]2 I* t; `* p. P0 u
5 F5 ~; }$ u" x* ]) R& z关键词:三次握手,可靠,基于字节流。- M/ N2 y+ M& H* j/ g' x
/ h6 |- S% d# C* |$ m; T. h: l, V3 ?$ T) c7 Q$ ]
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
+ Z0 v. Z. W3 q
4 _9 L3 u* r' v3 a
TCP服务器端和客户端的运行流程
5 E( m* q3 @7 ^7 t7 G
7 J+ ^5 E9 f/ [
( i+ G( X4 c/ f* a; a% f如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?* D# `& v% h" O
( L( f) R1 e) E) K2 i
: g& P8 Q" O+ o! H; Q1.创建socket
) Q. d+ g" _ ]' M2 @ socket是一个结构体,被创建在内核中/ @4 [8 W% n# W. N; c
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议& Z& q2 y) }# n% M9 H. z
: [/ g3 ]) U2 T3 i+ y7 W: Q
w( B, h0 V0 G* [9 |$ g0 y
2.调用bind函数7 S0 ^, [& [! F# P5 O) ]
将socket和地址(包括ip、port)绑定。7 C- }' Q( l: Y S1 X
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序4 E3 E+ e6 I* I: Q- ^
struct sockaddr_in myaddr; //地址结构体
^& p8 R2 B5 u$ S bind函数3 C2 s8 B. T1 r0 K
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))4 [5 U" R8 |3 A" Z1 L. e5 p( t
# O" W7 [ o, P- _+ i3 N" H$ K: N0 Q& u) u3 r5 F" G
3.listen监听,将接收到的客户端连接放入队列
( {0 M/ W H- o: e8 M listen(sockfd,8) //第二个参数是队列长度% }2 B+ l2 S4 N! |0 P- i& d0 B
4 |* x' R. Y6 ~: t. L3 j
3 L, W M9 `1 F* C" ^. e4.调用accept函数,从队列获取请求,返回socket描 述符- [6 K, A( U1 ^
如果无请求,将会阻塞,直到获得连接
5 w8 [1 B0 d! [8 U int fd=accept(sockfd, NULL,NULL);//这边采用默认参数) F( A2 n6 R( w1 x7 x" _1 \
/ u3 o& o) t) O$ _1 Q# A6 l# g
1 H9 G+ `: Y5 F _7 @! ?
5.调用read/write进行双向通信' }! A" B, ?1 E6 s7 D! P; U* \% _9 ^
- U+ w3 M5 \+ O
8 m. c! {" I8 b$ N3 h" k6.关闭accept返回的socket" t( E. S! M- L+ x h
close(scokfd);
, L2 U8 k6 C* G8 Z$ H1 K
" M. ?" r# E" x( B! t: f
2 U3 E; d. R2 B% h' x
- I" l1 `/ }: B
0 ]9 P/ ?2 c3 F5 v6 }& j下面放出完整代码
+ u! N: B$ ?4 k# w% q( C
! c- y3 g! ^! C" L, A- /*服务器*/5 Y- ?! g& F+ K% \
- #include <stdio.h>
. N7 _* n/ `8 ^& P, ~# _ - #include <string.h>6 `! Q& J) h/ |, C) `
- #include <stdlib.h>- b" H; }4 }4 ]
- #include <strings.h># M2 @# F b2 {1 v. Q
- #include <sys/types.h>
1 P3 f* t% G( m7 T2 ^! J - #include <sys/socket.h>
$ C- N8 h5 \3 m9 H( Z1 Y - #include <arpa/inet.h>
& X" ^* W% f+ G! @, L# n2 W& U - #include <netinet/in.h>
# _ \2 H" t$ d0 d. ~2 w - int main()
4 c1 r/ V2 {1 |- F# u% L* G; x - {
" w/ [( | D6 R* `2 m1 N - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
3 b; p' I0 ]+ I2 v/ x - if (sockfd < 0)
9 `. w- V. [) z+ O) P* D - {5 V1 U w0 _9 x* L% l% a
- perror("socket");
% T1 Z1 b+ `6 a- L+ O$ T - return -1;4 W9 ~4 t) L( Z$ A* T
- } //创建失败的错误处理
' U+ F* Z# L5 N2 U6 n - printf("socket..............
3 R2 Z) r- s# ^8 T0 Q3 @ - "); //成功则打印“socket。。。。”
, z2 a5 ^5 x' n# ^3 c4 m - 1 v5 ~# z! a; k9 l, M
- struct sockaddr_in myaddr; //创建“我的地址”结构体
- c i6 @$ H N* R - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
z' n& p& f3 |% X) E - myaddr.sin_family = AF_INET; //选择IPV4地址类型6 A" N$ E" w2 z0 C; F. J. v
- myaddr.sin_port = htons(8888); //选择端口号
* b- A9 R8 E1 s% d) I$ d - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址* j& y, a+ m5 X' T/ T/ p- ^
- T; M- P3 e5 v6 t" ?- i
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
4 i1 q) B) ~' b+ Q2 e8 U- `0 L; h - {& H* I, ?; C, A) l
- perror("bind");& w: d' Q1 P) i* m) E
- return -1;
4 N+ V: E4 _( N5 A - }8 O/ r' x5 Z9 `; s5 a8 {# k* c. H
- printf("bind..........
0 e& g! T! ~' Y6 @' @; ] - ");
3 c6 g/ ^( k8 X& w2 |0 p' R -
6 H, s* P, Y3 k! B8 ^ - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听. S9 |3 e- X. T1 a
- {
; t6 r* ]7 O% s7 z/ e* M2 P3 K0 s/ W - perror("listen");
. I. L; o: D* K7 { - return -1;
t6 ~7 V! L" M6 L% y - }
V8 d8 S5 A4 H% D G" t9 b' | - printf("listen............
G8 ]0 g4 Y2 H: _# o* R" w7 h - ");
3 m1 w b6 L5 G. \3 A( w I) v5 S - 9 _2 n$ I9 a; z
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求2 ^8 X$ S. v& X6 W1 p3 }
- if (connfd < 0)7 _" \2 m- ~3 u* S
- {
0 R0 C% l, m# ~+ I+ m1 u - perror("accept");6 j1 ^, g- P+ k. N
- return -1;
9 Z0 L# i, E) G- i, J& X7 s - }
* r0 X3 a& Z5 X6 p3 B E7 ^2 Y' ] - printf("accept..............: w# e1 }0 j. j6 X, I! d0 {
- ");# T2 X9 w# j2 K( Y
- char buf[100];//定义一个数组用来存储接收到的数据" L6 ?. i; a* B- K! g
- int ret;8 j: g& ~5 }9 z: Y+ ~$ Q2 }$ H% l
- while (1), ? ]9 c7 G- A8 n. P, M
- {; {/ C' b* y' H/ {' R
- memset(buf, 0, sizeof(buf));
8 p' O9 y- C: M% G/ V3 R1 ^ - ret = read(connfd, buf, sizeof(buf));
K/ O! A2 O' m2 ~ - if (0 > ret)* y+ g2 Q4 D1 }/ X: Q, G1 t0 s
- {
, T) K# w: @# x' Z. Z8 ], l - perror("read");
% ^- H( l( _; w. b+ u - break;
9 T; P4 ^ q! e6 O - }//执行while循环读取数据,当
, F; _! I* _4 G% u - else if (0 == ret)
# f e# }% a0 L8 y$ o, w; D - {
U3 g+ [( A, p6 Z - printf("write close!
# b1 Z6 t* o B7 W: s/ x+ E - ");! j% I( t0 p; `1 d6 @
- break;; _, k r; r, i& `& w& }
- }5 E! J' W6 \" @% E* V% J, K" S
- printf("recv: ");" t! k2 u0 F) F! A5 O
- fputs(buf, stdout);//打印接收到的数据' \5 p9 T' e$ S# M$ V' M" h. f2 K
- }
0 c( t$ m l, |" f/ S - close(sockfd);//关闭套接字 U4 K% q1 s) m# R) j
- close(connfd);//断开连接2 `0 p% p. j& ^1 K
- return 0;
9 A& o( ?, W9 O* Q0 [: T, J5 S - }
复制代码 - j0 |) J' i0 v. E) I+ _# j' d
* {8 N7 Y g1 v5 R! R& q, F
- /*客户端*/(具体功能和服务器一样,所以不再加注释)( d" z2 O! P, q1 q: z! Q) ~
- #include <stdio.h>3 ]6 \" N# T0 }/ v; j5 v
- #include <string.h>: Z; c: O/ `( C9 I1 H. ?
- #include <stdlib.h>
& x: D" A; u" Z9 V - #include <strings.h>
8 [+ |5 T- d* b+ }# O6 }, a - #include <sys/types.h>
' Z! ?% ]) N# x( P - #include <sys/socket.h>
# h% T" @) v' @8 E5 o - #include <netinet/in.h>
$ Z3 D1 z! I8 Q! C! ? - #include <arpa/inet.h>
. r, O [9 f( P$ M - int main()
# L. ?& y# g) A/ e - {: [7 P" h6 F$ ]/ G6 l
- int sockfd;; U( s( V- N' k A4 T$ E
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))5 S( w4 c) N x
- {
. }2 _' {* t; P - perror("socket");
6 p5 ~3 J ~4 \. D; | - return -1;
- }& j7 K. c y( e( g - }1 @/ f+ U' V( M/ B
- printf("socket..........." R. K' i+ n* {2 D
- ");, y. k9 b( H; D7 s: F: G# d
- 5 _% J3 t2 z: A
- struct sockaddr_in srv_addr;, A, H4 R5 K) T$ ^% s# k
- memset(&srv_addr, 0, sizeof(srv_addr));: n- {+ f( X( ~6 @0 @/ S2 r
- srv_addr.sin_family = AF_INET;
2 i4 T! ^" `. p a3 W - srv_addr.sin_port = htons(8888);
7 |4 n& A2 D* y0 ? - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
4 H3 z2 F. F- v0 n1 `$ V - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))); H; A6 u/ }+ G$ L) u
- {, u, Y5 m- k2 A; r, M* d
- perror("connect");
3 n1 t3 H* _! }7 M( ] - return -1; //exit //pthread_exit
?$ j9 z: I$ B - }- ~& X e8 i/ z5 G J) N
- printf("connect..............2 ~& r0 }! Z# z
- ");8 L2 A- q( ~" J* L
- char buf[100];
2 l+ b3 l% ~ g5 y7 E' N - int ret;1 U# H3 c- k0 h& Z9 F
- while (1)) n% I( p& q. ]1 W2 c; ~7 C7 N
- {
) x5 w+ g9 p( D9 U5 @ - printf("send: ");
4 C1 Q p Q _. I* }' r - fgets(buf, sizeof(buf), stdin);
3 D1 v: i1 ~ N7 V5 p - ret = write(sockfd, buf, sizeof(buf));$ n0 ]' A8 i s9 U
- if (ret < 0)4 h6 m) J: {& j+ T
- {$ A# [1 c2 Y& |( s( ~* x( W0 K
- perror("write");
) Z# R8 b6 u% { - break;, {: }, Q" k' Y- k
- } W. Q. e: S, P- y5 j4 e* I0 i
- if (strncmp(buf, "quit", 4) == 0)9 D: N, t& v! x$ {* k, w
- break;
4 c( J( ?0 U/ |, ` - }
) R% p9 ]5 \4 Z+ U8 y3 W, W& w - close(sockfd);
k, M, `) i3 V' U7 q - return 0;) a) ]5 Y/ e8 v7 a
- }
复制代码
' y( C9 [* z# r
# O+ ]2 L$ M# Z7 h; x1 s: n |
|