管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
0 X; I$ _$ m4 g N8 V- r1 S# T! e0 m% d0 c* I' i8 I) w; e9 Z
6 P: v* Q4 c) O4 K$ lsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
. ^! q' A1 }" |1 A. S1 Z+ J
4 X6 _3 W0 o! a; w2 H1 O2 q+ f5 W5 K9 Y! v
TCP协议) `' s* q2 z5 A
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。, l2 x3 Y) v! ]" P$ o6 q% C
: R) s- f7 w7 _3 r* O2 q( B' |6 c; K/ t; D
关键词:三次握手,可靠,基于字节流。7 [7 a' h) N2 g& w7 K: L |
3 t( X& a8 [; Z% T
, R; E" U7 i; [# z可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
( F2 u8 |! Q$ i# ]
0 S, I) ]5 P; f# n* J! v* F- V
TCP服务器端和客户端的运行流程 h% i( s& M& h: w" ]5 j) L
4 Y8 Q# V- h5 U! C4 t3 a0 e: L/ n4 t8 O
1 Q9 e9 ? k) M2 g: T0 [如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?8 T$ `7 C7 F$ Z
4 g) H( Q! ^ Z. l+ T- E
5 |% a) b5 B& b2 V5 d1.创建socket
2 K s3 J6 F) y) L& [ socket是一个结构体,被创建在内核中8 ]. A% R! A6 N* p
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议7 g# K1 R3 Y$ K1 I# e3 f
3 h o% B6 v/ z; w* b
i1 I" @+ q2 q2.调用bind函数
3 j3 J( G! C% M$ u+ L! v* C 将socket和地址(包括ip、port)绑定。9 L/ r" j9 c4 o- p# h
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序) y0 F! j6 }& F
struct sockaddr_in myaddr; //地址结构体
) ?: Y0 J9 l9 F* {+ [# _ bind函数
; O9 C) a+ I4 x% M bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
; @) b. ]! Y4 n4 |
. i' M/ q% Q+ @& N ~6 B" b! n8 D. c, b, c
3.listen监听,将接收到的客户端连接放入队列+ S9 b* n$ |* D: }
listen(sockfd,8) //第二个参数是队列长度2 i4 p" T$ S) g6 j
3 V0 Y- c" m' b* E+ k0 q
, ]/ X. G( T0 q, X4.调用accept函数,从队列获取请求,返回socket描 述符& v, `6 U* m5 K# q, S
如果无请求,将会阻塞,直到获得连接- n! f9 W7 m+ ?% }. o; K
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数( O5 s& D7 A x# H: d
5 u( u0 N d% N `
' T, s3 e+ l! [# h" l
5.调用read/write进行双向通信. V# ^6 a3 P/ M, A& M
( C( }( q* e! o
* j. B: x/ Y3 X2 L* |6.关闭accept返回的socket
+ Z4 f9 m* y7 c g/ f1 A close(scokfd);
. C- j* G. d: w# }# t7 e
: ]) `7 e/ X+ |6 _/ H( x
3 C, F M+ }# [/ t& s% \ T; _3 J. w5 a1 S( k, l' \! I
4 V; @5 C- W/ x1 C7 e下面放出完整代码8 x1 y8 x& o& A& R3 D
, F2 d7 A7 _8 X' S2 ^6 X5 H5 w
- /*服务器*/
9 r8 p. [7 o) {. h7 n3 y" _8 O - #include <stdio.h>
+ P, X/ Q N1 b, | - #include <string.h>
+ O& L% i7 Q* j; Z% i4 u& Z7 C* U - #include <stdlib.h>6 q4 g4 {6 U% U3 m
- #include <strings.h>$ ?. S( {) J) w# G- M W' K
- #include <sys/types.h>8 U9 J9 Z- U6 E. p
- #include <sys/socket.h>
' U6 p$ c1 W" \: p - #include <arpa/inet.h>/ n. F1 E$ ?# I1 N2 |4 a1 q
- #include <netinet/in.h>
C# {3 n2 j- h+ W5 ?2 N0 r) | - int main()- _! x8 |7 }. I W- L* o, N- [
- {
! y4 k" `" ]& o# {: C1 V - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
! T2 r" \8 m, c0 F/ J" \; | - if (sockfd < 0)
; P* k9 T- g4 }$ f) N - {
, L c6 z6 a) I( q2 @5 D _' e - perror("socket");
1 h" B4 V1 v; }+ q1 _3 v - return -1;4 c$ r# Y6 x- x" a1 C: v
- } //创建失败的错误处理( d# l w G: T- R6 b9 h2 J
- printf("socket..............# b' \1 _: c/ j/ ?( J* S
- "); //成功则打印“socket。。。。”
' Z4 E! O9 c1 u4 I - 2 t1 A: [) Q- d" t) {! k7 i
- struct sockaddr_in myaddr; //创建“我的地址”结构体
" y% `5 M& M; C7 k3 h' A - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)5 b# c/ g& v7 Z+ `/ |) s
- myaddr.sin_family = AF_INET; //选择IPV4地址类型
# \. {8 O6 N# \# ]. O; q - myaddr.sin_port = htons(8888); //选择端口号. M c7 n% h/ j1 @3 k8 d8 G
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
) E) H* c2 `% o' Q. e, _5 c - . V7 Q2 x" H3 A {6 A( L
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字' z+ [& w: V% K h& {
- {! w& P- K0 `+ F4 b# W- F* o
- perror("bind");1 ~$ @* p7 `* \
- return -1;* p. K' B6 A; t9 e- a7 X
- }
. v' C' U1 e$ X5 f: n2 B - printf("bind..........
2 [7 C) o; T9 j - ");# L ^- F+ v/ ?/ e; v% A
- . S1 k7 [! m6 n1 w# m
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
' _6 l3 c# c- @# x - { U3 h; _! ^ Q
- perror("listen");$ S9 U# a1 c1 c( `$ \
- return -1;$ G; t" [$ |* `( l* Q) d! d
- }8 D" U" J2 h5 l, {- X! C! `
- printf("listen............6 |) Y: S v$ j& q) V# Y* Z1 v4 t. d
- ");% f5 K; H- C" M- o
-
' m% z- l6 `; X5 D7 s5 l - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求3 ]. V' |! k# M8 U
- if (connfd < 0)$ T5 P- @3 v0 r$ l" l1 c# k: Q
- {! L1 s( k% \/ d: b( \: E/ C2 K" Q) d
- perror("accept");
- F5 v3 R0 z" @, o6 u- I - return -1;5 ~0 w, S) z- J+ z
- }( C2 {( L9 S5 h9 Z/ e9 y
- printf("accept..............
7 @2 g! b4 @* O/ d5 f$ a - ");
' ^' `& P) e" [+ q - char buf[100];//定义一个数组用来存储接收到的数据
; b' L" F' P% h7 C: s - int ret;
V; K, Q5 _4 F( F" p0 v ~ - while (1)
6 B/ a% q% Q+ x/ K# @ - {
- e& W9 J" ]$ x+ G& {* ] - memset(buf, 0, sizeof(buf));) E4 I k2 R p% q
- ret = read(connfd, buf, sizeof(buf));
" D+ T& c( l* J/ Z+ j n5 z* _ - if (0 > ret)
8 X$ ]0 \6 E, x! J - {! X% S! F9 V$ e! _
- perror("read");# m- j: ^6 Z8 a
- break;. D; W' q$ W, J2 t9 ^
- }//执行while循环读取数据,当3 K( a+ t: j3 w% @( ]8 h: x
- else if (0 == ret)7 v' {# `( \( N" J
- {
: G2 k8 r: d" w, n# i4 U& ]$ y. D+ R - printf("write close!
& E" ]- @( ?4 A) G' F# o - ");6 m; Q5 ^& e9 l" t" ~6 L$ F
- break;
6 W& v, e1 q$ b# f: u - }
2 ], v+ M1 F" n9 N/ E% P* i9 V! Y - printf("recv: ");) h& W9 }# L+ |: R% S4 v3 U
- fputs(buf, stdout);//打印接收到的数据
n }" T& ~0 t, l1 T$ j - }
- I) G) k7 z" T7 Y7 [3 X. j% ` - close(sockfd);//关闭套接字
8 D: @6 q4 d+ v( c' W1 a - close(connfd);//断开连接% @( }7 w* c! E( f/ T4 L# {
- return 0;
8 x* O$ }' w! v: f2 `2 I6 { - }
复制代码 8 {/ {9 Q, ]8 ]& I- [8 s
) b6 Z: {- y" y5 S+ R: L8 A2 D1 G! h
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
+ `6 ^! k3 O! d& k$ G) t - #include <stdio.h>
/ \+ W; `# u: {. W9 [( r( _: Y - #include <string.h>, k+ A9 E: G: \& s0 d, d, C& F
- #include <stdlib.h>
( ^% m r3 k( Y& T1 ~- ] - #include <strings.h>/ U0 D6 W8 V1 W8 v6 b
- #include <sys/types.h>
6 _& E" T; o) J& {! r/ U9 |1 x; b; o - #include <sys/socket.h>9 J/ H# l" I6 Z7 {
- #include <netinet/in.h>
2 n' R9 |( P1 Q4 h5 J0 n - #include <arpa/inet.h>: N4 @! ^1 e$ X) C5 C+ N
- int main()1 q3 h* w9 H& z% z- E/ k9 O
- {' v) G0 I( u) A) Q
- int sockfd;( V% r# I8 o4 d" q, q
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))3 ~1 @. t+ @& Y0 J* N' E$ N
- {% B' |8 j1 J9 r: d/ o
- perror("socket");/ d# ? {4 O" m
- return -1;
5 y; r' \6 s. a8 r$ w1 I; [4 y% X - }+ R1 {. K* V# G* O6 v# q3 r/ m: j0 \
- printf("socket...........
2 V: w" ?4 ?) W0 G9 Q - ");- ?* H) ?# i7 K* S: l" l
- , A# X* K" t6 y; `/ x
- struct sockaddr_in srv_addr;- H& t9 T4 O1 n7 w2 O
- memset(&srv_addr, 0, sizeof(srv_addr));3 M6 v3 T! A4 G- ]- r+ q- h
- srv_addr.sin_family = AF_INET;8 c; @9 B$ i5 k
- srv_addr.sin_port = htons(8888);2 Z2 @2 W+ ^2 V) O0 l, B
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
1 {, U) q: i7 [1 ?9 h - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
$ Q7 }+ j3 `" [& O3 Y - {
: \, f; j$ S U& A8 l - perror("connect");
6 b6 Y7 o$ Z0 O. R - return -1; //exit //pthread_exit
5 c3 q5 }7 a* g- r9 S, v! K/ {. e - }* N% m# V6 G+ a* y L8 U& E
- printf("connect..............- U) {2 ? R# G# E
- ");
, ~8 c) p0 f6 F! L, L V - char buf[100];0 U6 f( G" R) @/ A2 u
- int ret;2 a) A0 `* _' E6 P2 f
- while (1)
5 s) b5 {# g) g9 S: o8 Z - {5 Q+ P$ M. B% W" F' Q6 C+ l
- printf("send: ");' U3 Q1 j! K1 u9 C1 s
- fgets(buf, sizeof(buf), stdin);
) {9 j9 u Q$ Q! f - ret = write(sockfd, buf, sizeof(buf));/ X6 w9 {( o$ O
- if (ret < 0)+ g! Y$ m8 j y3 C% v
- {
$ x' C8 L, J- @+ f+ r7 n - perror("write");
8 E7 v$ ]0 _* b$ s- s4 d - break;
2 s7 i- t' w0 @; Q, b# c - }
( Y5 n! v( o ]+ F) b - if (strncmp(buf, "quit", 4) == 0)
: }8 e* g2 n# Q4 M2 s7 e - break;
R" Y. ?7 P) o5 W* t L2 j - }
- k* G) q$ I( R - close(sockfd);! w+ k- {: a& }% u: X! y; q
- return 0;$ S& \/ ^' u( s6 Z k
- }
复制代码
5 h& p6 ^9 F3 }! g2 e$ T2 |/ P5 Z* q$ ?/ K
|
|