管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。. G& X' ~, i9 ]
+ j9 H) v: Y& Y& D0 r ]- g$ f2 {- f( t- \: c5 B2 J
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
9 y B' s e# J7 @1 g9 R+ | C) h- U$ j) ^; w
7 n8 J4 R3 E3 R+ gTCP协议0 q$ Y3 C! E- U+ w F+ @! n/ i
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。" J7 Q6 Y0 ~6 i, x2 G' {' j
& |5 S; ]' r1 p; ?6 T7 ^* }
7 M9 j' n/ H' S# _关键词:三次握手,可靠,基于字节流。
3 S! e2 m4 C9 q y* |! g: W" c- M3 x t6 j" D: k7 A, m2 s7 v( F( h
! b6 @" E5 i9 d" f" P可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
* |* G r# `" N% M" K
( |0 U$ T" b4 r+ }6 UTCP服务器端和客户端的运行流程6 i g) c% ?/ D
. T) ?- t7 g: Y. \8 E9 w1 R" `. u5 p# }
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
- A; x" {6 }! \( a$ Q$ L
* P7 T1 p: s; s4 q; _1 [# O& Q. D
1.创建socket5 H# Z. Q0 u7 S# u9 F
socket是一个结构体,被创建在内核中
' j" m& N6 D$ R+ c; v0 Z: E sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
' E& S% ?/ K7 f: r. C6 j$ N8 K" S1 p4 B8 G, o6 }$ M
( [; \5 D$ d* F' ?& `
2.调用bind函数% ~. |4 [7 R+ g$ [% O4 ]
将socket和地址(包括ip、port)绑定。- y4 c6 _! c2 j& c
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
; N, W; [/ n* q- _! m+ o3 s8 `6 Z struct sockaddr_in myaddr; //地址结构体
9 c1 A7 P! `2 H( U0 W6 j bind函数 j( v( S @4 n! @8 B$ s
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
8 K: G4 y9 z) u; B' K, H5 n$ |" }) g/ Y" {, P# p
4 q2 e# g3 ~% c& d0 Q2 |6 A( Y$ J3.listen监听,将接收到的客户端连接放入队列( ?; Q, ?4 \1 U& O$ M
listen(sockfd,8) //第二个参数是队列长度
- A/ G7 S1 ]; O: l6 b* Y
' p9 f( Z9 W' a- z6 t: w/ ~4 |! Y& U' g1 F% D
4.调用accept函数,从队列获取请求,返回socket描 述符
8 S$ y/ v. ]! G 如果无请求,将会阻塞,直到获得连接, W2 p) c: h* }/ ]0 d- f) k/ J) s
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
" O& e0 h& x! b9 [0 u% v: j6 F, s( N: o! d* q
1 K$ p+ t% E0 V1 j" Q5.调用read/write进行双向通信 h, d O ?) w/ z, Y
. I$ B" I) j6 ?: r4 X; _3 F$ S; K
* a o0 r5 L# Q
6.关闭accept返回的socket( `0 i( F, {/ G0 m
close(scokfd);
2 L5 g: q: X+ x8 h5 p2 W! ~: j( S5 x
1 @; Y. ~: O- F: i$ c9 l. [$ [% k6 j' I: h1 p% X
6 p& K- S, W/ t7 O
P% Y" D+ a7 T# S+ H! L' ^下面放出完整代码
. C, ^/ k$ ?3 L8 g g9 v8 s7 S- o- G: y% `' X6 K c
- /*服务器*/
3 z/ k' ]/ N( a; }! e; E6 x - #include <stdio.h>+ T9 {! c5 I5 T7 b7 }
- #include <string.h>
7 Z2 p* o% ?" q - #include <stdlib.h>
9 e" C8 q+ j. c - #include <strings.h>
) M8 o g' y4 C6 m& } - #include <sys/types.h>4 [! l6 ?! c) {
- #include <sys/socket.h>5 @8 K4 c# W0 R+ o4 ^
- #include <arpa/inet.h>
- H# }8 `9 C/ R5 S; h, L# Q) ~1 ^ - #include <netinet/in.h>
. O5 ?- I7 [; k& V7 L - int main()
. q0 w$ y+ ]4 V1 k - {# ^' w* A3 ~/ ]6 M
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字3 q% p. c: W; D: x. s1 i6 d# Q
- if (sockfd < 0)' O8 Y! p0 U# }" k$ ?, E
- {0 z! h b+ @' N b5 _3 J+ T
- perror("socket"); h% U% v! h' h# G* b; @6 w
- return -1;, ^+ W( T$ D3 m! L6 k
- } //创建失败的错误处理& T8 Q0 V6 ~$ c/ V1 y
- printf("socket..............5 g$ X3 t' B( i1 p+ C& p3 U
- "); //成功则打印“socket。。。。”# i# }! ?% _5 I& D" N0 B
-
; N. I2 n# X0 y' _% E4 | - struct sockaddr_in myaddr; //创建“我的地址”结构体
% D9 i) i. ]0 Z! K - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
+ ], ]- a/ n5 r& ` - myaddr.sin_family = AF_INET; //选择IPV4地址类型
! c/ J0 Z! I3 c - myaddr.sin_port = htons(8888); //选择端口号
- u! U* _/ k! e" [' v3 |9 `. Y* |. c - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
% J7 H6 C# q; L -
* f. d3 n; ?. F6 N1 d' z; X" h8 H5 L - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
9 W2 {, g# f# K2 g - {7 m- q3 p& W$ Y; U$ K: {4 _
- perror("bind");1 E* M8 O; ?2 ?, _& \. O% i
- return -1;$ P$ P. t' O9 H" @; g: }9 a# J
- }
1 ~( t" }0 v2 k( ?! J" b# j0 l - printf("bind..........
1 {& F" N' V& G0 O% j0 _. H# A - ");4 i7 N6 J1 x E* p8 h
- 5 O! M8 V+ D2 t6 |
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听2 S- T; }- c! T
- {
) O$ n# J6 C0 T - perror("listen");, l# `5 s4 I2 n- ~0 D+ H( P8 M
- return -1;
p3 t) Z8 |& v, m/ w/ ` - }
9 R, U' {$ c# V2 u* t4 R3 ^7 z/ K - printf("listen............2 S. z# V& z/ l
- ");0 Q5 D9 N) E" ~% W% n- e! C+ w( K
- 1 F; _5 M0 P2 d. \+ C' Y
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求5 ~" D% U; Z/ E
- if (connfd < 0)
. ~" l5 l ?3 Y' H% L/ U! c2 z - {1 ~# s" S4 r, X6 c5 L( e
- perror("accept");
5 U( @0 H6 y( M - return -1;. ~' v1 d( s" k/ W) n
- }
. N8 P8 d# n0 \3 G0 K% A - printf("accept............... H( g; H8 Z: s u9 i# f- Q
- ");# k l& H. G! p; `, C6 M
- char buf[100];//定义一个数组用来存储接收到的数据
, N8 t0 h& X r0 J* P1 Z; C5 @) L - int ret;
& A4 Z7 Z# h$ o8 i: J: K6 ]8 s - while (1)7 S* U+ W- L( ~; I" z9 f1 t0 @# b
- {
' J: S! i& |5 V* a! ^6 J - memset(buf, 0, sizeof(buf));
$ l6 O* q2 ~' o( H0 e - ret = read(connfd, buf, sizeof(buf));7 [% D3 T+ A$ Z$ l
- if (0 > ret)
% ]7 H1 w5 ? G* C - {
' p8 {7 z! v- v! N. k: ? - perror("read");
, a C" K; f& b$ u6 t - break;) Q9 B6 D% z' K# L1 X* i- g' x* X9 v
- }//执行while循环读取数据,当7 L! G& j$ ]! G
- else if (0 == ret)
& C' C" R8 p! s1 V - {
/ S! @$ e0 E) J3 n. A% y9 V9 Y - printf("write close!4 Z" M! y. m( l6 s9 P' O& {) H. K: I
- ");& d+ Q# T8 n' S4 \" G
- break;+ f: ]- c: ~0 c8 b
- } R6 ~2 w* _6 D$ V" R/ j
- printf("recv: ");4 x$ k) L) `( F! v; u" s
- fputs(buf, stdout);//打印接收到的数据
4 Y0 B: p+ M1 @5 Q- Y - }0 E9 d- ^1 a, q7 t* B. r# h
- close(sockfd);//关闭套接字
! w$ j; U# C) M: B - close(connfd);//断开连接, {& C; i7 B5 Z' p8 P
- return 0;# p$ F# `, _% N6 Z. l
- }
复制代码
8 x* @& b# U* ?) }2 s4 H/ l; p$ V, ~+ k3 T
- /*客户端*/(具体功能和服务器一样,所以不再加注释)6 [2 A, i" g/ U
- #include <stdio.h>
. Q* L# h# F7 t, ^$ x - #include <string.h>
% d5 R" S, P2 a - #include <stdlib.h>7 m3 C4 p) x0 o+ Y2 `2 o
- #include <strings.h>8 o. ~( ^3 } f- j
- #include <sys/types.h>
, l1 s c# V6 i - #include <sys/socket.h># X7 ~" o4 P, V1 c @. L
- #include <netinet/in.h>3 V' W8 d( \* ~. p, W3 C
- #include <arpa/inet.h>
/ \% }, K( @8 U2 T M - int main()1 n& T. }9 r1 A5 g" q3 _; b
- {
' }. O& _5 Q+ w. b) O - int sockfd;+ z' o: T' e3 ] E, x1 r
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
, H% L2 S$ s R( i - {
1 A8 v6 s0 f; @ ?, R$ m, l - perror("socket");
+ {% ]" ^5 R8 d3 `; K4 X* w# _) F) \ - return -1;6 Y( q; v! J/ [8 j/ R+ n) N5 S1 N
- }
6 l! u! J4 q/ R" q4 X - printf("socket...........
9 ]7 z& ?. Z' Y" H( G - ");) s, n c$ ]3 C0 d8 d/ V
-
3 W% R7 D4 [+ @# { - struct sockaddr_in srv_addr;
0 Y3 _9 r! J! J& O; n - memset(&srv_addr, 0, sizeof(srv_addr));3 }& H' W. H2 K( t8 l t
- srv_addr.sin_family = AF_INET;
. ]- e3 J+ ~; Y M - srv_addr.sin_port = htons(8888);
2 Z o' D" Y+ q2 X# A5 A - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");' B# X( Z2 D( v, Y5 n) x2 j4 |
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
* i+ b+ A6 _1 @) g! c3 \* @ - {
5 y: I- e4 ^4 R3 B - perror("connect");5 h9 Z" v% A1 z# L2 b/ K
- return -1; //exit //pthread_exit7 W" T, n9 b3 Z w! }$ Y! Y' X# o
- }
' R e: s1 N+ C8 g: f - printf("connect..............
0 P& N8 B6 R+ }9 O4 E+ f: ?4 B4 Q - ");
1 d2 T3 g7 q7 ? - char buf[100];" E1 b2 r: a) Y
- int ret;
. @' p2 o7 X+ w - while (1)- K8 C6 I7 j* r$ o1 @5 E; C3 d
- {
j; u: m {& I/ P& E+ d - printf("send: ");
' I- A/ @7 R, E! p4 z2 f - fgets(buf, sizeof(buf), stdin);$ a, O- I; [6 G/ C' w+ v- P
- ret = write(sockfd, buf, sizeof(buf));
& T6 p0 U# Y' S0 P& v( J - if (ret < 0)2 g. G1 g" E: r x
- {, B2 I4 z1 U+ @3 O
- perror("write");
, Y+ u2 C# E0 t4 J( O* l - break;
! L* V+ [8 W# R* h9 X7 F" N - }
4 x! U2 t* V" ^5 l e+ R - if (strncmp(buf, "quit", 4) == 0)
: ]; n: L6 `4 J7 b/ o. R5 J - break;
! q4 I0 H. M4 F - }
# |% x) w9 C# Q( g( F8 y* y - close(sockfd);
8 V) Y& k5 F* g3 k) d( a" ~ - return 0;
! W: a. Z5 u& j* D3 ~- [' r( | - }
复制代码
$ _! g$ i Y P8 v2 ^! e$ @8 Z8 |3 y3 n
|
|