管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。( P1 e+ i f& J9 J0 L9 P5 u- p
% b! w& m' p3 p" X& f. _# ]
- J" W* X- `# A4 N5 E* I3 ?socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。7 X$ d, @, z& b: W. R% r
& _0 s1 I* L- G+ W' g% t- E' r: V3 K
TCP协议
; L p. N, i) J. @. J5 k: pTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
6 {3 H: S0 }( ]; S2 }) o" p' \" z ]6 O3 K g. B& B* O2 w2 G8 e/ ?
* u: j$ l7 m q- ]1 D r+ M' B关键词:三次握手,可靠,基于字节流。
4 @+ q& {/ l& ?- i6 U3 o0 a) }
5 T1 E& y0 O/ @! D6 J. Z3 _" a/ O+ r& C- x$ n6 r7 o
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。4 q9 J8 R( y9 p) }- l4 p! E' B
3 @: q4 R7 N! G6 K& m+ x# z2 UTCP服务器端和客户端的运行流程% [/ R4 Z& T0 R6 s" Z
. x' }( D* d- L" a% ]- [0 q
# j' I0 M1 o1 I' K+ t+ w# o如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
( E2 W. a( b( }5 I# Q- X) B7 z! B+ J: i
+ Q7 f; }) d: b1.创建socket. Y1 r7 A; ]8 ^4 {1 T8 F. j
socket是一个结构体,被创建在内核中
% c K2 C+ Q- K$ w8 C sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
7 R2 R& O8 J# l
! y" z2 }* I+ ~4 \1 @. @# d& ?2 U1 i% j0 I6 T
2.调用bind函数
+ V& S, z9 j1 z" U9 Y# o 将socket和地址(包括ip、port)绑定。
2 S6 Q3 x3 Z8 B, r# S7 R% u [ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序0 m4 d- w1 m; e. c! h) T1 L; K: C
struct sockaddr_in myaddr; //地址结构体
+ j# a. M. i' t, W$ D4 q) G! e bind函数7 S6 j% o5 R" C
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
0 N; E5 M7 W3 \% w8 e2 b( `4 _6 f& u5 |
+ ], }9 Y0 F( |
3.listen监听,将接收到的客户端连接放入队列; a, B8 l( M+ c, W$ [: L
listen(sockfd,8) //第二个参数是队列长度
4 R+ v% p8 N0 A$ ?- {2 b0 i7 [9 a2 d, q, l
4 M: A1 x* }7 i# e+ o s4 A4.调用accept函数,从队列获取请求,返回socket描 述符
# S- L# S1 _$ s2 T1 l 如果无请求,将会阻塞,直到获得连接8 _- z8 {! W& R8 j7 Y
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数8 N* I" i' `/ n3 Q# ] b
( l3 c7 j7 i+ Z1 u( Y
7 N- @; G- ]3 n% D5.调用read/write进行双向通信
; ~% B, s9 E8 v" }# c3 O5 N& v7 P5 O5 W3 f' P S# v+ R/ p
3 ^- B9 d# R2 I! z- f
6.关闭accept返回的socket" o( j, G+ H" U2 J& a
close(scokfd);! x$ c; I9 P! L: c- j' y
8 w3 N+ ^( F0 F& ^/ d `1 F* |
; E3 u3 p5 {$ [9 j0 _5 x" s9 N6 |+ |3 n+ Y7 H5 f, i8 C" @
5 R' n Z1 b$ ~
下面放出完整代码. [! J4 a/ }' I" K8 P' V/ y% i
9 f$ m& l5 |8 m# }6 D
- /*服务器*/0 N! u) @- t- _ l
- #include <stdio.h>6 I, }; U' @) k* O# _) D
- #include <string.h>
3 f9 y; a! F6 \/ c% s8 l; V& A - #include <stdlib.h> {; p) Q5 u- Q$ P5 T4 @7 g' K
- #include <strings.h>
' m8 h- ~$ i# B1 G1 U) T& R' R4 }5 p3 p - #include <sys/types.h>
7 E/ b1 N2 l6 _8 E - #include <sys/socket.h>
2 u7 k1 O3 ?/ _6 Y2 C# _8 p/ @3 T3 W$ g - #include <arpa/inet.h>
/ C& N6 \+ L$ q# ^7 R# u - #include <netinet/in.h>- C8 t" i, N7 G; ^' w
- int main()* w. p) C+ r+ W8 Q3 N
- {
2 R. B5 h& ~% Y6 e! B' @! o. `; L* o - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字, e2 n& f# H3 Q1 \, Q$ N" h( }
- if (sockfd < 0)
, q( a1 Q2 Z' f% H+ X! g0 S - {) L. R/ ?7 Q/ A$ { ] |
- perror("socket");
4 w* }# e. I7 j1 d' h - return -1;( H) V7 e0 a9 J1 a" ~! t$ E$ n
- } //创建失败的错误处理
7 C9 c F: l) H% }4 W - printf("socket..............
; F4 O* u9 ~* } - "); //成功则打印“socket。。。。”
/ ]2 W# k" I% h" w. ] - - h& `& }( Q' L3 B
- struct sockaddr_in myaddr; //创建“我的地址”结构体
6 |: l5 J o$ H" |0 @: M6 b - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)4 K% M5 x' B( G( @: U4 v
- myaddr.sin_family = AF_INET; //选择IPV4地址类型! H# J7 ^. L4 C0 Q/ C- j0 i; v
- myaddr.sin_port = htons(8888); //选择端口号
& i+ x8 U0 y) O3 p9 R: g+ ?/ O7 { - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
" q$ K/ `% o! m - ! y9 Z3 [' Z+ v, J8 I
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
- j+ \2 {. j" K- w- F2 m/ w! z3 |$ K - {
& @+ Z ^% S( y" P& Y4 X0 C8 x! v - perror("bind");; |& d- d6 y* z6 p
- return -1;
, Y8 ^3 h" \+ d( B& d5 O, {3 K9 K - } P3 r' H R# R1 l+ |' ?7 M5 N
- printf("bind..........
+ [" Z" b* I; g4 i. d j' p - ");
8 B' x8 D }/ E# H0 k' r$ h -
# z, D& P5 R- R- d - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
! p2 u( r) _* Z/ i# J/ W2 m F - {
3 w' Q* A4 Z" U - perror("listen");" S5 L$ ?5 U) [- Z! k/ p
- return -1;3 H5 U0 Y- H0 s
- }# t1 U. i5 T+ _
- printf("listen............5 E+ L/ _/ I/ ]4 c
- ");+ q+ A3 R9 u# @ b
-
# ]" k5 @1 p! _& d; a2 Y6 r$ i8 z0 c - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
1 u, |0 a8 ^7 {' f9 i: l - if (connfd < 0)+ ?6 M0 l3 t" t/ @) o1 Y8 _0 r6 \- k
- {
; o, Y1 \ Z4 ?% x$ F f- c - perror("accept");
8 a l5 Z& r. w6 N! `6 D, u" n# C* X - return -1;; [0 g: A) e& E- z
- }
! k6 o! }4 k+ ]" V: v3 P - printf("accept..............
& e. U, f9 c, B. X - ");* |3 D, Q7 z [& }5 c
- char buf[100];//定义一个数组用来存储接收到的数据% z/ C4 B+ ]" j2 U
- int ret;1 O4 p% I* M/ u' Y( }
- while (1)5 r$ m1 F5 N, p9 H
- { h7 I1 ^% I# E# M. I$ v4 [3 E
- memset(buf, 0, sizeof(buf));0 h/ E1 n( v# ], H# }4 Q
- ret = read(connfd, buf, sizeof(buf));
/ Q$ K' V( _% W8 b; k" R4 R9 G* H& D N - if (0 > ret)) M3 Y" A# D( D9 E
- {9 B/ ~9 I6 D, H' j0 x! @
- perror("read");
3 @5 S ~, a: h- y# @5 S - break;+ R/ u- y2 T: B! q# f
- }//执行while循环读取数据,当
1 {. k, i' F7 H; w# X" [! H - else if (0 == ret)% E4 ~5 [9 S! P* q4 q. Q
- {9 L, s( X) A; ^6 P6 C \0 k% e
- printf("write close!
, {/ T: J, ^# P* o' x - ");
7 d2 b7 _* k% B0 V8 P7 s - break;
" A* `; z( H7 T) R" r* b9 E3 i - }
0 S' D& V1 Q" w - printf("recv: ");
! ?3 m* _/ M& I! d" z - fputs(buf, stdout);//打印接收到的数据, u( e2 |3 n2 I
- }
4 [7 Y* k D- R. ^7 U5 b. J& ~ - close(sockfd);//关闭套接字- V0 s& t/ R9 ]. i$ V0 F
- close(connfd);//断开连接
k& ~1 t% o& G3 ~" X/ X" d2 X& V - return 0;
; w% F8 B; @8 v, ~/ M- o - }
复制代码
x0 {+ X( i5 z
' b! c. M' R/ t- n3 V: ^- /*客户端*/(具体功能和服务器一样,所以不再加注释). _5 n# @% w, a$ e4 q! F" B6 n
- #include <stdio.h>
# S5 x: R6 S1 a& X - #include <string.h>
' t( |* b4 ?/ k% A0 M - #include <stdlib.h>( w+ }9 p' U" N5 R( _. g; A
- #include <strings.h>
+ w5 S. a6 A" }) s) U - #include <sys/types.h>6 s+ F. N/ R, J* S
- #include <sys/socket.h>& w' G/ Q, c2 H( \/ {
- #include <netinet/in.h>
: N4 c5 ~ `/ s! U' n1 a( C - #include <arpa/inet.h>
) B+ z( G8 v9 C5 G. E r - int main()
+ r( l6 C# W4 {* ` - {# T& {# H+ W0 O( I) } |
- int sockfd;9 |, G1 d: `( l' s* G* X% l9 C
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))5 V2 J( G9 d, s" @
- {
* D" {$ \9 Z6 x1 d+ Q - perror("socket");
Z' o% k4 {( r - return -1;
/ L7 u1 B, |+ I+ |- ~ - }
+ a" N" V- l! X6 e - printf("socket...........9 @. U; d$ `2 w
- ");' g' \$ m9 m: Q4 C# V6 g; ]9 k
- 4 }9 K8 i9 e6 @5 o
- struct sockaddr_in srv_addr;7 Z! T- e; q6 T
- memset(&srv_addr, 0, sizeof(srv_addr));
* C# m6 }1 F7 y - srv_addr.sin_family = AF_INET;
: h2 W" q5 L3 u" `! M" x - srv_addr.sin_port = htons(8888);4 G2 Q: W/ P% {: V1 }
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");# |% ~, G6 l3 `$ ?& N
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
% K* W% v6 }# [; ~2 O3 P' A0 y. k - {
& _ H7 h9 _) m) k0 W( \ - perror("connect");
3 D$ Y+ L* ~1 @+ p) u- r - return -1; //exit //pthread_exit
/ ]( a# k& d/ L! V9 I# V - }
' ^- f2 q8 ]4 A5 y# z1 f3 y - printf("connect..............
# z+ q( R- O0 ?3 r1 F9 {. M - ");
4 ^) Y5 p) W& h. {# Y - char buf[100];6 L$ j2 ^2 w% f' a$ Y
- int ret;
: U4 X3 w) [; c+ x5 T - while (1), n; i+ b- Q/ C) F5 d2 ^' s
- {; P { O& G, V3 _" d7 C
- printf("send: ");1 r9 r- Q! x V/ `9 D( i
- fgets(buf, sizeof(buf), stdin);5 Q6 ]) m) F: v
- ret = write(sockfd, buf, sizeof(buf));! x3 @8 S! A* y( @3 e- Y6 x* T
- if (ret < 0), ] M% {4 a' w7 J3 ]$ Y. i
- {
+ I& x4 ~+ N, v( v. [2 h - perror("write");/ h& e7 ?7 J4 X Z7 _
- break;" ?( l9 c$ z- U! A" X9 \% a/ x$ f
- }" j# t( {6 |' F
- if (strncmp(buf, "quit", 4) == 0)9 a$ \# w4 v' a- b- O# o: }# D
- break;6 p+ u# ~) c1 j
- }3 l, m( Q2 ?3 W
- close(sockfd);
( i- p- u; q% D( [% v - return 0;& j# U# o; K6 b! S+ G& {1 o
- }
复制代码 - U) @9 e" L! d1 r
/ ], B1 Q5 y! c2 {' ~' b5 p
|
|