管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
* \+ B3 u6 i, d1 g
; J# C# S. d/ @8 x; G; P
/ s8 R' |1 Q; X! Q3 V$ t# Q; qsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
8 \- v& c; W8 i! J) C: b: i& _4 Q) S# b0 K1 C* L
6 _8 P2 |" C& X* p
TCP协议( _4 A: E1 S- {8 e* R1 p- L0 m% F
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
( ~( @7 E+ f. |& U# l% C
3 l% n; P+ \9 F+ ~! h1 ]6 E1 b0 O$ J. C* u* I
关键词:三次握手,可靠,基于字节流。
3 b" y, O W5 A$ g! N! Q" Z$ c2 Y1 B9 C2 K: T6 o3 M7 c
5 u6 o, { |) M# k E% Y" x可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。" Q; s T0 o; y8 l) h* F: Y
* D2 P5 }; I' f# T: M" zTCP服务器端和客户端的运行流程
' c/ S0 R7 [9 B( T( W6 _
% I" c7 Y* O# u
8 g7 }, W2 l# L如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?- h4 I) }6 _( U( [8 G
2 _7 Y( x# \7 P
| F4 o+ m, ]8 a. ~2 ~/ Q1.创建socket6 P' A) `: V6 @$ J! @' O
socket是一个结构体,被创建在内核中! B k( h9 K% y9 X5 z
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
' r0 R* t4 z* a" @4 j5 X6 i
$ C. W8 y6 c, ^% X: t3 e, {& H) n, g( j
2.调用bind函数
; x. y8 I$ N4 Y }8 d' S6 Q( ^ 将socket和地址(包括ip、port)绑定。
8 ^! y* ~; w, R" i0 M 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
1 y7 E9 F* X8 a4 e struct sockaddr_in myaddr; //地址结构体
! g6 j' ]. A( y0 Z6 d h/ C8 J+ i& _ bind函数
3 a3 b7 k7 [$ s8 [9 m! \% V bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
; z- I# e" q7 J' ?+ T* \# V' `/ S5 I# B7 `0 j* [! Q7 Y( x$ E
( w- w: A+ `& v3.listen监听,将接收到的客户端连接放入队列5 W9 U& P2 ^( e& \
listen(sockfd,8) //第二个参数是队列长度
. l9 {, ]' [ H2 j1 \
& F- V! H9 r+ w2 s6 E2 z" l+ I" [0 S' H
4.调用accept函数,从队列获取请求,返回socket描 述符! M% w" d" B8 ^% x; A
如果无请求,将会阻塞,直到获得连接% ?( n0 b7 l# x( d0 d+ t) u
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
8 w9 j+ y' j6 z. B4 ]/ q3 {% ?
5 v: R: ^2 |1 N2 Z# H- k: T: L. E- y" j" a `7 S7 K9 _$ t
5.调用read/write进行双向通信 M x3 ~" x; Q' ^- m
4 }# D1 a5 x7 q7 I* E/ f
! f1 Z1 ~* A5 A) q. A. l
6.关闭accept返回的socket
! ?4 D: d% L) e7 i5 D" r% O$ B5 c close(scokfd);; f. R; e/ g& e# [0 H* z4 d
. k5 y+ e* W( U; Y: Z9 u2 u! Z a3 |+ d* Z! C
0 M2 [- g& M% \
: @# t: e+ f6 T- { W J下面放出完整代码
" K) L. V2 j5 D, M8 w" F. k9 {: O3 X5 C) D; [. J
- /*服务器*/# T' A; V2 O) \5 v( z0 W
- #include <stdio.h>+ L. C. g4 }9 `3 T+ H
- #include <string.h>
8 T1 Z7 {6 q+ f. n4 P - #include <stdlib.h>
9 j' v/ R/ n# Q' E- ~* ?) j - #include <strings.h>: ]. z3 N% n% _
- #include <sys/types.h>
4 K5 v) Y! Z- l$ c7 _ - #include <sys/socket.h>9 i# Q1 h/ b$ Q6 ^
- #include <arpa/inet.h>% _" k1 S+ j! n! `9 {% g, Z2 X
- #include <netinet/in.h>
. B# k; ?$ z/ [- z S4 P - int main()7 ~3 X0 S9 l1 f* m, Q! N5 ?, B+ g! H2 C
- {
* H) J* a+ u- a5 k, Y - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字 D# X1 S. X4 U4 {" @: l
- if (sockfd < 0)6 k/ ?* R% ?* z- k
- {
+ q$ ?7 d# ~# I9 M i) N - perror("socket");
4 D/ s. y( R6 p* e$ u* h/ L. W - return -1;
4 d& V/ n. N) F4 t/ r - } //创建失败的错误处理
- N: W/ `5 g/ s, |5 R3 w B' x - printf("socket..............: i' |" E) ]6 r8 P" f" h( G
- "); //成功则打印“socket。。。。”
. Q! I0 y1 t7 } A -
7 H# A' h6 W" S# x - struct sockaddr_in myaddr; //创建“我的地址”结构体. s* y$ u2 t9 S) S: k" C5 e: o
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
! I0 g: k2 X- K7 n) [$ ] - myaddr.sin_family = AF_INET; //选择IPV4地址类型
8 }4 ]# t/ C! Y: r3 N; s0 @ - myaddr.sin_port = htons(8888); //选择端口号, _4 s- f0 b% f3 F. W0 t$ D! g& \
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址' o! _0 m& Q7 x& u% m5 F
-
# }$ `; O1 i/ J; Z, N - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字& c* F9 \7 X) w4 s
- {+ f) y. u; ?2 t: Z
- perror("bind");
+ \9 U" J5 {& c+ S, M7 H# r - return -1;
% c+ S/ J1 j" J- \9 K* C, i3 C# D - }" |$ k4 j+ S4 f+ [2 E* }
- printf("bind..........: x! N7 ^" u% p; h
- ");2 y- C) F0 s1 C. L1 q) f9 r
-
) L; p9 n4 W+ d4 w" g - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听% x, g: S. N/ V% _& w8 s
- {
& l3 Z0 v( ?* A) _; B1 I7 Z6 y - perror("listen"); @2 x% M0 v* Q, ]
- return -1;
* _ E! a/ c. |8 R) j - } ^* w( d6 H9 s. Y# n& r5 A G# k
- printf("listen............
; Z: x9 [2 o3 [* |4 ^ - ");, `# G9 C- [0 ~, {
-
4 v% R' X2 X8 S& I) X - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求: ~# ?$ c) x* {5 [' @$ |
- if (connfd < 0)1 P: [" f* S% k- x2 y- h/ c; l4 @
- {
" Y% Z E/ {4 h, ? - perror("accept");, } T$ `7 x; ^6 _# g/ i1 T
- return -1;1 c7 p# L2 ~6 D3 H( w
- }8 e+ {% O/ G; W" k
- printf("accept..............
( |/ O: O/ W( V' G* J - ");4 G# R4 K3 [2 ?$ @; E |3 Y; W
- char buf[100];//定义一个数组用来存储接收到的数据
6 K9 p- G# V% F% S" W c - int ret;
+ J; @" Y0 Z$ [; `/ }( g8 f2 s - while (1)
) ~# n8 U1 Q* P1 p$ S3 h8 [ - {
" s$ Z! h. R0 z - memset(buf, 0, sizeof(buf)); D/ @7 F7 M4 x" h5 l @; F n
- ret = read(connfd, buf, sizeof(buf));& Y- {& Y! S0 ^
- if (0 > ret)- X( R& b" ^- G% A
- {+ n& c' a" t, _7 T5 R7 {1 V0 z6 x
- perror("read");
' j+ \) O& E+ q; [# x7 T. _! B7 I; M - break;
. {9 u: r( S0 i; l% Q - }//执行while循环读取数据,当% A) W+ y( ?& c7 C8 @
- else if (0 == ret)
8 }* z) q* ]' N - {
0 L: A) X5 Z& p% A4 E ] - printf("write close!
2 O; t) t' a$ R - ");
0 g8 K; V$ F( i - break;
- x( \7 d$ M* p# a; ?7 C ?9 ^" [ - }
* ?0 V1 l3 A' N" _; K1 V - printf("recv: ");2 P! I: [3 G. A6 f/ j8 ?0 Y
- fputs(buf, stdout);//打印接收到的数据6 r' m9 r8 N& ?+ }& m C
- }+ d9 a- e4 k$ F: _9 o) c8 f
- close(sockfd);//关闭套接字6 Y1 d, n% Q) M d* ~
- close(connfd);//断开连接
* `. m/ Z7 E( s" S - return 0;
% b& I' X+ |: q" L - }
复制代码
. }! \, s( N5 [& S' g0 @; u- M+ K9 S H# Z
- /*客户端*/(具体功能和服务器一样,所以不再加注释); F2 e% A/ C8 F/ y; e- r
- #include <stdio.h>+ |1 T K3 G2 `' [
- #include <string.h>
, ^3 q- N& D. W - #include <stdlib.h>
: l! N& v6 O) b/ K: N; a - #include <strings.h>
/ R, t) N: g! c' ~ - #include <sys/types.h>9 N; I% t% X) d% M$ Z! L8 ]7 Q; d1 k
- #include <sys/socket.h>. h$ |1 f/ d5 E: E7 ]8 j
- #include <netinet/in.h>2 b0 r# P8 X- f r
- #include <arpa/inet.h>
% E$ u- v% Z( s F2 a2 K - int main()
( R% O7 [, \0 q' L: C& F - {' R4 b4 a, I* D9 x: s* B
- int sockfd;) B0 S) Q5 t% m% ]1 K! Z5 D5 |' Y
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))- | h) M4 R% W/ m2 `8 e4 g
- {
0 }9 L/ q3 U& ^* M( g - perror("socket");- w7 z2 e, o0 j4 Y# j9 K" w
- return -1;
$ |; m M. O0 I3 ]0 H - }5 U# A: { a7 K. U
- printf("socket...........0 [* t: m4 q( _( H
- ");# ~% Z e% ?/ e" Q
-
; z" S6 ]% E& x/ g5 C, w - struct sockaddr_in srv_addr;
$ k3 _1 F# z2 J/ R - memset(&srv_addr, 0, sizeof(srv_addr));9 _" X% b8 [2 R8 \2 A6 ^
- srv_addr.sin_family = AF_INET;
+ A3 T9 k: C( \2 t9 x. W - srv_addr.sin_port = htons(8888);4 ^7 a) Y( S1 Z( l' s6 K
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");/ Q1 I0 m/ ?* P6 X
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))). ~6 g3 y3 M [6 P- ]
- {( m9 v( I# [- x% {
- perror("connect");
+ ?9 D" C5 ]/ _) B1 X$ `3 G) Z! T - return -1; //exit //pthread_exit4 d! C3 m8 l3 k* W
- }
5 Q8 i; M3 R6 Q6 M8 J - printf("connect..............% Q6 s1 e: P2 _. v
- ");2 h4 B) s+ |$ Z% i- s/ G
- char buf[100];
( r" z7 B* Q- ~' b0 L - int ret;+ x: V3 `) i8 u0 S1 t
- while (1)+ w' r5 E; m% o" U( V
- {" E O* o& _1 S$ C5 F" j
- printf("send: ");0 @) J% }+ d& Z
- fgets(buf, sizeof(buf), stdin);; V8 l M# t1 O& m: a
- ret = write(sockfd, buf, sizeof(buf));
& I1 U6 t5 A7 b6 E - if (ret < 0)1 B: y/ g. B$ y4 Y/ D( ~
- {8 |$ W8 f8 s) n# w8 Y. M) V+ D
- perror("write");
T( G6 {( |, c+ B ?3 k) p - break;6 t0 q6 d7 H/ y# ~7 L5 {; K
- }* a% Z& X2 }- o# X7 C9 z
- if (strncmp(buf, "quit", 4) == 0) W' `( Q- z# Z. J* [4 }
- break;
( q v0 P% K( t; Q4 r - }
1 v; e6 N# ]. v4 F5 ~% d4 f - close(sockfd);/ N$ H. i5 g7 u9 v/ D' R
- return 0;
9 v$ e, E( ?4 q - }
复制代码 8 q/ S3 a/ ~! U* E3 w1 ^ \
# y4 Y* ~& a7 H2 p) r& V |
|