管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
' }4 s! ~% |1 k9 ?5 S" x7 L) S4 P2 @/ ?$ l; R! e& d/ z
! p8 o+ k I0 N: T0 l6 \ S7 d5 r
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
9 N% f% _, W! @4 y4 ^5 T) @
' h+ S) i8 |6 Q$ h, ^) T$ d
( s; f3 A3 |+ t+ V6 qTCP协议
% D: }7 N+ ]8 S5 n6 yTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
8 G @, l" I) F% K( o; w; \% o# ~/ z/ r% N% {& w$ }
o/ @: K% C R0 w5 G+ O& Z# A# H. G
关键词:三次握手,可靠,基于字节流。- f: W! O7 @6 Q/ w+ ?
! t) r( I7 ?7 B4 h4 R1 h8 s5 V: j' {/ F ~( ?6 X- w
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。6 i0 z T, q/ c6 a& l0 D4 |/ @! G
8 }+ w7 s5 d) PTCP服务器端和客户端的运行流程" R: k9 _/ X+ Q: y4 w o
/ J- f9 I) @) {$ m, N4 j6 Z, H- O: o9 R0 Q3 ~2 `2 G6 F
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
" l* X9 x! Z) Q! V" _- c
- M: b7 F: I/ Z* j0 h
& Y2 B- @8 `# k4 E$ E* F1.创建socket
1 W; T# l" M, ]' Z# u$ ] socket是一个结构体,被创建在内核中
5 [. {, B: e8 F3 r0 o sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议; h6 u: M6 a; T# H/ Z* A8 M0 i
/ M8 f4 {8 X/ M8 k+ O9 Q- S2 O
; ~4 Z8 k7 o9 L2.调用bind函数
; {& h) a1 U" t" j% w 将socket和地址(包括ip、port)绑定。" ^3 W* o3 F, |# [& Y1 T
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序" \% m, y& J* k1 u
struct sockaddr_in myaddr; //地址结构体
2 J2 Z- [9 b( |0 e3 J2 S% h8 k0 E bind函数
8 S4 F- Q$ a* ] bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)); X* E5 V; b' f9 B( R8 s6 D5 L
; G$ E; l- J: z3 P
$ U$ |6 r# m: a/ k! p3.listen监听,将接收到的客户端连接放入队列/ B8 g' U3 B* m5 e2 @9 V- T
listen(sockfd,8) //第二个参数是队列长度
" F% L) n! @( u$ J" i
. T \! {0 D& Q' v
$ R3 A2 _) l! {1 @: S5 X: l4.调用accept函数,从队列获取请求,返回socket描 述符
3 W# l9 E; D: E 如果无请求,将会阻塞,直到获得连接
: C# |1 E* U1 X5 \, h/ Q int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
. ]2 G7 a3 r. E. @1 f* T
9 A5 m4 ~) p' p1 s& z4 ~
$ T+ H! M+ d# t3 S! W( w5.调用read/write进行双向通信
/ _9 t" W9 n% |- |6 U- s! b0 S) X, u4 q4 {; C1 Q) @1 q9 Z/ N( @
1 `- Y. `1 p0 {! c. U* @6.关闭accept返回的socket/ x) R2 e9 u- \
close(scokfd);; ~" v6 d6 |0 X& @: l
) ?4 y0 o g% s8 {& {
5 @* I. ?% S; b. V3 E% T) j0 P/ |$ N
' n! P# o: w, ~' Z; O3 `7 c8 q0 ]' e& W7 b! L6 E
下面放出完整代码
, E+ A3 l" U7 k# Z. h& L7 p# G
5 @* s' ]" x8 m4 X$ ]- /*服务器*/
0 D$ O9 U/ Z3 p, m$ @' D - #include <stdio.h>
4 {7 D# C Q8 [3 S$ [1 \, k6 f) t - #include <string.h>
$ I9 b# J3 P, a( U - #include <stdlib.h>
* |: o. v4 \% f9 @ - #include <strings.h>
0 i. M. m! g- F+ \$ l" h2 b - #include <sys/types.h>
2 G+ T. [, a g* u, ^7 \- {4 J& I - #include <sys/socket.h>$ A; p6 \9 O( F. y2 |
- #include <arpa/inet.h>
6 Q0 v6 x- H( R% a8 u( M0 T - #include <netinet/in.h> x8 P9 Y" F) a& W4 M9 Y. w" J
- int main()% L& J& w. w7 K* k( X) w
- {8 v- P9 F9 t- d( I& f
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
) T& O. d# Q% x$ m1 m/ P - if (sockfd < 0)
5 [9 K1 X) M! u9 p9 u9 B' Z- o; ] - {
- V8 p5 ~( p4 _. a9 n) }7 e* f) d8 L - perror("socket");
/ [ @' F( h6 s8 j Q, P: D - return -1;
8 q* t* X: H+ u$ [* z9 I - } //创建失败的错误处理6 ]7 C! r1 L+ X3 p/ i6 b1 T
- printf("socket............... {' Z1 n0 Q( z* V4 E/ k
- "); //成功则打印“socket。。。。”2 H% H& O7 O; ^8 |* t/ l+ v
-
. d& n8 C! w4 Q, I$ k - struct sockaddr_in myaddr; //创建“我的地址”结构体2 u' H9 e7 w& S- z
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)2 y! c3 g( T7 L
- myaddr.sin_family = AF_INET; //选择IPV4地址类型! q# ^7 A7 Z% S! I0 |
- myaddr.sin_port = htons(8888); //选择端口号
! Q& [4 T( r1 i - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址. S) Q9 H: Z6 m( Z' h
- + p1 i( Y* J" A6 r4 u
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字, o( O- Y7 D" ~4 r
- {
6 r% `0 W: O3 Q9 g; B* j - perror("bind");
* d9 F) P2 A0 q! s. } - return -1;* z8 x# z- n4 r; _
- }
! F. i: d3 n" b# B5 Y6 _5 P - printf("bind..........
+ s+ m/ J' @6 p - ");
* P: j" v! H$ e/ z -
( J; G: f4 ~% S" G" |9 p - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听% |8 l6 J! t+ B- h& g
- {
2 P2 P# P; o" K$ Z2 Q - perror("listen");
5 F$ @: `1 f3 @ - return -1;
8 Z8 c6 b E. ?) { - }
* D5 o+ q. k) _3 a. H! x9 C - printf("listen............
% m5 m. e0 ?$ a& p7 `' v - ");
% o. S; M8 ?% j - % V8 Q) {" h6 x, k3 p* a0 k
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
) X9 ?4 t$ d+ [" X - if (connfd < 0)8 w+ S, P" n' p x4 Q. {9 r
- {
( q) Q u" J0 K! m4 V* `$ N. V4 V - perror("accept");
, w; x1 ^* n* [" v$ F( t+ G - return -1;! c) i" ~5 a+ X6 K; N H. ~$ ^2 k
- }" x0 D v) g. t
- printf("accept..............
6 W: M# f; Y: t M, V+ U - ");$ V) v* P9 s: r6 ~. ]( ~. D
- char buf[100];//定义一个数组用来存储接收到的数据
1 P0 l0 L: \& j" C+ e+ [ - int ret;
" m8 C E7 h4 r7 H5 N7 N! ? - while (1)* e2 ~* M8 f+ |
- {6 l% Y" [% V5 t; E/ ?& I" M
- memset(buf, 0, sizeof(buf));8 }' H6 s9 \% x1 D, }+ O
- ret = read(connfd, buf, sizeof(buf));( I8 ^' I! X8 k4 h7 s% ]4 w
- if (0 > ret)9 { v* F/ c1 Y: m
- {/ P7 a8 l( K7 `
- perror("read");
" L% Q! c4 k& j - break;
) t* [( u f8 O& y, r - }//执行while循环读取数据,当* f% `! _+ m0 A; {
- else if (0 == ret)! A7 N2 R2 X1 y" c4 S/ b3 n$ u
- {) e6 W S2 G+ G) V1 y9 t8 C# N
- printf("write close!
2 D+ v& ~; n% u" h$ g g - ");2 u) s* y% I f
- break; N" B/ L2 B& m1 [! T5 u5 ]$ [$ i
- }
' B) j9 q H( S4 C. T - printf("recv: ");
5 s; t3 B+ {4 A6 R* E2 d9 C' A' H7 h3 Y7 u - fputs(buf, stdout);//打印接收到的数据5 w' g4 D/ R6 Q, D5 l8 X7 \
- }
1 W1 b( J, M3 J9 W4 O) T - close(sockfd);//关闭套接字
+ T8 w5 [4 S6 y( P) D - close(connfd);//断开连接
/ G, x- P" I; C! x1 V/ b) f& ^1 Y - return 0;
. a0 t+ D6 ^" V6 Z - }
复制代码
' a: U4 y. y3 r7 {/ G' Z6 t& X1 G s4 L: H8 j J6 {
- /*客户端*/(具体功能和服务器一样,所以不再加注释)
v, n" ~! v9 v& c. b - #include <stdio.h>8 v9 I9 L: m5 ~: L6 v4 W
- #include <string.h>
2 h m) i1 z4 N" ?: ]2 O; x9 L - #include <stdlib.h>7 \3 B; A$ z& Z% F
- #include <strings.h>
) E# p9 @7 S+ o6 p% o9 o4 A4 S3 ~ - #include <sys/types.h>8 b3 {$ X0 q# Z9 T
- #include <sys/socket.h>
7 j2 i' I: ]: m2 W' E - #include <netinet/in.h>
# S; ~2 Y+ I+ r2 O# d, ~3 w' {0 n - #include <arpa/inet.h>
# G! F6 X0 k; p* x) M b2 p/ N; W - int main(): X' A/ ^: W# c- C, `
- {
/ y" n+ o+ a: v( K. M$ N - int sockfd;" Q5 v# ?3 z* E4 y( R
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
$ {; g( y Q) @* Z+ W, L - {8 j9 d% Z6 @4 ~+ G3 T' n& R' b
- perror("socket"); c9 m& q. n$ E, X m d- l
- return -1;: {, b5 x5 B4 @3 x* ~: ~! t& t
- }
], E. \" x$ [% z% @. @ - printf("socket...........
9 ^8 ^! I1 d( j4 r3 p - ");6 ?, W/ M' q) C/ F8 I
- # f. P7 J8 o7 V8 v
- struct sockaddr_in srv_addr;
7 ~5 f+ u' I# d* P - memset(&srv_addr, 0, sizeof(srv_addr));
/ b% ]; l2 r" v! Z) W2 e' C - srv_addr.sin_family = AF_INET;. o. Z$ S$ m# y, |) _* \
- srv_addr.sin_port = htons(8888);
9 ?3 J. V, t) [+ V - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
! a" Y0 }( C0 l' }, j% V - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))9 k! X! g; l$ P! l
- {
. q1 G: L5 N3 v+ `4 M; b - perror("connect");
" _/ @; E. f" z2 `( Q# ]9 ~5 Z D9 H - return -1; //exit //pthread_exit h* e1 }$ w4 X" t
- }
9 l; H. {4 U8 { - printf("connect..............
, t! n0 q8 u0 ] - ");4 u3 \% O8 ~ Y/ R( }
- char buf[100];
# Y1 F$ T/ S4 G1 a7 e( y - int ret;
: Y' G; ~4 Y# X* m" g - while (1)
4 a+ V9 m5 o' j# u3 C9 L! A6 b - {6 w1 X2 s; h) D
- printf("send: ");4 V+ q% n _( Y
- fgets(buf, sizeof(buf), stdin);, l5 U/ d( U/ r4 Y% T8 V. z+ P
- ret = write(sockfd, buf, sizeof(buf));
& z0 T, n# a* N8 l% d- ? - if (ret < 0)
$ U; i; r' `7 M5 k - {. ~$ z5 x8 D" J* d) y
- perror("write");6 S; h F5 y3 f- n. E+ Y$ {
- break;
H9 e# m4 U; x) w$ y L" T+ E - }
: w# ^$ n/ ~" H6 r J9 Y% m0 [- ] - if (strncmp(buf, "quit", 4) == 0)* O4 @; O( R, U& p; K: K
- break;+ O" V& K; Y* D8 Q' G& l
- }
/ B- V9 }$ E& k, d5 h8 n - close(sockfd);* p% y6 W2 B# O/ O! a- c* A$ z
- return 0; ]+ Z6 m5 \8 I1 T5 e
- }
复制代码
. {" l- \9 B% {* W
& O; s g+ r4 L7 o |
|