管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。" [) r" g- M, Q; K& j
' W. I" r: e# K0 |& J0 `. ?6 E
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。. }2 `3 t4 `, }# R- W7 I# p1 Q
& d, @8 i' r- l, i9 y0 B. q+ k+ \, W6 I
TCP协议
2 u3 {2 ?; K8 k7 `) S, hTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
3 I) ^* s/ ~0 |( z/ B; o/ c6 m, i5 n9 @7 b) {* _- F5 \
; o# Z% h6 T/ t$ ~3 y6 J
关键词:三次握手,可靠,基于字节流。
% k% d' X+ C9 w2 Z
% ~7 H& L2 \7 d7 d: j: l& H& p) _: `8 m& R
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
) ^! J; n' N8 N. A3 @, P" ]
3 M) c+ @' T! t2 G( K; d5 N, g5 u1 ~
TCP服务器端和客户端的运行流程
9 g3 ~# b6 T* t# |' ?* `) X8 s6 e' S
% H7 w w# g% q1 S: V; ]0 I
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?' w9 [: A4 h& T9 z% d* _( E
: c: M& i8 G' m, G
" l' U+ R. H* T. `& f
1.创建socket5 w* b2 ~$ k+ X J, p! @
socket是一个结构体,被创建在内核中
6 W3 v; Z/ G4 n8 P0 N5 K sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议3 w) B' |- B4 L; i( S/ ~
, G: `$ m/ b7 ?, B4 A! v2 w
6 ~8 E1 C: {+ W+ l& M/ _$ \6 M2.调用bind函数: x9 E* W5 e/ l
将socket和地址(包括ip、port)绑定。( X# t% |$ [$ i: m
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
, k$ G0 M2 a7 }5 C2 N struct sockaddr_in myaddr; //地址结构体
& i) w6 ]3 j, w4 ?5 | H ~; Z5 \ bind函数
) z& {2 ?6 [" x bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
1 F d+ \; i+ l4 v- Y6 @$ T/ U' p! W# s$ B7 A( ^
6 p- j G6 h: t, x7 n/ b3.listen监听,将接收到的客户端连接放入队列
! z) k8 L6 [9 c listen(sockfd,8) //第二个参数是队列长度
0 `! F- Z+ \1 @/ S
! T9 R: _7 Y7 n; a
- y2 F' k8 |1 T: {4.调用accept函数,从队列获取请求,返回socket描 述符
/ C: w' O. N4 Y" q 如果无请求,将会阻塞,直到获得连接
5 J) u, M+ V3 `8 } int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
$ B6 `# F1 g2 A
+ s% n/ m" O8 D% L+ ^
2 u: B8 P# p0 a N. P( p5.调用read/write进行双向通信# I# S+ m# G5 @+ T
4 n8 ^4 R7 E9 Q$ s, W# W4 ]/ h- ?% Q( n3 [5 K4 G+ b
6.关闭accept返回的socket
$ S) W S7 v3 u0 r8 ^! p2 {! J close(scokfd);
5 C7 e1 B% S) G7 g- Y2 s
0 Q! P; ^ w( N* f3 t9 h6 ?5 m9 U# D
. `& G' _5 H2 z9 g, j0 g! \6 f- F0 {
下面放出完整代码
2 B9 a( D ?% j- G! C+ T9 T' X' Z- H k* @
- /*服务器*/* |9 }! \# Z' X! n+ p
- #include <stdio.h>
# K7 l: G0 R, m( r( v4 l- }/ P2 a - #include <string.h>6 m0 B3 ^# m) R7 o+ f6 I
- #include <stdlib.h>/ n0 ?5 ^3 b9 e- h
- #include <strings.h>- E: Y) K# M i: B. a3 l; C' k3 O
- #include <sys/types.h>! o2 h7 L( C# w) n8 j
- #include <sys/socket.h>9 ?7 Y% r; I5 E
- #include <arpa/inet.h>, y9 t. W/ D5 \1 W& _% [
- #include <netinet/in.h> j f4 O' \$ B4 {
- int main()+ R! Q. [" h- Q7 _" k6 D/ m
- {+ V" l6 K( c0 Y+ ]
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字1 V- g$ L0 E+ E' G' @/ L/ }
- if (sockfd < 0)6 N% t; B5 I5 @, j
- {8 R: J" k" ]1 b8 D' r6 D
- perror("socket");
+ v* U' }/ A' v% A! U - return -1;
& Z; l+ c! R% |6 N& g - } //创建失败的错误处理
, R, ^1 J# ?/ {9 Y - printf("socket............../ Q, n: W3 f) p$ c! G) b! {
- "); //成功则打印“socket。。。。”9 k, g3 Q; W7 c8 a& U H1 N( @
- + ]- j: l$ ]; Z6 [- g/ a
- struct sockaddr_in myaddr; //创建“我的地址”结构体
& W( |1 n6 I- Q8 A) ~ - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见) z- t+ `) [9 Y! q
- myaddr.sin_family = AF_INET; //选择IPV4地址类型( j& J G# \* v6 R2 h6 [) [5 m+ G
- myaddr.sin_port = htons(8888); //选择端口号) M) _% d% b4 |7 w
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
1 q& p1 |" l* y - - `" w& c Y) T% K8 u1 J! D
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字) G; Y, a4 T$ t
- {
2 z9 ?4 h T1 | ~ - perror("bind");: o6 {2 p% K3 i
- return -1;9 G% m2 C5 ~3 [9 G: I! v9 D
- }" o6 c8 s) @+ J6 \' e7 v
- printf("bind..........& T0 s" x7 @5 j- u! c
- ");
, k) C( L% [ t2 O4 r - . K' F8 s1 y7 K+ s
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听5 M) m% |% `2 N1 i) L+ t
- {
; V9 R) E$ M4 }' [9 B5 o: M% X - perror("listen");
- x, [ o$ K9 c1 ~* M& a - return -1;1 C, D8 }1 l/ ]; h( o
- }1 `3 o, |/ J$ W$ R# @
- printf("listen............( x9 `& A9 \; h3 V
- ");
4 I" Q- }( _3 ]7 G/ f1 x5 Z -
) \" i& b5 \3 v9 r3 z - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求3 Q: ~* i) t7 h& \7 K: I& j
- if (connfd < 0)$ `$ p- W) p5 u! a5 @
- {
" v! f' C. n! |, K0 v# \ - perror("accept");4 d- N( X- l( a( ?' r: h! E3 j5 l9 Y
- return -1;5 p& G. z4 t) L( U3 {; E
- }" |) B. t9 U# ^7 o* y5 _/ m+ k
- printf("accept..............) M% t1 q+ x& [. @
- ");
: U6 O) T& c8 q - char buf[100];//定义一个数组用来存储接收到的数据
8 ]5 [ t! \' ^/ k( n - int ret;/ Z# B& W/ T5 K, r x, H
- while (1)
9 H$ I% L4 t2 }: S: k - { l# B0 E- Y" B
- memset(buf, 0, sizeof(buf));
8 G$ g' A/ G/ p Z5 M - ret = read(connfd, buf, sizeof(buf));
$ m, o V' w: g5 u8 v" c - if (0 > ret)
- Z5 f4 A7 L- p: B - {4 @( B/ f* y8 b5 W# H
- perror("read");
. S- V- J9 U* r% w: P - break;
: H& l6 }% s% O4 [ - }//执行while循环读取数据,当
/ h6 y0 ?7 M. [* k. w# X( F6 ?! K+ M - else if (0 == ret) P6 F; g+ K- C
- {
3 w& @9 g m2 i - printf("write close!9 E$ X$ `, `" d0 k9 F6 U
- ");9 V2 o/ _9 s" B$ b; M/ B0 t( D
- break;
! H: I4 b. }8 ] - }4 t. f2 U9 \9 h+ A7 f/ ]
- printf("recv: ");6 _; ~' S" A$ {7 |7 F d
- fputs(buf, stdout);//打印接收到的数据
5 |: _0 E3 q Y: W3 ^( }# ]8 { - }
! J, D U, V4 w r3 d4 i - close(sockfd);//关闭套接字7 O) T- I2 D- v6 _
- close(connfd);//断开连接/ M: M2 M5 n; |
- return 0;$ n$ t. |" g8 e8 X! _
- }
复制代码 3 T6 _3 Q9 r1 u+ {1 k5 {
4 w; ~+ i) l- x* ~) s; V
- /*客户端*/(具体功能和服务器一样,所以不再加注释), j6 A( u' O) X+ T4 |
- #include <stdio.h>
0 V" g0 y1 b( Q) n" S - #include <string.h>
3 v& p) X/ e) C; ] j - #include <stdlib.h>0 E A) C: Y7 [4 u! H/ g
- #include <strings.h>: n2 u. s6 Y4 o
- #include <sys/types.h>
. v1 _% J2 `/ A - #include <sys/socket.h>
$ ]* j9 J0 z8 W6 V, J - #include <netinet/in.h>
* @" B; |8 Y4 S# y, N2 _& R - #include <arpa/inet.h>
* c9 e+ B, I8 f# Y - int main()
" c- q5 V* ^3 ^8 D% W' R# }( q, c - {' P+ y5 a$ q( Y$ I. }! ], D; k0 i# J
- int sockfd;# E, Q; {+ X( @# d8 P. z: w9 {' |" _
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
6 Z! M5 s/ X3 t) z6 P! [: i - {& M3 y6 C g- _' O
- perror("socket");: ?! A' v2 P2 f3 M
- return -1;
7 s: H5 E5 R$ M4 g* l* z - }
' X- g/ O* I3 I$ l- S2 @ - printf("socket...........
2 ]0 I: x& Z6 f5 Q - ");2 Q5 o! S9 @6 n% ~
- ( w9 P9 |) G4 K: I# v
- struct sockaddr_in srv_addr;8 U; B/ `# I: a" L$ y; T
- memset(&srv_addr, 0, sizeof(srv_addr));
# U" S$ q9 \7 P - srv_addr.sin_family = AF_INET;8 N% [* p+ s- y0 k9 f S- k
- srv_addr.sin_port = htons(8888);5 y, c' n5 D, p. E" l
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
! W7 r+ J/ U' Y' e# E - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
# B( S# r# G* R - {1 m6 S. [5 t n% P4 Q0 {. N
- perror("connect");
0 O" @/ K( S; @' P% v& C - return -1; //exit //pthread_exit) u4 ]" |- N: S/ o1 d8 i
- }
! L* U+ d- X5 g/ [) t2 l! K - printf("connect..............% w# Q, @9 d2 q" T
- ");
3 W0 u; u; j; d" Z/ Z - char buf[100];( f7 C7 B6 N( R. }) C
- int ret; B9 [' v" ~- Y3 h( p( X/ a! R
- while (1)
1 l6 m) q! W" h2 V - {$ c! V9 T9 J$ A+ e
- printf("send: ");/ ]7 V1 D1 O9 r
- fgets(buf, sizeof(buf), stdin);8 i! ]9 ]4 S' ]0 s
- ret = write(sockfd, buf, sizeof(buf));
0 M/ t. p! X) F/ S) H - if (ret < 0)
, g- A7 A! m6 a2 o7 T- s - {( d; \8 H a) e* C' \
- perror("write");
; d4 c0 [7 f- I - break;
+ [! I! V2 U0 k3 Q' e8 k( P - }; _7 M; S. H! W7 {& e7 e/ w
- if (strncmp(buf, "quit", 4) == 0)
* A% ?# t+ T# l. d _5 o! { - break;
4 {6 g( \- C6 B. Z$ |! n - }! l, q" k% g8 `: q/ H* @! w
- close(sockfd);
/ C- s* Q* T9 f& L* |# o# z - return 0;
- P3 Y) e' F) S5 }* J - }
复制代码
' t# @* C! o6 x
% @& o$ {+ }1 A, C! V W8 M: g |
|