管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
4 H7 N7 N8 F9 _$ Q0 R1 R$ s2 H6 V( y3 O( J
1 C) O+ |" Q+ G: R& T' O
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。5 x& i) |. N* ]8 o' H" ]
2 L: X' y0 H0 v& r, \* H
/ C b/ V7 ~; E1 TTCP协议
9 |6 i: H" t; F2 l& rTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。) |+ c" c! W9 W
! d& L+ H' e% \$ s+ O6 C7 o/ `
% s+ {4 S" Q# r; t
关键词:三次握手,可靠,基于字节流。6 z) x" c& e) p9 i# D' b; h) z
5 N: i: K' G' F- C. M
- E+ K6 y) j( A& ^. ]可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
+ q4 A/ l9 F/ I/ n2 D% y3 D
4 Y: N0 d" K1 F% \3 zTCP服务器端和客户端的运行流程" h! I0 F. D1 I2 p; t
8 t( x4 X" d" B# d `) P
- B& x' r% X; `" ~1 [6 y+ B' A如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢? W3 S z& N8 M% [2 H4 }4 B1 M
+ T/ g# U; t7 ^$ C8 u! i# A
0 Q. N& P% k" @8 v1.创建socket. D9 m" Z- }" `# A" |5 S( E
socket是一个结构体,被创建在内核中
1 e: }# s: e. v3 t. x sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议9 V5 E( I+ u( i
: D& [1 g) F1 l# A9 q$ I f M' Q& {$ X M3 {1 t6 A& M4 b) ^
2.调用bind函数
3 K' S- e ~0 s 将socket和地址(包括ip、port)绑定。
& D0 A9 v" N6 {, l' L, k; Z0 I2 j 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序% |+ e; n* m6 k
struct sockaddr_in myaddr; //地址结构体2 n4 `* N, k; b& U% T
bind函数% X6 Y" F3 k7 b6 j: I! s
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
: j/ L( \! \7 a7 l+ `2 q# S& a H' L" ~" a( e6 h
2 h: k; p8 K0 b. x( ]9 Q# E9 V; ~3.listen监听,将接收到的客户端连接放入队列
7 N+ V0 _" R( i$ d& O listen(sockfd,8) //第二个参数是队列长度- l3 @3 f7 Y8 P2 X. j9 P/ c: B p; t% i
7 \" _- }8 w4 T& Y8 T# u2 k1 U# v7 U
4.调用accept函数,从队列获取请求,返回socket描 述符! @; i4 i( K; l# @$ z
如果无请求,将会阻塞,直到获得连接- T1 s. V: W( ^! k2 f |- K, F1 m4 `
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数: [6 N# y( Y! ^
. u4 W5 h- L4 ~
, d" m( Q# q+ N h: ?5.调用read/write进行双向通信& I. C5 k: l$ P) X/ N( f
) M2 n, w L$ f3 T% g7 C# H
! P0 ]. H K4 x2 @8 P6.关闭accept返回的socket2 ]5 c- }, j( Q" I- N- s/ [2 O# o
close(scokfd);$ z1 G3 H% T7 l: L" Y3 [
7 d' @9 u; ^, C( ^2 f' [. Z
. e. g( x. M1 F; t
# o" B. z0 Q/ b8 C% @8 ~$ D' e. `" F2 v- b _% x6 w% K& q' p
下面放出完整代码
9 I9 {8 ? x. T4 v5 h2 ~7 p" Y. z: C; w
0 x4 ]4 o# [! ?9 j& ~5 @# U, D- /*服务器*/: s+ b( Z7 p7 z* y& N4 o! _
- #include <stdio.h>, x# d! f7 q l
- #include <string.h>
4 @2 X/ L% w9 z2 ?$ `, i; @ - #include <stdlib.h>* R0 ?' W$ M$ x' B* w0 |
- #include <strings.h>
9 q- P; v& _! P4 l - #include <sys/types.h>9 x: X' s8 P( B# B% r" _% \6 ?
- #include <sys/socket.h>
9 @' J8 }6 J( \7 F% }2 h - #include <arpa/inet.h>* n" m* t% R2 x; o' }
- #include <netinet/in.h>& ]9 U% \! R& p( z/ \; O! n8 }
- int main()& @" I1 [. Z; v( |0 o
- {
) _5 [4 U( A+ g, \$ Y) @ - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
[8 x: P2 e6 {/ \; M) q - if (sockfd < 0)
0 _) R+ n/ ~5 r; h' G, n2 k - {9 q4 A/ m+ w+ G3 @& E* D
- perror("socket");
1 o3 A0 C4 e M0 F# D7 K - return -1;! I3 v) @, }9 ^
- } //创建失败的错误处理
2 E. H5 e! o* ?# v6 g( D - printf("socket..............& A! s) s* i: E
- "); //成功则打印“socket。。。。”% J Y2 k1 L; T6 L( s- w) ?( g- X
- $ r0 ~; v; ^, W* B! U
- struct sockaddr_in myaddr; //创建“我的地址”结构体
2 z/ a/ w3 \; R8 \$ U9 l* S - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)2 |3 X! o+ V" B/ G0 S, v
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
0 P" Z# p( R/ S' V0 Q+ x! ~9 N - myaddr.sin_port = htons(8888); //选择端口号9 [ _2 P' m$ ]/ d
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址% V; Q" Y% B0 _7 X9 J
- / I- n2 t2 L6 D( {3 r7 S! c6 F* R
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
* }; p' j& M. d - {8 C2 F4 q/ H4 T) g- M# t
- perror("bind");8 t% P5 E" ^: h$ _
- return -1;, x! ~8 k+ h+ O# c& |% t% I
- }0 A8 M5 _! b- h$ J$ a
- printf("bind..........# p: P3 T! z0 A( k; C
- ");! H+ `! W8 S4 J' M; }6 v1 p
- 5 E7 \! c# E1 r! V* m$ M0 V
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
: m, t6 A! B1 l8 U - {
1 E y! z _9 U! q - perror("listen");/ ?( a9 w. x" H( N. C& s
- return -1;
/ g- D/ ]/ C* I8 V% x3 g/ w - }
9 c# r8 w; o: q: A - printf("listen............
1 h2 r2 c+ u6 T5 o: x% U% q( `+ y - ");% c! ]4 U& q! G; Q" _
-
5 F0 {- x0 Y/ } - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求( _) {9 z! L. Q9 s" T
- if (connfd < 0)
$ V/ E; H# } g - {# X0 ]4 p0 K" K# V$ H' |1 `0 ~
- perror("accept");3 \' F3 g: b0 y) \# r- }4 X, @
- return -1;
# x" N5 `5 l6 M - }
( O. B+ j" a7 r0 M7 v9 V - printf("accept..............0 e5 k0 @3 X* S3 x0 M
- ");% Q- A! i$ |8 X( H( ^! P7 j1 A8 X
- char buf[100];//定义一个数组用来存储接收到的数据
" b/ O9 @+ T, h - int ret;
8 t8 }1 D- W* @ - while (1)2 O" L6 G8 M- M5 r' l# o
- {
3 G3 D9 u6 F1 p - memset(buf, 0, sizeof(buf));1 }: M2 q" ]% p, K4 h$ N! X
- ret = read(connfd, buf, sizeof(buf));- s. ~9 W( E* @
- if (0 > ret)+ c. H# d- f( ]# v$ F$ D
- {
& @0 t+ C* w5 O2 @6 Q9 I% T - perror("read");& Z; x8 r( n) a2 \$ c
- break;
/ M% j6 `9 C/ V - }//执行while循环读取数据,当. S1 y) [3 ^- c
- else if (0 == ret)
# }; I" m7 D7 ~ - {. U# c; W- `: u
- printf("write close!; u9 j, I, h& M% s9 x
- ");
9 t F$ Y0 `" G8 X' c$ S - break;
& y! U: I: j, G# X3 g& U: ^. _ - }
6 a! r3 l5 l' g! f4 n# m4 t U - printf("recv: ");5 L9 F& ?7 X' Z# }; S3 P
- fputs(buf, stdout);//打印接收到的数据
: F+ Q, E( ?9 q2 Q. U# M - }
( N& @) d( }9 p# ~1 p - close(sockfd);//关闭套接字7 F2 @ n2 M8 J
- close(connfd);//断开连接( O9 V6 m! e/ l! L
- return 0;% {1 ]! N2 S) g6 F/ ~
- }
复制代码 2 |- Z% O4 `- G7 D2 a( l
$ v8 J& r, J: K' [- /*客户端*/(具体功能和服务器一样,所以不再加注释); \2 u# r4 q6 d, P* g
- #include <stdio.h>
3 b" G" l2 ~! T5 `6 L3 Y - #include <string.h> g/ n6 W4 o' T, e* a
- #include <stdlib.h>, x0 R2 k* }2 S8 ^
- #include <strings.h>
4 b3 v9 S8 F- r \0 D: U" b9 I4 i - #include <sys/types.h>, v- M3 B6 z) [! d0 \
- #include <sys/socket.h>! T$ x" x* |0 w4 [; x( ~( M
- #include <netinet/in.h>5 w+ ~& o) G/ {5 t5 u f" |
- #include <arpa/inet.h>2 o# Z& |% [5 H! z: a
- int main()6 t: r5 g: I( w W
- {
: ]0 x( }% `3 ] - int sockfd;
+ o$ w$ ~: X4 Y/ ]* x! w: S+ r6 m - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))( f* Q( R$ V/ U, m1 U- P
- {2 Y# i) a3 t. |4 p" D4 a+ o
- perror("socket");
' p8 w" D1 {8 w( w - return -1;. _% ?; ?) F" O. e+ _. l# z% F
- }
9 [, M- m( F8 Y# X$ K" f - printf("socket...........
% D4 U7 I4 d: r/ ~ - ");; L8 m' Q4 w, Z
- ( _( }$ p4 T( Z8 x {- R
- struct sockaddr_in srv_addr;* e4 T) m2 a5 b% C
- memset(&srv_addr, 0, sizeof(srv_addr));$ \) h9 t5 K3 W2 z$ S
- srv_addr.sin_family = AF_INET;
% }2 f5 S- K9 J0 ~( O0 @ - srv_addr.sin_port = htons(8888);5 l! D; m6 G3 A0 b; \1 F# U
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");) P* v( W) {: [
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))% M0 `: G5 s4 f$ Q, l7 Y/ M# e4 b
- {
" e! j, M P" n7 V6 a l& C0 v9 l- Z- x - perror("connect");
. C8 W' a+ ]; \ - return -1; //exit //pthread_exit: y4 l7 y7 N$ b+ m) ?0 j+ U! l' |# V
- }9 Z' p) ]7 J! t: R9 c S
- printf("connect..............
5 w, t. B9 R5 ~) [+ m - ");, a, B; V- q; [
- char buf[100];2 F u$ C" s7 c$ P" E0 f( \8 ~
- int ret;
; V( D& ~4 ]# K$ `. K - while (1). @) j3 Q7 {7 r
- {
* ?3 U0 ]/ E4 G2 R) n9 N+ I1 A3 B; Q4 x - printf("send: ");
- {6 ?" Z p+ z6 X. F+ m6 V+ b - fgets(buf, sizeof(buf), stdin);
5 L# ]9 N9 R* f- t - ret = write(sockfd, buf, sizeof(buf));
3 v) f( K8 Z% C- ~6 S, p - if (ret < 0)
/ ?- S1 @9 W* X" B3 k - {
, w/ Y, H3 y; U5 i7 l+ A) J; d - perror("write");* Z Y, e! P5 @. d6 Y
- break;% f" F+ i7 ~6 \3 ^: `% G" l/ v
- }% Y c( A! T& _, F. h3 ^
- if (strncmp(buf, "quit", 4) == 0)
1 |+ ?# l* y& u# H - break;
+ \( b9 a5 @2 V3 l% X9 U - }# U& p M0 i" u# K
- close(sockfd);: q1 W6 R" V5 L9 B' h
- return 0;/ f* k1 a9 T! f
- }
复制代码
3 d2 t9 b( w& a! n. |" J; {
* |! G- n: {& q+ }% A8 F |
|