管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。. l* u4 I( H' I( T
# K& v' ^% G9 f7 K' ^, _
; r% I7 W# ]) Bsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。1 W% w R1 _" c6 l5 W, G
2 |& L& }4 o+ B( J' |/ B( Q2 o4 b
+ `% c" _" `2 J( Q' y! x2 C9 ~, M7 D
TCP协议
; u4 o k! ` ~/ x6 e- K# G# aTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。' r; V( ^, l8 \% C6 k( E" c+ B
5 P- J4 K, ~1 L9 I, _& @: E2 i N# _
关键词:三次握手,可靠,基于字节流。
* f4 i: h* W5 H2 n" \9 B
' {! W0 ~: c1 o: g, H
1 }: L* G6 w$ {1 g4 F! k+ q/ A可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
# a9 n$ e, p; K- c0 ?
8 O: b. m3 a/ f, z& r0 o
TCP服务器端和客户端的运行流程7 D; v# w8 O, c% E
$ {4 `, [+ V9 e" \3 g. y1 s, L
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
/ V0 q% u) ]6 {- B* |* D' Z3 w0 q* w5 ]6 d' O
9 Z/ C% `4 F0 H5 g% h# u7 l
1.创建socket" g0 X% j* J9 ?
socket是一个结构体,被创建在内核中
+ m' Z5 r$ ?8 A+ x7 ^. { sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议4 W+ c; g4 y% \, R+ K) Q) ]
, j# H& x/ d/ @% y, |2 u# [4 ^$ B p/ \2 V f* l0 ~9 n
2.调用bind函数$ s! \3 W$ J( E: E
将socket和地址(包括ip、port)绑定。
3 l& {2 p6 l7 ~- A 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序. f# k: p; T& U) q: u" D @
struct sockaddr_in myaddr; //地址结构体
2 N, r( c2 G y P+ ^+ K bind函数4 m& I7 l9 {3 s* }. u! J g4 i L2 q
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
}$ a/ x% J/ _" e- \/ f
4 b* v \% ~# x! y' k
/ U9 n5 y, Z& A! o8 N3.listen监听,将接收到的客户端连接放入队列, h, V9 V H" a
listen(sockfd,8) //第二个参数是队列长度
3 o( g i, }, Y$ Y
) Y& g, C5 `: h: x9 X) V* c
4 u. t& X) c/ @0 z; M8 @1 Z6 v4.调用accept函数,从队列获取请求,返回socket描 述符" a9 F, ^- C' U, @: o
如果无请求,将会阻塞,直到获得连接
. r+ g6 }5 U! O3 w int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
6 R1 {. P& G; ?/ z/ H% O) J% N! S! P2 P( R
9 W/ b+ b& Q) i0 s5 X: U
5.调用read/write进行双向通信
y( O7 @2 `9 t" [+ T( ]9 d+ Z( i- v9 q" W; a/ ]" R
! c* V- _8 ^' k% k2 j8 ?; i- e- }% R; K
6.关闭accept返回的socket
1 @2 B- z3 F! m, u$ Y4 Y close(scokfd);8 k, v- E @: u
5 ]0 q, z1 F5 B* V
0 j7 n& G: z3 x8 L
5 Y2 I" G; ^7 f2 z9 F+ A
m. Q* ~+ j/ H/ E- E下面放出完整代码& b$ y. T- `4 I# Z. W% m! [
$ q, w- U+ d( Q, U- /*服务器*/
& m* N! W" E+ T5 m+ F( {, C7 I - #include <stdio.h>! C1 w) H8 k2 Q, s
- #include <string.h>5 F/ F% l& m( W5 P8 C
- #include <stdlib.h>
) C A& O }+ m+ Z5 t0 d4 i9 U7 P - #include <strings.h>
0 v; d2 Z7 \5 E b9 ]0 f; c - #include <sys/types.h>
+ B0 x: E$ ~0 e' R - #include <sys/socket.h>( u( s5 R0 D/ A# J3 f W
- #include <arpa/inet.h>
, j" c4 ?9 a0 j4 g4 i2 U - #include <netinet/in.h># R" {+ r# m% F5 A9 H! X
- int main()
. w M( c- _4 O+ s8 U, S - {2 y9 s; k% e& A/ `" X
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
' C5 s4 ] O' y; o$ E1 Y - if (sockfd < 0)! m. }) o( Q; P5 Z
- {5 C7 k6 A, T" e' _3 I0 N- Z
- perror("socket");
2 d- u D* {, l7 e/ r - return -1;
$ \+ E2 K. B6 j3 s& U N# ^: X - } //创建失败的错误处理
5 E* U4 \& e7 _2 {+ U/ U - printf("socket..............
2 t) ^$ ]. b6 k, _/ o: y - "); //成功则打印“socket。。。。”( t0 k' }' Y1 {8 w: @
- - `8 C8 o/ }3 R: ?( s% o& z5 N- D
- struct sockaddr_in myaddr; //创建“我的地址”结构体
0 {- J% E( n# L3 B1 c. j1 c - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
3 M2 F3 E1 z" \7 v8 E4 ` - myaddr.sin_family = AF_INET; //选择IPV4地址类型
+ B4 O, {+ n# @ J+ |3 g/ h9 w - myaddr.sin_port = htons(8888); //选择端口号3 v- c& w2 {. `: t3 V
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
, z$ |$ H# H1 G+ K - . W0 U4 i( e1 k
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
: `/ y4 ]* y. f- z2 x' B3 y2 E - {3 f. \; F4 n: i, J( {
- perror("bind");; H* N/ k. X, m! N% t
- return -1;
" Y- R% Y; b0 [" Z4 i4 M - }8 f( q5 q: Z3 } f" X+ t7 q
- printf("bind..........
4 z+ ~/ Q* k% M, Q - ");. G! O E9 k- q, F
- : w& R- i+ z& `9 P# t
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
5 t) F# O! M( o! U# x - {: |0 g4 _( V$ w
- perror("listen");
% j! N; \; }" v, G/ T1 ^; b - return -1;3 ?, A+ Q& A4 d7 V' Q
- }
3 r9 s7 {, R9 ~7 k4 n* l! t - printf("listen............6 {( d* y) N' \' X2 x- z
- ");
, e# l) p2 T c' | C' l -
. ~+ O' z( p; f& b$ q - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
" B- ?$ l# z# a4 G - if (connfd < 0). s0 R2 s! j( \: h0 O5 d P9 m) Q
- {0 j( B) y" t. C7 v7 Q: [
- perror("accept");* R1 f1 D! t0 ^% l, c
- return -1;
9 w& E) b. W' d! y/ @ - }
7 a# _6 O- L) M1 d& s - printf("accept..............
" `7 W/ l4 l: v# C( ?, C$ C - ");+ d1 c+ }; Q! X- C4 u+ X5 @- _
- char buf[100];//定义一个数组用来存储接收到的数据
7 ]# t: m( O% o# T4 X- { - int ret;
7 x, l* J7 f. @& ]7 V - while (1)0 J, \" ~* Q5 t0 A" C
- {
r+ B# e+ ^8 | o - memset(buf, 0, sizeof(buf));; }: y M6 O, }# W9 x
- ret = read(connfd, buf, sizeof(buf));
! t3 u+ w$ S' K! Q' R - if (0 > ret) D# d& C& b- D, d; u' H/ H9 d$ p* l0 g7 c
- {
4 m7 b' U: c9 ^: S2 z7 S# t# {3 ~ - perror("read");
% b1 ^4 N% _* _& O8 P9 F - break;
8 G8 R: q8 l4 }- e! O' Z - }//执行while循环读取数据,当
- N$ R3 Y. ]) z5 A9 s6 ]7 F: L4 J - else if (0 == ret)
* Q0 E6 y* ?7 o+ u - {
* `; \5 }5 u7 Z* u5 l - printf("write close!7 m5 e( h$ E+ \2 d" B% y* W) l& M
- ");6 |( A# S2 F6 t/ T# ]7 {" S' v3 o
- break;, r/ F0 A/ F3 z, f/ j9 E# P8 B
- }+ w% @7 n: s3 q; N
- printf("recv: ");
* b6 g, P! F s q. E" Z - fputs(buf, stdout);//打印接收到的数据8 ]' W: m& z6 a: Q {3 v; {& h
- }1 r$ B: l. F) W5 \
- close(sockfd);//关闭套接字) d# a7 E& z: F
- close(connfd);//断开连接# Y6 W! I3 w# F" s4 ^ i$ w5 Q* l
- return 0;6 O4 j- g: S. i7 G4 _4 x9 g
- }
复制代码 ( J# {1 Y* l# W* q3 F
; o' @. F- S5 V/ ]4 {$ |& D
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
0 _+ i3 c0 E& O$ p" _0 L0 {2 p9 N - #include <stdio.h>4 @2 L. K* K+ v
- #include <string.h>" f' P, l' K9 b. [6 X
- #include <stdlib.h>
4 z# d0 ]0 r# h# T - #include <strings.h>
0 v" w: ^; P* Z* A& F M - #include <sys/types.h>
: S5 j. |: I' j. q% d' y6 g5 I' z - #include <sys/socket.h>% o* K$ F- a; b7 E
- #include <netinet/in.h>
' _0 D2 X _- }+ @# z6 k* X - #include <arpa/inet.h>
) [+ ^) w6 L! x" h$ ~# z. B - int main()
+ _' g0 m( _0 [# X - {
* _; R5 w+ t) p# U7 } - int sockfd;
/ J9 ], }* x' a3 e( Y+ L$ v - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
4 H4 Z% S/ Y8 K+ H; m6 N - {
% v1 ~5 I: a/ _5 n, w( b3 c - perror("socket");8 d5 f$ `. F1 m/ x& x7 U
- return -1;
% u8 |8 H8 m. @9 N1 u& X - }
. D7 H5 S% p, I: [) ]# @& a - printf("socket...........
6 W( m1 t$ C. v0 K - ");+ ?7 P/ R ?+ N) y! J
-
o1 H& L% k" O% X% y' t# V; }: L - struct sockaddr_in srv_addr;
U/ [5 \) p$ }! y: j( {5 q+ s - memset(&srv_addr, 0, sizeof(srv_addr));
: a$ u [! e/ m' V - srv_addr.sin_family = AF_INET;$ H% k' L. V: V0 ^
- srv_addr.sin_port = htons(8888);+ T- `2 L5 X" T: x, O" A9 X3 b( {
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
5 ?( T T' I2 |9 ^# B/ @ - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
! H& v" c* F3 Y- m* N - {
5 G) n% _; _2 i2 n* ?2 w/ x1 O - perror("connect");9 ^- l* d" A4 I0 Y8 i# o- A
- return -1; //exit //pthread_exit
6 a+ E$ K5 P* b* f5 v - }' c; u' V. d' H- b/ S
- printf("connect..............5 L" x0 x0 d) I6 q
- ");( }4 O/ X' K4 c/ x1 h" ^3 |! q- V
- char buf[100];
: g* Y- k! v L( r$ H - int ret;
8 o( e' |6 V! ^9 ~+ E8 @ - while (1)
+ {4 K9 @3 X( a6 ?# y4 b" h" ?. K - {
0 x% J) b1 W- N8 a# Z! m" g E - printf("send: ");* a) [8 ]& e* p2 Q
- fgets(buf, sizeof(buf), stdin);
5 t, y8 t& c; H5 B - ret = write(sockfd, buf, sizeof(buf));! }/ i7 t, C8 j
- if (ret < 0)
3 a; H, e' S9 j9 |1 ]! b - {
& n" V2 W$ r( j6 p - perror("write");
( Z D9 S( Q; A - break;+ W" N( ?( r# O/ [2 \# N- d
- }
& y) D' {6 j5 ?2 b, ` M - if (strncmp(buf, "quit", 4) == 0) `' d( K, n6 [! P$ B
- break;
8 P5 h5 U: W; H6 M+ l0 h6 c$ h - }) Z% @8 W) [1 u u
- close(sockfd);" Q6 ^5 \5 }8 b# ~
- return 0;
6 ]( y7 L* I. B/ {8 ^0 s. z% O - }
复制代码
7 t( X8 c3 [. k9 z* u8 k! P& u7 |7 l/ u
|
|