管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。/ Q/ M" D7 a( A/ _5 Q* e+ P
' k7 c/ Y6 G. ^6 n$ ^) b5 x
' M6 F8 ~# b2 o l/ E! m. `socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
$ Q3 P) }# N; l& R
3 [) Z+ o. l' F# D5 g
1 v* ^) r F. R3 y1 q/ qTCP协议
+ {6 T$ {0 R8 E. ?* QTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
8 ? F' ~7 k& Y* U6 j* g: x& E4 L7 h8 a
2 Q8 u% S: w; d* _ W p关键词:三次握手,可靠,基于字节流。
! O. Q/ c( v5 d1 u* m( ~6 J5 |4 ]) C6 W8 U
1 j, D# I: o5 r; Z e% b4 | q; w- I, B可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。: ~5 A+ Q, a+ A3 |
) u ~9 _: U6 _' Q
TCP服务器端和客户端的运行流程' j2 l4 B: p! z& L3 l% W
) P+ h- q: |, _3 }0 m# _2 o
. y& c3 i* J" M& i1 ~2 @' t4 `
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?2 v) J& Q& G O. W
& ]2 b& `1 c+ \7 W$ h# O8 S3 F& N+ b; A0 _8 I
1.创建socket
4 f% x( ^; X' _ X% G t7 f; V9 Y socket是一个结构体,被创建在内核中
5 F, }0 }2 }& l' O# } sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
5 v, a9 n; ], Y2 d4 [
4 {/ c7 ]( j* b5 [% |8 N* j$ q: X4 P/ G7 p! }
2.调用bind函数
& o7 Z& q$ u" B8 [ F 将socket和地址(包括ip、port)绑定。
]* M3 t# O' b# q 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
: L$ W8 \. v+ @9 _' A struct sockaddr_in myaddr; //地址结构体
' b" ~4 \ D. g) F bind函数
1 M+ Z0 X0 r2 L( T! k. T v bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
% L7 O1 ^1 E; n5 \! I7 y
/ O; m# j* T$ k
; _& I) y+ F8 E+ b! S3.listen监听,将接收到的客户端连接放入队列: @9 U. W8 k1 \- G
listen(sockfd,8) //第二个参数是队列长度: E0 Q: n, X3 \2 g
0 J3 s) H! x; `8 v" J A
' F1 t3 t; @3 k" \, R B7 l
4.调用accept函数,从队列获取请求,返回socket描 述符
) D6 l! `5 X' f q/ ]! p2 ?2 w/ w 如果无请求,将会阻塞,直到获得连接
- L0 {: Z. X: ]$ K int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
2 o2 d% U, l, h% w+ V1 `* }6 _' u3 v6 n3 s% q6 `' h5 h
% ^1 \7 x: k& v2 p
5.调用read/write进行双向通信4 |" t4 X8 o/ a! t1 E2 F$ R
$ f% K I2 D$ r3 `
3 Y* V3 L8 m7 ], i$ c) a; T: s* I6.关闭accept返回的socket4 \ o7 E% N8 u6 I; O; ?8 O+ r
close(scokfd);- ^: _$ N7 A4 G) N8 `
! n& W% \5 S9 y9 a. c! i
' N7 _- b8 |( ?1 G/ ~9 `+ g2 V, T, ?' h" a" v- F
4 K5 W. x. O2 \7 e$ f
下面放出完整代码
- C/ _( f' a4 X0 Z( h! u
$ R' G4 r8 c9 J9 S, L M% t- /*服务器*/5 S: }( l+ x1 n" K: f F. q
- #include <stdio.h>& M" Z, R+ j0 p
- #include <string.h>. o( s# b8 g0 Z. _5 i: \
- #include <stdlib.h>" A3 t6 o; g( p# D4 ~' a, n
- #include <strings.h>
- f; I: ~/ w% S* y' x% e; l - #include <sys/types.h>
# O7 ]6 V- C8 V8 E1 R( H - #include <sys/socket.h>; J7 l/ a: n' A5 h$ c
- #include <arpa/inet.h># T8 S6 ~0 a4 k9 u3 \0 l" J$ V0 K; |
- #include <netinet/in.h>3 W! |) j1 v4 O! Z) N
- int main(); p: Z1 P0 L: g% y( }0 ~8 X
- {
* C' M6 D% t: B) Z - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字) ^: ^0 C- G6 T8 k& w
- if (sockfd < 0)
6 L( ^* C$ N- m5 f - {; n: a: w O$ C7 m) ?9 a
- perror("socket");# y# L2 Z( `$ X! C3 i
- return -1;
/ }, A) V8 }( j, v. i$ Z' B9 z - } //创建失败的错误处理8 N& ^$ N! l, c( k) u3 L
- printf("socket..............
# m( t# a. L f& p5 }5 _7 K - "); //成功则打印“socket。。。。”, x5 e% D8 S5 ]" W' i% t, F
- m! \/ X6 K; I) m" ]
- struct sockaddr_in myaddr; //创建“我的地址”结构体
" c& C; L% M8 ~, s) K H* \ - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)) A/ N4 W9 f7 V3 U& r
- myaddr.sin_family = AF_INET; //选择IPV4地址类型! k) i! x4 `" `( h p( D
- myaddr.sin_port = htons(8888); //选择端口号' l( \, P+ P; o
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址, L( l( G) B) [6 }' ^% p, g
- 4 s. ]8 A* N5 Q2 U; d
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
. f: u5 D$ i" t* X - {
7 P$ y. W+ @# I+ r - perror("bind");0 p, Z9 D' @2 E# B
- return -1;
( z8 ^5 ^- g& p. B- B( i8 M - }
' y" g% x9 }* E6 W - printf("bind..........
7 T6 ?9 ^0 b9 f3 g! c" q, S D - ");% W$ a' ]( h4 S% O U4 C, I
- 7 }9 O' P! t( x# F! y+ i
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
& _+ r& K) X- Y - {
3 [; z; l5 D/ o& O2 X! L - perror("listen");, O4 T7 v. D% Q, i3 u. Q
- return -1;: t( v/ A4 l' d$ c/ u. I8 u% V
- }4 e5 P' O7 H3 c( F( g# Z
- printf("listen............
8 a6 b3 J* W) V X6 } - ");
4 \+ k t4 o @% F! P7 ^1 Y# H - : e+ E6 d0 j4 D
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求. _: [1 [5 ~+ f
- if (connfd < 0)6 P- ~) P6 `) e- h5 [: y9 c
- {
7 c/ h2 M/ a) }. G! H, n6 m - perror("accept");( a% A8 r z' X6 n. O2 Q( o8 Q
- return -1;2 p1 C* o" M/ w6 @
- }
& j# R2 `# _) X B# p9 } - printf("accept..............5 ]' |/ ]* f% U/ A$ P1 A$ V( L. e
- ");
9 Y( `. [7 G0 y - char buf[100];//定义一个数组用来存储接收到的数据
4 y) T' O8 y; B0 j - int ret;
' v" i: g) n7 W/ ` - while (1)
% o5 a3 L' d) [ - {1 K5 K- X. Z0 }5 y7 s
- memset(buf, 0, sizeof(buf));
; r2 i- g1 B7 k; M4 D8 k - ret = read(connfd, buf, sizeof(buf));" L' k$ R9 _ i8 `
- if (0 > ret)
. J3 U8 i6 }! W$ r3 R6 Y - {! _* C/ [* L _
- perror("read");6 n6 W$ Y6 ^$ N8 k
- break;3 I0 R. z8 v9 H% {
- }//执行while循环读取数据,当/ X( R' y2 q) V
- else if (0 == ret)
$ o# D/ l1 }' @) J+ F' z5 [/ o - {1 `! k5 y$ k6 I+ G
- printf("write close!9 ^# y1 f, A6 Y0 _# w9 w& c
- ");
! J5 \* \; D( B3 q" p1 c8 V - break;
- y; c c. ?5 k# ` - }
x# G: K$ x2 D9 l v - printf("recv: ");
7 n- X7 V! R7 p. f; S1 }0 k - fputs(buf, stdout);//打印接收到的数据3 Y3 D! M6 z) p4 y: c0 r2 w* }
- }$ }& s8 c% T) K- Z
- close(sockfd);//关闭套接字
! u8 X7 S* z4 ~0 S - close(connfd);//断开连接' C$ z( N0 O& A* Z
- return 0;, F7 u9 w# U; y1 k5 ?7 H- g
- }
复制代码
: ^& Y4 s; _5 m1 C" _; g
& _% `2 F+ u6 l- /*客户端*/(具体功能和服务器一样,所以不再加注释)4 ]# }! B; q* j. m6 i
- #include <stdio.h>
, V* v- b" l3 p2 F; o2 }4 m2 B - #include <string.h>
: n C# J' U, Y: u; B - #include <stdlib.h>) s, ?6 `' D* e) ]' n' f- F T
- #include <strings.h>' G0 O: d7 Z. o/ a
- #include <sys/types.h>
& Q; [2 c# M3 x/ i - #include <sys/socket.h>
+ Z* T; k/ W( D5 J+ q1 Z7 z/ { - #include <netinet/in.h>: C4 h# O$ X# Y! T/ H
- #include <arpa/inet.h>
A. u6 \+ g7 V* J2 ]1 o. u - int main()9 r1 E( I' y7 M) c
- {
$ y. s8 a/ D3 c# A9 f - int sockfd;: N+ t) u E( b! }0 ?/ U
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))1 q$ {8 B8 D1 g/ n% P
- {; `. {. Q0 {) o1 [" \
- perror("socket");
+ P' U7 P0 N8 o. m2 p4 i, V1 { - return -1;
- `2 R% P1 C3 ?; `* q- n2 B. m3 ] - }& T* Z" { u4 z' ~4 T, K
- printf("socket...........
1 s" e, F# T8 J - ");) m2 i8 z' Q2 [$ _. A- @
- * Z7 j T' W( b6 j- O0 S
- struct sockaddr_in srv_addr;2 S/ z7 F9 |4 K) M
- memset(&srv_addr, 0, sizeof(srv_addr));
/ Z1 ]! ]$ ], x* P; j7 V - srv_addr.sin_family = AF_INET;
2 p6 q0 x, O2 X/ X - srv_addr.sin_port = htons(8888);% k7 X0 {: S8 U/ r7 h+ T( w
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
! s, p# W& @2 m& g. @( a, L( h - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
, y- @# c/ X6 Z+ l# z% j+ E - {& {( v* m o0 r( @( h9 Z1 y' X
- perror("connect");6 ^4 d9 @% U5 m2 G- E5 D
- return -1; //exit //pthread_exit5 [% ^ g/ y/ a
- }2 {8 b! r3 |0 h7 Q7 ^7 S5 d
- printf("connect..............& Q* t+ v# L' N/ m- q* D
- ");
2 M& K1 H+ `- p: ^2 D2 ~ - char buf[100];
% S; f* w% a5 m% x* G- U; d, @ - int ret;
# k! \; m) z& W* @ - while (1)# w% M- `: Y! N+ l; L
- {
( |# d! @1 v4 r: V9 O1 J2 L1 ] - printf("send: ");
+ P' i9 X* O2 m9 J. l7 c9 l - fgets(buf, sizeof(buf), stdin);
/ s; m) u1 S7 v2 c& U - ret = write(sockfd, buf, sizeof(buf));
) v$ \8 a( P$ n" f. S4 C9 _ - if (ret < 0); ~. p; S5 W4 z1 P9 n- ~# Z
- {# X4 o) @. F! L; Q* N9 l, E; q
- perror("write");
; E7 U0 c! g( F - break;
/ I; p2 d& C* ^( S - }
- R! z3 n3 h& U! t; [& ] - if (strncmp(buf, "quit", 4) == 0)6 p4 A+ F6 G( B% B- u4 F0 R
- break;
* n) {3 K, t5 W; n. N - }* z6 z( G, h& d2 P; a
- close(sockfd);
+ {& ~* V" P3 F* x: l4 t - return 0;, p$ P' j3 n& w8 q" \( D3 F
- }
复制代码
: N% i4 }6 ]! i0 T+ Y
' o. \# Y! F) ]+ Y. }# j |
|