管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
+ }! T# \" h9 ^6 A. B; y* ` ^1 F& Z6 @' _
7 K/ W1 v2 P% ?6 o5 n
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
' P s2 V2 ] q. p" \8 I9 |2 N* v: K3 }! L6 K
& T2 s) l% B+ \" L
TCP协议( F/ L7 S& f( s+ @
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。; h- `0 W0 |; ^2 O- k
& Q3 M2 P0 I# W' y2 Z; A
: R1 x }/ f+ n5 @5 ^4 J8 _关键词:三次握手,可靠,基于字节流。
/ `$ N. M: W0 F* O1 ]8 K( l3 h9 R2 u' [
) K2 Z5 v, {! [5 T8 I& w可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。6 X& d0 F+ T( U" a
+ e; k( @3 o5 Y$ }$ r( |# C
TCP服务器端和客户端的运行流程
+ S* R/ [; Y9 c0 R; |
. @0 T) P$ V& p- B# v3 m
9 }1 R) F& ^$ _8 g& Q如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
( S8 ?9 Y% l. z# I3 r, x# _* W
1 {6 p: s0 r, Z0 i" o% I' x, [
, y+ q+ u0 S9 o+ ^& E1.创建socket
; {; |0 a* C. B: F3 [ socket是一个结构体,被创建在内核中 n( k# }2 \) f; A. J( [
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
1 o2 `5 K0 s/ l5 {1 n) c9 M U3 x- {! I7 b- |* |$ v8 K
7 N8 M- w8 B( h3 R( z/ w' A
2.调用bind函数
8 p* E; s. X% X, N; g 将socket和地址(包括ip、port)绑定。/ i4 v& l( C+ }$ N# Z
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
6 f2 l+ Q s. n struct sockaddr_in myaddr; //地址结构体2 g6 f( _6 f8 g& j4 W
bind函数
* o9 o& ]& N. Y' S3 U bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
! g- t' m4 V- P) v9 |% A
! n! C) T, v8 |, S; j& a7 n* `0 ^# ?& F; f8 a) R7 r7 q/ D
3.listen监听,将接收到的客户端连接放入队列6 `" C* D8 U$ m; a6 e9 X" U5 Y
listen(sockfd,8) //第二个参数是队列长度
' C. o, L. K" s* U2 n p/ P! S$ G) Q+ l% g3 Y0 Q, O: `
: X3 e( o' J5 ^+ b* d+ H% b; d4.调用accept函数,从队列获取请求,返回socket描 述符: k6 R( Z0 N: [, E0 b1 Q: k
如果无请求,将会阻塞,直到获得连接
6 \, \/ v) f o! U4 ~7 g int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
; X( E& k# H, e& B/ b
8 i6 L# K5 g. {; p9 J8 b) O6 g' |/ J7 W. k
5.调用read/write进行双向通信+ m7 M3 G; L# d/ t5 o: a( s
" A, W% W. j& y1 n7 E
; m9 s6 O' W' S6 O/ M- R/ }
6.关闭accept返回的socket3 _* Y+ @6 n4 M) l. \
close(scokfd);
9 }; A/ d% v h0 z7 k9 z: T
- [- M4 k! [$ K3 _ x) u" V9 D5 k4 [1 Z! h, y
6 h1 l) O* R" i2 I
6 O/ _ w, r6 e' @( ~- c
下面放出完整代码
/ U( ~* S3 i2 k7 u, @* l6 q, H& G/ _
- /*服务器*/4 w) F& G0 w* ]$ y6 Z
- #include <stdio.h>
7 u/ h9 C' U; V# K3 ~1 u3 `3 o; | - #include <string.h>
% x+ ~- z2 E2 |3 | - #include <stdlib.h>
( e: H" V' V+ e T7 ~+ a- e - #include <strings.h>3 n* Y7 [5 J, `& x; F2 v
- #include <sys/types.h>9 Q6 k1 D$ P! [1 A+ C
- #include <sys/socket.h>9 h' u7 H0 p8 u
- #include <arpa/inet.h>
3 T2 g5 L& I- R - #include <netinet/in.h>. ]2 j/ S* m# U& g2 E# n' w
- int main()3 x$ \, g* T" G' B
- {
7 [" Y' @ J" g2 ~ - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字; b. U# |$ m- o7 L6 H R
- if (sockfd < 0)
8 m: Z% z% H& ^/ L; K - {
6 G- r1 w* j, O* f, U - perror("socket");
0 G, X; Y5 v; c" c2 i" t - return -1;
8 ]( d& e! C- p, V, | - } //创建失败的错误处理
. p4 P* V6 Y# Q/ c0 W. f, {4 x: ? - printf("socket..............! q" }- s2 X- j" D: f7 L: q
- "); //成功则打印“socket。。。。”
. g3 y' x- M8 Y, M: P$ L6 p- `7 | -
! W) R8 k. ]% H1 }6 Z# d! i+ d d - struct sockaddr_in myaddr; //创建“我的地址”结构体6 z: T# j7 {# U) I* p& V
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)9 U# |0 u+ s, H' X" k
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
: u+ e9 _! U+ O% A - myaddr.sin_port = htons(8888); //选择端口号
' J3 h" x$ _6 K- D8 [ j0 [+ o - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
7 W* b, |2 J1 B/ A# j - ! [1 H5 a5 R8 o+ k
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
8 j5 A' O0 ?$ a! ^5 m# D - {
, }2 y! i% b/ Q0 M; |6 R6 S - perror("bind");
) I0 j- t' l7 ^8 `3 ]& R, ] - return -1;
9 N$ Y( F" F% l( _2 N1 h7 j - }3 o- l+ f7 n2 b/ F O: B
- printf("bind..........
0 D4 {0 k0 z' A- D% q1 W - "); H6 ]1 Y( `- y$ E
- ( K$ T) J3 G7 }4 n I O" }3 ~
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听4 A4 _& W5 d+ |+ H- [
- {
. z; { g' W5 l$ v2 j6 v$ T* [ - perror("listen");
/ v- W0 W+ P, A& m' } - return -1;
, k. E" K; u% f w8 a: D+ ]/ A5 r# F - }* a$ t! ~* g) }2 ?! p' w
- printf("listen............
! ^7 i$ s/ E. ^3 i$ T& Q' M - ");, U6 O- D: p( t6 u4 t$ q
-
2 G9 | p/ ?* F1 B% q - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求/ ]. B2 e9 a1 [% ]
- if (connfd < 0)
2 K+ z; |! t; ?9 q6 Z( Z - {7 j+ l( u8 b. ~6 E0 K, ~
- perror("accept");
) A$ ~4 q6 M. ~/ a' t* { - return -1; W9 W6 B& b; K9 H8 b2 {
- }
0 C6 z$ m" u7 a/ }* ] - printf("accept..............+ `) q) n2 ]2 j/ S: |5 S8 C* A+ ^
- ");0 k4 Q! \9 n( F( p* w9 m5 q
- char buf[100];//定义一个数组用来存储接收到的数据
& H/ r- S4 h* f, B - int ret;
$ a; w& z, Q/ n2 ]* p5 K+ V, s - while (1)
. y6 s3 {) v' k1 ]! W - {$ j f: L# ^1 K: B
- memset(buf, 0, sizeof(buf));
4 e# u+ R( X4 G ~# C - ret = read(connfd, buf, sizeof(buf));( d% F% U, l7 ~3 a0 z
- if (0 > ret)2 b- k; N9 b2 b4 B! x
- {
( K0 {0 n7 B! ~/ L* w$ r3 c - perror("read");, \$ ]( |- H+ L6 E
- break;. p6 f1 e0 g/ \; o/ R0 i
- }//执行while循环读取数据,当
& f5 L8 F& `; ?. Y/ F9 g4 X - else if (0 == ret)% H3 b! m$ p4 t9 Q0 q/ z
- {
4 I; y" d# N7 p9 I+ R - printf("write close!
/ Y. p: N$ l3 ~ - ");: t5 o% |5 o6 d5 C. F1 Y, D
- break;
* k) ? s- X6 r+ o, K/ N - }
+ Q# I0 L7 R6 n } - printf("recv: ");
, s* I8 w) q! v& c, F. c - fputs(buf, stdout);//打印接收到的数据, \0 B1 a& m' m
- }; @4 M v7 Q' b
- close(sockfd);//关闭套接字
. p- b& ]7 }1 x1 I' P - close(connfd);//断开连接
# ?, s* R3 C& e2 R" B - return 0;2 _; F. r& u) u. A+ U2 b* j% e
- }
复制代码
( k! ]& l6 v1 e. S7 J+ \1 p" M K! n
- /*客户端*/(具体功能和服务器一样,所以不再加注释)* w% }# K {/ H
- #include <stdio.h>
. c% Q7 v" O- Q( {1 } - #include <string.h>
) G5 g1 I9 Q9 J. \. r - #include <stdlib.h>
, @9 K7 j* n( U! q - #include <strings.h>5 P3 i& Q- h8 c" R
- #include <sys/types.h>6 ~( E& ^$ i8 s. t$ F
- #include <sys/socket.h>9 \7 p/ c7 c: \/ T3 O6 F: `
- #include <netinet/in.h>
+ Z# b* o, G" Q9 m: G2 w! W: }/ }; C - #include <arpa/inet.h>
3 e" x" Y4 V$ F! v7 P5 n { - int main()* P5 v& U* \: h o$ c
- {
1 g% n/ P1 B: Y2 P - int sockfd;
5 t0 G9 ~/ q# D; b - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
5 O- H' ^. C8 M' e - {
6 G: f) v, C* O9 o1 l* Y% j& T, [ - perror("socket");7 T2 ^; C2 _3 F
- return -1;2 X0 L- k8 P+ `% P" P* W
- }
. S+ f2 ^8 L7 M/ Q9 { - printf("socket...........
- a7 I# Q" p/ F - ");4 z4 X1 O+ Q Q9 `# b
- " h' H& a3 i- U5 T a$ B1 Q7 N* a
- struct sockaddr_in srv_addr;8 Y9 ?% Z& |* M7 P' Y
- memset(&srv_addr, 0, sizeof(srv_addr));. v* T/ n: e* N% V) K
- srv_addr.sin_family = AF_INET;' [7 D0 X8 l$ S- {2 g
- srv_addr.sin_port = htons(8888);
) C' l5 x2 w) o1 R* J( H! _4 z - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");& \4 B1 u# o' N
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
~% G1 G. u* I+ _ - {
5 d. Y7 s' k3 _" j! [( L - perror("connect");6 L, T& I6 }3 s2 m( w
- return -1; //exit //pthread_exit
; T+ Z- k5 `/ E2 I - }
5 f1 v3 ^: L3 k4 ? - printf("connect..............* f. W/ n* X! j2 M% i
- ");- w' X4 S' ?7 g% {/ ?/ q- _
- char buf[100];
' `1 I, ^1 m+ C+ [ - int ret;: r' [1 e, R! E
- while (1)* [- z0 I7 J" z
- {
" ^' y E; Q; H - printf("send: ");9 h( W- L) V, h* q/ W
- fgets(buf, sizeof(buf), stdin);. v1 e6 N1 S6 i" ?6 j5 u, @
- ret = write(sockfd, buf, sizeof(buf));
( V- m, J; S" H l - if (ret < 0)
3 X+ S9 r2 y9 C. q - {
8 j4 B$ |" ^) N) l% j$ ` - perror("write");. F6 k7 t& k P% u5 E
- break;
. g, D3 [8 l& y+ _$ @2 N1 b! z - }
i; S0 f% G; B( b1 w: s - if (strncmp(buf, "quit", 4) == 0)
) `) `1 Y! J* X. W - break;# D$ O# h# L( p# Z( Z1 u
- }! W4 Z! g! a% O7 `
- close(sockfd);
$ S4 T$ p9 h# D: ~ - return 0;
* b6 Z1 X& P2 J' W% s/ `3 ^/ a - }
复制代码
' n- M$ w8 j* g/ s6 Y8 X% a( O, |: H$ A' z0 f2 i9 }8 o
|
|