管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
. i! u, I( d, {4 c6 r: P: ^( V8 O S
2 n9 _% _* _, ^3 I9 L3 ^0 ^( k' [: S5 `9 y: L+ {% F% u4 O
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。3 ]& v R0 n2 S0 o! N. ?2 |; p( Q( x
7 o# y; m. ]3 ^
1 K9 |$ b. b+ J# H# q3 U, y$ b6 ^
TCP协议
j+ P2 { w6 ?( lTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
2 |3 n! E! l% h8 I4 z3 H% Q1 y
8 {+ ~! ^+ Q3 F1 Z1 h3 ~
& h6 P: ^$ s8 c关键词:三次握手,可靠,基于字节流。
6 X V7 r( `: a: q/ f
" j+ b G- k- |* q0 m
+ W: S( O6 V, ~' J1 b可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。& o$ P; C# t2 f. M- `4 K
. A0 {" Y& i% z) p7 q% R
TCP服务器端和客户端的运行流程
0 `" P' f3 d" |* F9 X
; T x; h/ _+ Y- Y* G4 L: U/ y" k. U. U
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
* }: n y; f' u/ w( g4 T) Y2 @' E% a1 e
( J( u& I( Z6 x- x* F8 Y. z/ `8 A1 d0 @( V
1.创建socket/ a9 m, G9 X A" N
socket是一个结构体,被创建在内核中! L8 ]1 Z# o0 s% {" ^- j
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
K. M) n/ O& E# C: d3 f& t
# v. \; s7 R/ {* a5 s
$ D" j$ O% R9 ~1 h2.调用bind函数
. v8 p* B' a3 W+ ] 将socket和地址(包括ip、port)绑定。# g1 w( I P) X( ?( @) @
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序" Y4 t7 L: k, L7 h
struct sockaddr_in myaddr; //地址结构体
5 D6 k/ h. M, i- p* }/ N1 i X bind函数/ [& d2 g, n$ w+ o! q9 ?: s* ?. F2 F8 j
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
* E' L6 E) Y+ ?0 v4 x& s: ?1 p9 Y. ]6 ?# `
: W2 O4 O4 R. K( h* K: b
3.listen监听,将接收到的客户端连接放入队列
7 M5 V' c, @6 b' o7 O listen(sockfd,8) //第二个参数是队列长度+ T8 U! d" e% u% r2 u* D: B
5 @5 P# R4 D7 a; E0 N" w% s
: p, A }- f% N8 ?8 X$ d4.调用accept函数,从队列获取请求,返回socket描 述符
0 |" F! X; n c+ `: k% E 如果无请求,将会阻塞,直到获得连接
- y- Z7 _* `$ R4 F8 N int fd=accept(sockfd, NULL,NULL);//这边采用默认参数1 m9 u: E i+ o5 H, z5 b
/ y3 u% K9 a8 t$ N# ]6 f( O/ j! V! A3 W- A8 r
5.调用read/write进行双向通信
4 j' X+ Q( N4 @. [, Q( n% R8 ^
. W9 T1 y) z; o8 S; j
6.关闭accept返回的socket
# _, B& e9 U6 E' {) k close(scokfd);3 M W- U5 p' Z5 A% S7 x/ v8 H
* l* y- F" F* z, H) u
. n: E, p" g" U2 Z: [0 g% P- Y" |
$ i' ]3 i8 w9 T0 I3 |1 e
下面放出完整代码. @3 m! X/ Z4 V
! i- p. F* |* k+ H- /*服务器*/9 [, z% t" A0 x/ U" r- c- O+ S, P
- #include <stdio.h>( Y* Z( K0 y* t2 W6 M1 W: a; f
- #include <string.h>" d6 u6 o+ u5 x' Q
- #include <stdlib.h>
( n2 i' ?) t d$ u2 R - #include <strings.h>
4 R5 S) B: Y, z: B$ v2 L) k% f" k - #include <sys/types.h>
% Q! b& J* d: P% p3 J+ d3 t/ n/ S - #include <sys/socket.h>
1 a! l2 O8 E/ }: v2 O+ [ - #include <arpa/inet.h>6 L6 I9 s9 J3 i! J9 a& M8 J
- #include <netinet/in.h>
- l) I/ C8 u/ W. n - int main()
$ n6 e J$ M3 K- D% I - {
' d9 z- n' @" |6 @4 Y - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
3 j$ ^+ O3 [( b6 d - if (sockfd < 0). A7 K& Y4 ] V' |. H; }, T
- {
* w: j4 s! ~" J1 I - perror("socket");% X9 m7 X7 x! t) `. ?+ Q
- return -1;, M) K2 G. R8 z) H5 W- ]' y
- } //创建失败的错误处理
" [* B( L3 Q4 Q% I7 T' @ - printf("socket..............7 Z9 I, y6 f/ ~+ i" ~
- "); //成功则打印“socket。。。。”6 Z \; U: U7 R: i* w
- ! R9 _0 P. o9 g# b. ]: G" f. s( W
- struct sockaddr_in myaddr; //创建“我的地址”结构体
- n; ^0 q6 L# {7 S, D - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)6 J2 T f, J1 R0 I. |+ W
- myaddr.sin_family = AF_INET; //选择IPV4地址类型* O; |5 Z; Q3 Q8 g' r7 }4 [/ ^
- myaddr.sin_port = htons(8888); //选择端口号
2 l2 H& D& Y. c+ m e" ?2 \ - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址7 [5 ?1 i y9 c( y
-
5 T$ s; S. e, L% E) D& \; o+ Z4 k# ?; k - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
' _. R7 j/ L, x8 ~ - {
" c; |) k6 N( c, ]6 \& U6 K# s$ i/ S - perror("bind");
& G9 k/ `5 v1 Q, c - return -1; B9 q3 V4 @# u
- }
8 f. Y/ K/ n# L - printf("bind..........6 a' d! O/ ^- [' [9 Y" g f
- ");
7 m: A' ~1 S1 b* {9 ]* s - . m: D: Y F. l" b
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听& o. @9 l, {# ~% d# a4 c0 M
- {
" V/ b; t" K& o5 P* V$ Q - perror("listen");/ d- s+ m% d- R" S7 }
- return -1;- D# P" b) G4 k+ J0 X T
- }3 {: E& A! N, H" W# f
- printf("listen............; ~" ]+ R, {5 I% c9 Q* ~& l' }
- ");* y% |; j' b& b& H. a& T, T
-
1 ]: E5 D Q: V$ n1 A - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求. S. v# c' `" w9 B% K$ c
- if (connfd < 0)! ^8 @* k( l' G2 z0 q u
- {
/ q. R% g2 O5 d4 J - perror("accept");. v1 g) y/ i8 j
- return -1;
- h8 p. Q8 @' ?3 B5 o+ ] - }
4 t" g6 s+ m. r* @ - printf("accept..............
" y0 N/ t4 {' X: V+ | - ");
7 a% [1 F" c3 @7 R - char buf[100];//定义一个数组用来存储接收到的数据: b; h5 [! f2 z: @9 h q% x
- int ret;
w# K6 R; S8 Z6 C6 B - while (1)
* t; G5 Q2 ]/ v - {* T7 Q4 D" c2 e' U. K
- memset(buf, 0, sizeof(buf));
8 `- g0 T o3 z j! m" C& _. u - ret = read(connfd, buf, sizeof(buf)); x( b, @" y* w. ~4 C. H) E
- if (0 > ret)
7 V( `+ s% d3 ~- X - {
3 D# l" T5 P+ A! N - perror("read");
; A5 u- g& F! ]0 o% _3 u3 L( [ - break;
/ u0 [" `& ^" |4 z - }//执行while循环读取数据,当- d; f# ]/ [! P( f) p
- else if (0 == ret) r2 Y! O. `/ x8 X2 [& I
- {
4 V3 r) S) a+ _& A9 A6 ~/ \2 a1 f0 Z - printf("write close!9 [! X9 Y, P1 W2 \2 h/ w+ B1 b
- ");
. ]0 N/ _1 h- T M7 l1 Y! u4 | - break;' \! H' n/ r' V, r2 W2 b# z* R
- }, G$ V# v9 h& f6 V6 r( G) o
- printf("recv: ");% J8 ?" q" Y: A% F- s
- fputs(buf, stdout);//打印接收到的数据 |7 i; R+ a. L4 f) c, o; f
- }
/ P" s7 f f$ a' t, u6 { - close(sockfd);//关闭套接字- ~+ H" T. O. u: K; O7 O0 E9 ^0 x; c
- close(connfd);//断开连接
" j1 {6 n L9 e# ~! [* F) N - return 0; F( V6 ?4 {4 `4 z* m
- }
复制代码
2 }2 n# A/ [* }( V B+ ~% u4 u
2 R5 \+ ?( w! E1 ?- /*客户端*/(具体功能和服务器一样,所以不再加注释)4 Z( h5 m) t; Q& _( |& Q% N' [
- #include <stdio.h>
. Q# X: Y4 P. ^; V; e - #include <string.h>5 f7 A4 _5 k1 o$ e
- #include <stdlib.h>; c% e1 E- {, K9 i$ U( @
- #include <strings.h>
4 m8 U+ v! @/ ~' o# |5 g5 @* }9 n8 s - #include <sys/types.h>2 a$ {2 x& d A
- #include <sys/socket.h>
9 j) T Q0 z6 b6 Y m, l" S% p - #include <netinet/in.h>/ M3 ]1 C: B6 q6 C
- #include <arpa/inet.h>& `1 a g- J$ e4 j
- int main()9 d1 p& {& {5 a* w( z" C$ T
- {
" X I9 j+ r) |1 D% N - int sockfd;
0 w* |9 B( y: }# { - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))$ h3 G- ^; R: b+ o( R5 g' S
- { _7 g x; Q" W7 M$ I0 u
- perror("socket");
$ D4 ^3 L7 |/ q+ x3 i7 s2 ^ - return -1;
9 i) d. H; t. ?2 F - }) ]0 f9 m% ?) b' I5 a
- printf("socket...........
* ?# @' K" d9 | - ");, y0 n! s9 o: m) h6 x2 h
-
- Y# B! f) O$ N9 W; p - struct sockaddr_in srv_addr;
. P, N& F! R5 t7 v* ^( C: r - memset(&srv_addr, 0, sizeof(srv_addr));& O) ~6 x$ f0 R2 R
- srv_addr.sin_family = AF_INET;, \) ^% N: F0 _1 C$ S: ] o
- srv_addr.sin_port = htons(8888);/ N; `5 k: S5 _. I) [0 V4 n6 f
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");' E6 f3 Z }/ S* n& `3 c
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))( r3 \( D* t; {4 O
- {0 t' ~, @# X9 B N Q1 n' V
- perror("connect");- J1 i9 T- @8 @5 a( y, ^
- return -1; //exit //pthread_exit- _" ]% [3 z7 ?
- }$ o+ Q2 u; F: j4 B
- printf("connect..............
6 B/ K$ a- O7 E+ m" m - ");
* n8 U0 i2 L e& L+ D; Y) i0 \; U6 Q" v6 J - char buf[100];; e& V$ `& ~3 E$ T0 g
- int ret;+ l' @' J% k! F4 K
- while (1)
% ~9 X5 |9 D. M: Z1 d3 @% r2 O0 Y9 ` - {
. t1 b, s- E2 X! r) ~ C0 m7 w - printf("send: ");
, x+ R/ q- @8 V# e. C - fgets(buf, sizeof(buf), stdin);: v) C% J# \+ W. K3 s* p9 l6 k* V$ [5 P
- ret = write(sockfd, buf, sizeof(buf));
+ } R4 j& L: n( Q+ \' I - if (ret < 0)
3 y% W# }! Z1 i$ o$ @& X# B* | - {! x! J# E4 {1 K" k4 q
- perror("write");' u! i0 ` d9 Z
- break;$ V" {4 x d; K' T) s) i6 S
- }
. o: R$ Y3 _+ E7 f8 e2 V - if (strncmp(buf, "quit", 4) == 0)- K2 E) p2 M G+ m% k
- break;
( _% @& p7 a% k' J5 q - }
) {- B) }9 I+ y) _* @9 e - close(sockfd);/ q) B" `$ }+ Y4 s. x9 y; f
- return 0;
2 D" q9 H& l" T - }
复制代码
7 |/ u! Z, A2 H$ X" A- L
# x$ w1 y( \. a" S |
|