管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
: n( l& Y6 W# g. D' I4 m
* `2 Q3 z6 Y- j5 D! U2 K3 s) V9 n% a" W1 v' y6 ^
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。4 @. r& U) U) u: B3 R2 M5 N! j
. U- `- M3 U/ [2 [& g
8 a; n) p6 W! k4 @& x
TCP协议6 D5 f6 e6 p; Y0 k9 A3 u
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
: ^/ g" S; C3 f9 b; x3 y$ y. W6 c8 f3 W/ C
/ r4 C& h0 |+ m; y8 J& F N: @& E关键词:三次握手,可靠,基于字节流。. b- r3 n. Z, _, E7 p" b- Y
2 {& z3 u0 I7 R2 I4 `9 D+ d+ Q# f( t+ H! P6 C
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。- L4 ]" `. v8 I/ ?8 {$ L( K8 e8 t. v
5 f3 d- G( o6 ]/ ]% kTCP服务器端和客户端的运行流程* @* O+ ?# Q+ L
& B- Z$ d6 H; [* r6 }$ w5 _9 [# R3 h4 B6 h8 }
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
7 T2 e& w3 y8 H, V M2 g" D5 I1 f8 ?3 X6 Q/ L6 { P
5 ?8 d/ D( H8 X, a
1.创建socket/ o9 X& \: X* E% Y0 G$ W
socket是一个结构体,被创建在内核中8 |' [7 e. r9 _9 ?/ I E8 d
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
. r; Z+ V p$ K3 r9 b
# V7 A9 W1 T# f. C/ u) C3 K( l8 S$ h9 E0 {* R9 r4 Q& {
2.调用bind函数
0 k {! j0 I2 ~5 A 将socket和地址(包括ip、port)绑定。; n! V1 m4 R; y
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
; B S% n0 P+ j struct sockaddr_in myaddr; //地址结构体) b! N; {. q C
bind函数
3 P( u) z5 B- a* Z! e4 M bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))& n- n- k- F8 q5 h# X7 i( U
. V: C3 D# g9 N- @# a* x1 ^# e/ Y! ~: L( s. M' R+ R- }; Y
3.listen监听,将接收到的客户端连接放入队列4 u1 R: @4 r* y1 `. Q& X
listen(sockfd,8) //第二个参数是队列长度4 N7 B4 `0 F% l% j( p. O0 [; X& k
. Q0 i) M! ^2 |0 A( I# n: s( d w# Z$ s6 G; _# g1 {
4.调用accept函数,从队列获取请求,返回socket描 述符% J1 K/ {8 q2 X1 H
如果无请求,将会阻塞,直到获得连接
6 L- B5 l# i6 J d4 V- m int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
8 V7 U+ U% p9 x% U: T
( \" |7 [; i; Y0 `0 g o' P$ E' ~* A8 I! V- _# H' h: U
5.调用read/write进行双向通信
$ a/ q* E+ K+ W+ y! R9 J$ z3 G- M( _- N( s
# M# B2 _# s. X a6.关闭accept返回的socket
, _2 u$ q$ I! Z! l/ r close(scokfd);
8 _: ?2 q9 Q* o2 X- F$ _; ^8 q; |/ O& T: D% ^/ Z" A
7 Q- t6 J: c) {3 n2 b
- O% t$ p. P& o. V7 U# D% B( N6 z% A. F
下面放出完整代码
: k- v7 L* z: `; M
3 a6 N2 S/ X9 r' c+ f5 n- /*服务器*/: E# R& A/ {( w& u; L+ l0 J
- #include <stdio.h>
# I# W+ Q/ Z) v! u) {% U) _ - #include <string.h>3 k- u( K/ R$ O0 N/ \
- #include <stdlib.h>
3 D# F% R: H3 h+ g i* C - #include <strings.h>1 ~3 K# N0 ~: u& N0 C, M. q6 x, m
- #include <sys/types.h>
9 H2 D$ j" }# e) m& a) q - #include <sys/socket.h>+ \; {; Y3 ?9 | O, h, m
- #include <arpa/inet.h>
9 W8 n" C1 N8 a( g - #include <netinet/in.h>
6 n6 B3 [4 k2 G* D - int main()+ O; [- X6 a3 }
- {
5 }: D$ T% r! `. i - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字+ f& H( j: j3 x: M3 p5 N: i3 b
- if (sockfd < 0)$ r8 z- L) f) s
- {
/ l6 _$ L1 x9 K# ~2 I' Y5 c9 t5 F - perror("socket");
! O( k9 @* g! f6 ?6 K - return -1;% ? x" h% L* A, x2 }
- } //创建失败的错误处理! M, N. Y) ^" }. m% E& Q
- printf("socket..............3 f1 H2 y, w6 ^
- "); //成功则打印“socket。。。。”
; C7 |" ?2 y! R) O) L4 [$ c - 5 Y. L# @& ?% u# h6 _, T- h |
- struct sockaddr_in myaddr; //创建“我的地址”结构体. m. O2 o. M" u2 T$ }
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)$ U3 L$ Z/ D9 J9 J9 b
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
) b, E5 ]- q7 V0 V1 t - myaddr.sin_port = htons(8888); //选择端口号6 p" s1 _6 Q' Z$ W7 H) m6 e7 D
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址* W) e6 u. e& I2 w
-
7 l( w! P! J3 W, _ - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
7 X4 G& l8 w I% \ - {
* W, [5 m# {/ J) i/ P/ [ - perror("bind");
; i; `7 I1 f; \% L9 i - return -1;
' p. ?5 r; t5 A9 v: n - }- C2 H- e2 F# C
- printf("bind..........
( w1 g0 n3 n5 ?9 X! }- n - ");
) b! h {) B7 o3 v0 @3 i! T - 0 N1 [5 V; k$ O+ U ^( k, ^
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
( K! ]. e# {; Y$ F9 q" G - {1 T6 ^; N+ I5 U* Z& j6 T1 v0 b
- perror("listen");1 V' f0 n, d4 f. J
- return -1;% C9 F9 X- G$ K. O: S
- }
& B- R4 D0 G" c; ]: O - printf("listen............
) B; s2 o' V7 ~4 i - ");9 L2 O9 W; n, o' F) }+ q& q3 z
- " |* l1 u' G; Z
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
; _ I3 P% ?; Y2 m - if (connfd < 0)+ N3 A; O3 T6 T& l' ~8 N
- {% |/ n5 X: l2 Z* u& f
- perror("accept");$ ]& P3 J; t/ E# {5 U
- return -1;
3 G7 h) h2 H7 a: f# G% m - }1 s6 K. L1 N! q6 ~4 K
- printf("accept..............
% ?* E. n6 ] O* z! q; { - ");
: b- ]& ~6 J5 a" O9 w: H# V6 K - char buf[100];//定义一个数组用来存储接收到的数据) n) ]- Y9 ~& E# t+ B3 ]
- int ret;
& E9 P( U! B5 ~ V2 w5 P$ z4 w - while (1)
" U$ v1 n( V) j& r! D! C u! R - {
' e+ [1 v. Z1 `$ | - memset(buf, 0, sizeof(buf));8 W) U8 _( U1 `* ~9 o( a. J
- ret = read(connfd, buf, sizeof(buf));6 h0 U4 d! {/ r) M& e
- if (0 > ret)* E, u* l+ N8 _8 m( v' M+ n
- {5 E V6 n4 o% q) _- \ Z2 ^
- perror("read");" G- [! w' Q, y, H
- break;0 ], {7 ~! k ]; f
- }//执行while循环读取数据,当) ]0 x! ?7 [! ?0 T6 H
- else if (0 == ret)/ w$ L0 V* q4 {0 s4 I0 L4 W4 J
- {$ T" A" S7 E$ ^/ e
- printf("write close!2 z% a- L$ ^0 E ?7 S
- ");
, e1 {, v# n+ X, B. J( s - break;) [* Y2 u. G* v" M2 a6 Z1 \
- }4 Y6 d% \9 X8 z# o
- printf("recv: ");
1 r( b5 u: V5 ^1 }) q1 x! U( Z - fputs(buf, stdout);//打印接收到的数据
v' B- @, s% W4 T: w7 { - }8 Q: n; I( l) n0 U0 M
- close(sockfd);//关闭套接字3 F) X$ W( E0 }5 ^* U& [! r$ e
- close(connfd);//断开连接
! j) n% _# [" G1 k, q7 {8 y - return 0;
* p9 C$ H9 ?2 y4 s& o. i - }
复制代码 : B* g; @1 \- F0 j0 J% _
* L; T+ Z8 D* B' T' }% v- /*客户端*/(具体功能和服务器一样,所以不再加注释)% M: A3 c. b. q4 ^6 r7 c- M
- #include <stdio.h>) T Q8 A: d* X
- #include <string.h>
X" s9 C7 h! j6 b# Q' s- x y - #include <stdlib.h>) p9 I8 k6 U! X2 w
- #include <strings.h>5 Z. z! u7 k* Q& e
- #include <sys/types.h>. q o" M/ G) ~/ \! j
- #include <sys/socket.h>( B! P9 |5 I8 m7 }! n* \9 W) p
- #include <netinet/in.h> e1 @1 X: X- R) F* t5 R
- #include <arpa/inet.h>5 J1 {1 \- i4 B+ u0 L! k
- int main()
/ Q: z' ^; p5 J - {1 D% H2 P# D- p& m
- int sockfd;
/ k0 L; }6 ?5 @ - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
/ W F- Y% C& }7 l2 p& F - {
- k! y, I2 ` \, s+ p - perror("socket");9 ], q% Q8 S# u4 X
- return -1;
+ L- _( F6 _: i4 n9 J - }" h0 ~0 i0 A3 d# E& ?
- printf("socket.........../ f9 ?- `3 Z5 W5 o( ?, b; y
- ");
4 r" Q6 D- z3 H# P( r5 y6 }1 Z -
" t( t G4 T# I - struct sockaddr_in srv_addr; K$ I; g( B2 u8 X) t v" P
- memset(&srv_addr, 0, sizeof(srv_addr));
2 ? p8 q; y1 v$ V- k, L - srv_addr.sin_family = AF_INET;' R; y0 L3 @+ E7 C' H- {( @" y9 U
- srv_addr.sin_port = htons(8888);$ t1 O; _1 i5 k6 S
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
) _6 `- l8 B' {! \: x - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
+ G7 E; A8 K; m7 Y- k V& u1 n - {- t3 e1 z" G6 }& |" H9 ]3 [' C
- perror("connect");" }/ I# j6 [2 s7 X
- return -1; //exit //pthread_exit
# i) D$ d- X) U: S$ H; q - }0 A8 Y% ~# x# C! d9 w, N
- printf("connect..............
2 n" X# e# N& ]& _& i8 G9 p( @ - ");4 U/ `# R# t" `* C
- char buf[100];
. {2 Y: l& I d8 { - int ret;
l% E* X' h2 ?5 R - while (1)
: A% `! |$ h# l+ Q1 S$ [ - {$ X' u9 E2 m! J l. A0 ?; g
- printf("send: ");7 [# C' N# ]$ {0 [
- fgets(buf, sizeof(buf), stdin);, ?. i* t0 i9 x+ V M
- ret = write(sockfd, buf, sizeof(buf));
s" V" p6 U0 b$ x4 ~ - if (ret < 0)( w6 A9 n' R* Z& y
- {
- F! s6 f1 F0 e. n/ W1 R/ F L7 f% r - perror("write");! B8 Q# N5 O5 U( c7 C2 @
- break;2 I+ n3 P1 ?) I p7 G
- }( F B0 z4 H$ a f
- if (strncmp(buf, "quit", 4) == 0); P b1 Z( T1 `1 P; g$ O
- break;
, I) Q( ]$ W" u' l( k$ |6 l( u - }2 h" H& }1 W. |
- close(sockfd);
3 p' w/ ^" C, g |. q% {0 l4 S - return 0;
" P, d: u& E& m7 i; J' v - }
复制代码
4 |9 B( |& T" X4 t" R0 `8 f
9 i | g7 c2 I) d |
|