管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
- j& @5 I- h j- c n8 J% V$ E: Z6 ?, j1 G7 P% e) }8 q# V7 a( u
1 h4 U |* e9 H5 M& G- r( E1 Csocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。, z2 [; }+ i0 j% q
8 d: _* ~% s! I
5 v- \- Z' U R+ ]( e
TCP协议 t+ [' `9 f; k5 X' ^
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。) [2 @4 l; J0 H' j
8 @+ M( B+ ?/ R' e$ M% }
$ H; w, K8 }: o3 y s) V( K关键词:三次握手,可靠,基于字节流。
h* ^; t' R8 @! `$ `* X" `4 B0 ]
; L9 N& } T- u, _5 l
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
2 n% F* i( ~8 B. G
: E3 X( k+ }; x; N$ v
TCP服务器端和客户端的运行流程
" g+ r( V- b8 O: M% d) F1 s. u3 y% E0 K0 r) H4 s
: o& B1 F6 F, z* [7 B _+ Z' _如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
+ \* p1 y- A) w% N& z" g$ ^0 d) Z8 u' J( ?0 n( g! u% Q/ C
! O- D$ V( S& C4 C0 J: v1.创建socket
8 G! V2 m# _8 K0 n. w* R socket是一个结构体,被创建在内核中- k5 L( M" V- ^0 n' S, {! U
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
: h6 Z9 O% g* l! c% k( U8 f$ p; \! v( ^ i
( Q4 @% ~6 n; I5 U4 i
2.调用bind函数
( k+ f5 d+ ~6 c$ x: e/ L' p 将socket和地址(包括ip、port)绑定。& l6 A9 Q" _* ?/ s3 W$ S2 S
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序( r- _( _. T9 ?6 `5 w3 G
struct sockaddr_in myaddr; //地址结构体) N: l! P& k0 S& z" n
bind函数
% J( [4 n6 p A bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))* k M! ?0 L1 f R8 W3 M! _
: ^. S" T6 n, i$ n" z9 H; T! K$ W, k! n
3.listen监听,将接收到的客户端连接放入队列5 b$ S* U& P; d, R
listen(sockfd,8) //第二个参数是队列长度
}2 }% o2 q( k0 H$ }2 c
5 _: N- `$ o- S1 |. `# \* p: s/ Q! z* C$ r G# N
4.调用accept函数,从队列获取请求,返回socket描 述符
! ?5 ~2 A' y( ~$ Q' b 如果无请求,将会阻塞,直到获得连接, B: K! U. w3 a; \: y8 {
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数" o$ z9 c3 ]- y1 M' N% I, r
o. [( ]+ Q0 ?/ Y Z
" z2 b6 j' v8 m* G% U5.调用read/write进行双向通信% f% Y7 L4 y& q1 G5 Z
6 ?# Z6 V7 T+ q$ N) y* x% Y) q, Q, o8 a6 [! e
6.关闭accept返回的socket
: u3 D* J/ K) y- `5 {* g close(scokfd);
- s% u: q7 O8 o/ H. P
$ {0 J" m9 f) G; C7 {3 _
; H3 }$ `9 ?& I
* \% p6 N+ ~! ^: @ K
7 B5 Q$ y, _9 M% f下面放出完整代码
: o) V6 n% |; @; z. z: Z7 h9 j0 I* X
- /*服务器*/% D4 K4 q! m/ c# c
- #include <stdio.h>
/ v t+ u: e$ m+ r6 V. I - #include <string.h>
" }% }' X/ _* z4 S9 a4 {: n5 ?! C - #include <stdlib.h>9 G3 d& D$ L7 h+ g9 q
- #include <strings.h>! u& U3 _8 ^$ X; x1 P! @/ H
- #include <sys/types.h>* i: N2 z% T0 W. p% N* e
- #include <sys/socket.h>
& R ?/ d, J; p% Y' Z1 {, Z - #include <arpa/inet.h>
9 `# Z' ~9 O; y' |! ?1 S6 D8 D - #include <netinet/in.h>2 b! e2 g- ~: ~- m
- int main()- V: X) i# K# A1 @3 O# \
- {
( i2 P/ Q5 G( U2 s" |. ] - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
9 {* M0 D0 l3 x6 o - if (sockfd < 0)* O3 Q. f' g8 v$ _. A8 E
- {0 X( R$ T" @3 e9 G
- perror("socket");; c9 w. p$ C7 p5 K- y1 y
- return -1;' z$ _8 S8 s- [7 x, a
- } //创建失败的错误处理
4 ~$ d' G @- C/ L8 r) M+ S, N# k - printf("socket..............
, V' _& e$ R& d7 p - "); //成功则打印“socket。。。。”
( U1 s1 N, l5 ^- B -
& A+ _; [" e Z/ L4 k3 D2 } - struct sockaddr_in myaddr; //创建“我的地址”结构体) J4 y6 L7 ^ f0 H7 `( v" `8 c5 _
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
/ ] K+ `+ T+ ~* L - myaddr.sin_family = AF_INET; //选择IPV4地址类型( K: m0 |, [% W' g
- myaddr.sin_port = htons(8888); //选择端口号7 }3 h# U3 Y$ M+ |' G# e7 G
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址! h. t' v# g; G# l/ B! v) N/ @
-
/ m" I# v9 y% C! k& ^% G! s0 g - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
W3 E9 K7 F/ ?0 ~0 C6 j' x - { |/ O+ a+ o( @1 k. Y- J% h4 D
- perror("bind");
6 h5 o/ f1 {5 {5 V3 ?' O9 T - return -1; J/ F# d* {, n6 z. Y6 G
- }
/ @3 R8 S7 p! o2 p' U$ F - printf("bind..........
' A/ h+ }6 V$ ]0 r - ");
8 H4 P( ~5 o% v' m- ^ -
3 f. W& K+ T& n( q - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
( k! H' i3 n' i0 Q5 [ e* f, C - {
4 H/ n2 C6 y* a! {8 c1 ` - perror("listen");
- b3 [/ G+ P( g - return -1;# h4 B, s4 |' e5 e$ N4 J
- }' o/ K5 M" R+ a* L. d
- printf("listen............3 X1 W( {2 _5 v( J( ?" E
- ");
3 d; J( I! E$ v - , ^: Y/ M/ e, N% H( Q8 ]; j4 n
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
' z# F+ q9 f/ B4 X. Q - if (connfd < 0)
! X `; \( n9 ^6 o/ i* Y. l9 w8 u. @: D - {0 W9 p N2 L5 X2 x5 o0 D
- perror("accept");
: e% L* C' }- \/ c$ E& W) | - return -1;
1 g$ \0 K2 e i4 v8 f1 z: f - }
3 @! I" O. ?2 `8 v9 E - printf("accept..............
# l* O: b/ O! \6 M8 N - ");
4 ]1 x- e u1 l/ Z0 ^% U' w - char buf[100];//定义一个数组用来存储接收到的数据
' H( `/ C# q8 s& ~ - int ret;9 R* s! N% T( b2 i- A
- while (1)
0 Y3 d1 v0 z) p+ O0 o - {
/ @! d) j! w+ C( y! P - memset(buf, 0, sizeof(buf));6 u" }, S: a' ^7 U& R$ ^
- ret = read(connfd, buf, sizeof(buf));; R( Q0 x5 M: P& [
- if (0 > ret)) Z' _: l& D- i2 g0 |+ `( @/ m
- {& J. e% y% M3 |* S F
- perror("read");
( E" |4 t' R* @8 w - break;- Z* f' e$ P4 d' |2 w h- W8 x
- }//执行while循环读取数据,当
9 Q9 L6 c' l D. T" r0 a1 c - else if (0 == ret)
, f, T& A' }) ?( m' Q7 J - {2 H. U0 ~. I/ r- a( f1 _
- printf("write close!
1 y. Y8 h1 D* P) X) s5 b4 m - ");0 Z1 i- T% G2 S+ u; N7 x) ~
- break;
2 x5 G' r7 Z* ]& K7 f - }
' H3 D9 ], N* ?, j [' J; c( Z3 Y - printf("recv: ");9 |$ C v# N. ?$ n5 M$ u, f
- fputs(buf, stdout);//打印接收到的数据9 I( J: R# W2 z7 ?5 }* a
- }
4 ^$ d) \0 O- v$ B2 j - close(sockfd);//关闭套接字% o6 B% }9 ]+ H) z: B" V0 g; N; r
- close(connfd);//断开连接
# O( t4 X6 e) J5 A& w. E - return 0;
% p& W5 h9 T6 z7 k4 U) J - }
复制代码
" D, `# i* ]; n& W: c3 N5 K1 {
7 v- H8 ]* Q# L- /*客户端*/(具体功能和服务器一样,所以不再加注释). d' u# z" t `1 x7 g: A7 R
- #include <stdio.h>0 q6 d9 {- o, J6 A
- #include <string.h>. [ Y: D: }& V. x! e! _! ?2 v, k* H
- #include <stdlib.h>
% e# u0 X! n2 p$ ^' {6 L6 L; |0 { - #include <strings.h>
{) B' l5 N, Z# [) M& ` - #include <sys/types.h>6 Y& H$ H( y6 ^. J" N9 z g K( g
- #include <sys/socket.h>+ K0 i8 s. p9 r) X; e/ U, l
- #include <netinet/in.h>
1 a3 a1 p1 t! r/ e2 x+ Z - #include <arpa/inet.h>! w ^! J, a% b: r3 N5 c/ z
- int main()
4 K% g9 n% [1 A2 n - {
: Q7 x6 I. l$ ^7 P! B: V - int sockfd;) ]8 p& I/ p: S; J+ V3 F/ o
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
6 o- M. C9 |: F5 K3 E. ? - {
4 _! F: t6 o6 D3 R6 |4 l: I - perror("socket");
7 s/ b' r3 z6 [% x/ l" r - return -1;
( v4 m& }9 n7 g: |! y3 d w - }# m. i+ e+ j! }1 _
- printf("socket...........7 c2 z4 ]/ e1 S% i. C8 \
- ");
$ w; F! r+ I9 `, e - " U& L. V8 @8 I5 Q
- struct sockaddr_in srv_addr;
# e" l; ~2 R' L# B9 E8 w& v# w2 ^: Y - memset(&srv_addr, 0, sizeof(srv_addr));
: r2 s- U5 K5 e1 f5 g6 k - srv_addr.sin_family = AF_INET;
$ g% B3 [/ }* R$ j `: P* v - srv_addr.sin_port = htons(8888);
. I' l/ Q W2 S( W! V q/ z) l - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
0 r) o- [1 d; g3 y - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
; \5 \$ O- Y+ q5 G, { - {" b6 K9 s- k7 g, S* n% f
- perror("connect");
+ d1 r% F% C1 k+ G3 F - return -1; //exit //pthread_exit! Y: g% g* @* q! w2 ?4 N
- }6 [$ V8 J; P. X3 t( y, M6 s3 M" o
- printf("connect..............5 ?' [* s% o! D3 P3 i
- ");0 r; G* y9 U8 V' l
- char buf[100];
! K: ?0 S$ f" z p7 G( q* y - int ret;1 f! X" S8 F9 P/ L0 {/ K
- while (1)9 G. N1 b. E7 v8 v: e: `$ F9 R3 s
- {# p8 |5 }0 s" i9 h% \, }6 j& S8 J
- printf("send: ");+ {0 S4 ~) y: u- _- R
- fgets(buf, sizeof(buf), stdin);
* H* c5 k4 A: K, E# X: o - ret = write(sockfd, buf, sizeof(buf));& Q N% a7 }( {7 u# | M1 b5 A
- if (ret < 0)% m0 z& c. l- H- f
- {
0 J: g+ B1 O, c+ r - perror("write");- |" R' _* ]% X# D
- break;6 L% F: Z- i+ V. Y. g& f! u. e/ f
- }5 m* r. g5 }6 ^
- if (strncmp(buf, "quit", 4) == 0)
+ {1 e3 D. w6 G" q - break;( @0 C w6 T4 c3 |8 ?4 n5 d
- }
! H# N+ X3 N3 O8 j/ M* ? - close(sockfd);* I, {+ ^, m3 a# C+ L" o
- return 0;7 i+ H6 a, w+ N$ q
- }
复制代码 2 y! M9 m8 d! y+ D7 O
5 ~9 r; F- Q! ^: d; } |
|