管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
) A9 N: i; O3 B" {% E! p1 v% @' B7 ?( F- q
2 ]% D+ p6 _) r v; v9 W$ Qsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
& t* X7 S& B" |' y& g1 j3 `, w; T. L7 [! ^
% h1 c4 I6 p% z0 K
TCP协议
6 z+ [* l c: WTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
( o& X4 i1 L, k% L. u" P" a
- C0 B2 g8 @# k0 P
' ^- I5 ]* R% p2 E4 @- O3 D关键词:三次握手,可靠,基于字节流。/ r, ?6 H6 O! Q9 p4 c: C: D0 u+ Q& m
8 E7 m" u! D" p% W4 k# x0 h4 S# a- k
% \% ^! J5 Y& a% ~: N可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
$ Z4 O3 Y Y6 r
0 {% T8 @# U, c) P: @4 G
TCP服务器端和客户端的运行流程
" V$ T9 |; {+ K: {. N/ ?! R0 m7 {' X1 R7 M* n9 l m
1 z- P& \8 q1 C
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
" A$ l! B. c1 _; E) ~; E. e) ^; t) d" b- | S+ k$ f
5 A1 Z4 l, Q( M: q1.创建socket: E$ T2 ?# X9 ~! t I0 Q5 y
socket是一个结构体,被创建在内核中, U J: u5 e( ^; o+ d
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议/ y+ a) x6 W" t. [8 c& {) S
4 \4 a& Z2 s4 a
# H) W2 p) S/ m1 h# C2.调用bind函数* B& `* N/ X: N& ^% f. c$ x
将socket和地址(包括ip、port)绑定。" s9 A2 ~: t+ y/ ~& s) X
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
2 \( q) I4 K) w W struct sockaddr_in myaddr; //地址结构体8 D& N! H B. R1 H1 _+ ?6 m
bind函数
+ C! c/ e! A5 O) ]* C2 F& n! w bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
/ ~1 P( r, @6 Z3 n5 h5 Q7 R
$ q4 h$ r& K+ @8 q+ { N) m& } a0 t& n! R% U( Z$ q7 F; _# L
3.listen监听,将接收到的客户端连接放入队列
8 [0 u7 w- d) M5 R listen(sockfd,8) //第二个参数是队列长度
" e& Y/ w V. s* x
/ I; G* m& X; D7 Z: D" j; @
6 C! ~: z1 i+ W* @; n. f7 r2 O4.调用accept函数,从队列获取请求,返回socket描 述符 l/ X# X7 @6 ]" d. d
如果无请求,将会阻塞,直到获得连接3 Z5 G) S" c, ^/ ~ }+ q
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数! w: o, D. A0 C# t7 w2 {- e
- V+ a" K, h" V7 q; t' K8 e! X5 P" T; E8 }7 E( Q/ F
5.调用read/write进行双向通信$ k5 F! a" L" x1 ~4 C
$ n4 y- h% `3 ~& P- g1 d
) |9 Q+ Z, H8 l5 `, k
6.关闭accept返回的socket ?! g( `1 z8 ?/ b
close(scokfd);5 ?+ A+ [+ v( E+ U C" A
5 u. O: Q5 M: [9 d1 a
; u- b/ E3 u3 C* Z: t& J5 C% N4 W8 x* l' c
3 S: f, k, B" y
下面放出完整代码
# x3 ]1 w k, F7 P* Q& G C- _" b% L1 V. S
- /*服务器*/+ q" z6 d8 v' A! ]1 ~$ |
- #include <stdio.h>* z( ]7 D, r9 v8 I. Z0 D
- #include <string.h>) ~( \. p9 T; Y
- #include <stdlib.h>
; H; e3 ]( T2 `9 I$ ` - #include <strings.h>
+ i& K( v8 i6 a1 q8 O' t" x6 h - #include <sys/types.h>7 O+ B; P8 I( g% W
- #include <sys/socket.h>
$ R* `' g1 \; k - #include <arpa/inet.h>% ?( F6 Z l! G' U. l
- #include <netinet/in.h>
2 _* Y! P( l0 I( O* }. L0 S - int main()
7 `: Y& N# S& d& d* K5 Z - {
' O/ A, T5 b" c, e# J8 ~ - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
# \2 K* H; |9 ?: N$ s U - if (sockfd < 0) j1 Q; D z7 F; \1 `; a# j9 u
- {6 Y$ S2 K& m: B) [' T' x/ _2 f
- perror("socket");
* Z5 z& S/ e/ d$ L y/ A - return -1;
# s2 ?& q5 E) n4 _ u; v - } //创建失败的错误处理
7 Z+ s1 W7 U4 G - printf("socket..............
1 R! d' V) x& H$ g9 ?' _* v3 @: s1 b' U - "); //成功则打印“socket。。。。”7 z; K9 u5 ?1 C7 X1 J; N, W, u# i8 g
-
5 Z7 f; Z% F {6 w! P - struct sockaddr_in myaddr; //创建“我的地址”结构体
; g- O7 E7 @4 _5 M - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
; s4 r5 a3 t6 p1 y5 v5 t; V - myaddr.sin_family = AF_INET; //选择IPV4地址类型
1 Y1 R! p) p U8 h9 W7 _ - myaddr.sin_port = htons(8888); //选择端口号7 q# W' j7 S6 I+ l
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
! F( u/ j6 W: @" i -
( |0 d% n+ b* r. B6 V - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字% y7 J4 U4 e7 C9 p: v7 t
- {5 K( M$ y/ u9 h1 h5 ~
- perror("bind");9 |* z3 e4 t- \) @9 J+ y% I+ O9 x
- return -1;. y% b6 ]" ?4 J$ l$ q; v0 J! A6 i# G
- }2 V5 b# M0 d/ {# n e4 s% X6 R+ a7 r
- printf("bind..........
8 ]4 e# \6 M, \1 ?/ i/ Y - ");# g5 `7 l" k1 j$ t8 g' Q
- 0 k& ] E$ ~( t: b$ L# J3 f2 F8 n
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听) ], \6 t. m: k
- {
+ m" Q5 f+ Z" t" T3 r1 `; z - perror("listen");# d4 X0 M5 j I) I' i8 Q
- return -1;
0 y* N' g: H) i, ?8 A0 i* L6 a - }+ Z! q4 y1 q3 T
- printf("listen............
9 |8 E t, K4 r# A. {+ ? - ");3 Y/ I9 U7 A9 g" g
- + I1 b& j5 ?" S% q0 y& Y) z: }
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求$ d0 @- V' L$ B* r0 U
- if (connfd < 0)% O6 C: _3 L2 S! A; ~: D# i Q# ?
- {
; ^, ~ G. ?9 F/ s, [& V - perror("accept");8 }8 y" Y3 V. u( M8 S2 V- A
- return -1;0 F% M' K* z( W5 [
- }# ]; w1 |: n- ~7 @1 v A
- printf("accept..............) ^' [' l3 Y4 c) Z. ^
- ");5 n4 m, z& v$ m$ M/ H1 l+ b( v( \2 T
- char buf[100];//定义一个数组用来存储接收到的数据
* |3 c l# { `: s - int ret;) e$ T! r! l- q1 c' A5 h
- while (1)
1 c. `: o7 x9 l7 _ - {9 d$ [1 t \% g3 X! W% r
- memset(buf, 0, sizeof(buf));) o* B1 \2 t9 a- `
- ret = read(connfd, buf, sizeof(buf));
$ ]% Q- F6 \# T# }0 E - if (0 > ret); h/ Y( @3 n$ J) e' f4 e) m" S
- {
: P- c$ J7 t+ o2 E5 f - perror("read");2 s7 n/ D1 R w) L- E% R
- break;
2 e4 w! b+ l0 l$ K* W6 u* A - }//执行while循环读取数据,当
7 m' {' j: h0 l8 |* O& {# U - else if (0 == ret)
; V( F0 l4 d5 c8 F% W& Q - {
- F+ L4 O- x g) x - printf("write close!' W+ n! j$ _9 G" q# Q0 H
- ");
" P7 q, k* J' o( d - break;
& {- ~" I9 ]( t% _1 ^0 ]2 r - } e* e% \0 F7 E7 \# u. x/ t
- printf("recv: ");, P% d2 ?6 p4 T' R# O0 j( C7 m" T
- fputs(buf, stdout);//打印接收到的数据- k {. A8 {6 a& K1 G/ F7 H1 {
- }
5 N0 H0 K) X$ L" a - close(sockfd);//关闭套接字
L" b! y/ ^9 k; ]9 B( t" G - close(connfd);//断开连接
) k4 g+ k7 [4 w3 J - return 0;
* T) u& w" V, h1 }; N6 W; ~ X$ \1 F - }
复制代码
% |/ ^" T2 V7 X. R2 d5 n3 Y3 f! Q% Y- _3 \" v) W- y% c* k+ h
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
5 G M! K7 A% b7 N( m* w - #include <stdio.h>9 C4 |7 I: O4 D: R. e! p
- #include <string.h>9 d# X9 D t7 _ G" ~, ]6 V
- #include <stdlib.h>" a4 U" h& ?4 b% D
- #include <strings.h>
' k& a% C# }: D+ B - #include <sys/types.h>- W5 `' k) g0 V# ?: G5 @
- #include <sys/socket.h>% \; g8 I6 g& I) L! ^! Q( k' D2 j: e1 ^
- #include <netinet/in.h>
9 p B& h: v/ c2 A+ F" y+ n - #include <arpa/inet.h>+ P/ M4 m! g$ }7 {4 F) u
- int main()8 N* ?, C+ C$ Q; D- k0 Y6 g4 {' J
- {- e& l% R& k9 ~7 r: L5 e
- int sockfd;
( l; _$ z6 t. m$ A9 ` - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
{9 o. T1 a8 A. e/ N, J7 X - {/ y5 _" N8 N1 f4 X7 b
- perror("socket");5 i* m# p( o/ m1 R. k
- return -1;
# l3 B; D& a3 N* ~2 P+ Q, B - }. Y3 L4 J4 w) z9 e& A' z" G+ e$ D
- printf("socket...........
# n) }, D8 @2 G - ");
3 T% A4 T; ]8 J, t2 o -
; A4 a+ ?; H" H* r - struct sockaddr_in srv_addr;. U H2 B# b/ d( M; V8 Y5 F) {
- memset(&srv_addr, 0, sizeof(srv_addr));8 I( o' x6 M$ f q3 d
- srv_addr.sin_family = AF_INET;
# J: O$ K+ k- L! Y, i v - srv_addr.sin_port = htons(8888);4 J) }) Z' e: z' Q9 O/ x% s/ u
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
8 e% z4 c/ x' J - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))/ B/ O, Q) h7 G4 `, y
- {& R! ?5 h$ ~4 l! `1 `% G' j
- perror("connect");% k" }+ q% Q! Q* O; [! c
- return -1; //exit //pthread_exit- r5 i6 s" Q2 m* _# r
- }* U, f, f- q+ N" M' k8 D6 L
- printf("connect..............
Y5 J5 [+ L- O: @0 h, S- G - ");
2 P0 b- R5 E0 m3 h - char buf[100];
$ T9 c3 B7 L, f5 U$ S8 i" M' S - int ret;
7 i6 B/ ] y0 U# I/ T0 V - while (1)7 `; _) n, E1 O6 I$ e' e
- {! a3 A& \2 d( m/ m
- printf("send: ");
8 Z( @3 J" e* Y# i+ m" B, U" B' K - fgets(buf, sizeof(buf), stdin);# |1 m4 v- Q- R# r7 V; v: }( w& Y
- ret = write(sockfd, buf, sizeof(buf));
! t8 Q3 {" y# T - if (ret < 0)
4 T% M+ H: O5 H/ [) }0 \6 Q0 _ - {
8 |; R+ E* D+ }3 t: _6 ~ - perror("write");" J+ d9 @* {2 v* `( ~9 ^
- break;* s5 a( t) H! s2 p7 A
- }0 K6 `' c5 |% ~5 ]
- if (strncmp(buf, "quit", 4) == 0)' O0 M& k# X* a0 e/ A
- break;
+ R6 W3 Z/ \0 F1 Y - }2 K8 f+ g4 a5 \* R
- close(sockfd);' E. I8 [' W7 c- T8 H2 T. ~
- return 0;1 ^/ s9 `- q" a+ Z+ c
- }
复制代码
: A# B( a! l& p7 Y" n5 b; H" V
! b. }5 O- k. |6 _0 S" a% _/ @- O |
|