管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。+ b% w+ V6 L3 }8 j
/ }1 X. o3 W) h) m- n0 c4 U, }1 c: }' J( m
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。& E$ [( y& H" S% D7 `6 C
0 f1 I6 q) q5 t9 R
Y! d, S! X# \8 ^; I! i$ A o. J c
TCP协议
2 ^: r- t+ d$ k) E: O7 O( f% aTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
3 M; P" B+ S- h0 _% v
; I3 \ F5 K7 T( n, q5 [5 W8 H8 }$ r6 \9 e2 R9 H
关键词:三次握手,可靠,基于字节流。/ N7 e& v* t; T: J- U$ H9 t- Q7 n
' }, W: Z# x1 f0 t* b
& s7 S' b# g0 o1 @# r8 y# w0 B可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。$ U! |* X% p8 J" e+ ~
1 w* `7 f2 e+ I; ]1 n; x4 ATCP服务器端和客户端的运行流程
- n6 c3 G9 m% \+ J' {& o" h* d! Q$ w9 `
: |! ? V& r' t) B4 ?. E R, E如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?: ?; c/ G7 F, M+ i X
) K, w( }: X/ q. [2 o- w7 H8 ^( m9 ?
1.创建socket
% c- V( I* K2 p- h1 ~1 m' } socket是一个结构体,被创建在内核中 x9 F7 b& N8 E1 | M$ h$ b/ H) B, y% q
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议0 R- G* u) i% I2 O; N
3 n; W7 Y0 x$ S
P1 i* [& |3 ^+ o1 d6 N2.调用bind函数0 W4 ?% F" H$ |( a- u
将socket和地址(包括ip、port)绑定。1 @0 d" D0 l( e' D% V
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
+ h3 U/ J5 K. {% e4 V; a0 y struct sockaddr_in myaddr; //地址结构体
2 b' P# y: O- P, X; e A bind函数: r2 ?! Q) V# a2 H1 c e" K( l
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
: Y( W O- G k4 t0 S$ C
3 ~! _# d: o/ I- _3 V7 T6 A, N @! |: h. v3 |/ x
3.listen监听,将接收到的客户端连接放入队列; f ]( C0 x) G: v! G/ `
listen(sockfd,8) //第二个参数是队列长度3 s& W( l0 r, j. z
* i, {( m. V1 y2 Q6 ~" l* {7 X2 w7 }7 }/ s9 w
4.调用accept函数,从队列获取请求,返回socket描 述符' M4 x9 y8 d1 a5 V# n3 }' h
如果无请求,将会阻塞,直到获得连接% V9 A6 U" a; _* G9 n5 Y U0 l
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
9 m" q4 t& x5 c8 a7 A' c4 }, ], S) ?- E/ w1 }) d
5 D# }( z# n( ]$ Y3 ^5.调用read/write进行双向通信. r* Q0 ]4 A; z# w8 p) Z, A
4 t9 T) _. d5 P" I$ o( ?6 |/ a; P# Q1 G2 T& x x6 x
6.关闭accept返回的socket
; p- F0 T- q4 P1 ?/ S* H close(scokfd);
0 d: L: ~! ^7 c* F# z* Z6 o
' S+ M5 X4 \. H
! f* V4 d6 J( P/ x& |6 d8 ]
7 {' J$ z' a" C( L* g9 h$ v% I B. L; `3 u
下面放出完整代码/ l: t9 d# w# }8 [
- N7 S& G H2 v; `- /*服务器*/
( _5 u' o% B: `4 R- n - #include <stdio.h>
/ K( S$ q" k& F: u F. n - #include <string.h>* D8 H( U' m. q
- #include <stdlib.h>- C3 v5 A' k% p! ?0 O6 k8 f" ?) T$ }3 D
- #include <strings.h>! N# ?: t ^' m* G
- #include <sys/types.h>
, J! L8 z, M/ t1 [3 S! t ? - #include <sys/socket.h>- K' n6 `( U4 L; D1 i
- #include <arpa/inet.h>& A6 {6 M, v V& j, n$ A) n# m+ T
- #include <netinet/in.h>% M2 K$ c' I3 y8 D' |7 R8 J+ D6 ?
- int main(): }4 l( b3 g3 }
- {6 B( @+ l9 h4 O: O
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
U( t+ B& m" }" a8 C' A! X - if (sockfd < 0)# ?7 r9 t e" O1 |
- {. y* q) w1 b1 F9 X W. E2 ~: e# V
- perror("socket");1 m+ m7 i( Y# o. j
- return -1;. N6 @% m9 S& A- a2 i
- } //创建失败的错误处理
A4 o# @# b; D: A8 W7 o5 G$ `! A - printf("socket..............- Z4 {2 @7 ~. l
- "); //成功则打印“socket。。。。”! d0 l) P' n: ^& X1 g
- " Z9 {* ~- G( ^* C5 c/ V% V
- struct sockaddr_in myaddr; //创建“我的地址”结构体
2 x/ X0 ^3 Q7 Z) h6 _" R - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
, A8 L' h# \: A8 K; Y8 D - myaddr.sin_family = AF_INET; //选择IPV4地址类型
& V! t N+ M9 Q - myaddr.sin_port = htons(8888); //选择端口号* j# Y+ c( F0 f4 _$ f0 b% X/ _
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址6 a, q5 b+ W' U! o; E
- 6 p$ ?) {, s5 C% X, W: F8 w K
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
! J" p& g: m* ~4 {* H: o - {
& m+ F% {! [9 I$ r3 o+ z$ | - perror("bind");( T! p' f o* f* n3 e# d
- return -1;
$ }& g/ p6 b! |# R7 r2 P3 f! I - }
( G/ G1 o3 ?6 M# {% O6 S) w - printf("bind..........
8 A F0 s, ~% E5 y4 Q$ s5 b* J3 { - ");
9 e9 [% z- R H - . \) D8 F9 J, M7 C; [
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
: I- v0 A( K8 J1 I% c - {( A2 \1 b% g+ H3 n0 Q
- perror("listen");
& x; p9 Y. m! u - return -1;- ?& f9 B; t* x! |2 }7 l! S# O
- }
2 i5 |: e# L& U; m - printf("listen............
/ t, ^% v. ^6 @9 t N5 M% N - ");
K, U" R4 M* T H3 E9 J, `; P" x - " W8 i: l, P1 \. o1 r
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
, A& q1 y$ c! F$ e3 @0 d6 ^ - if (connfd < 0)1 d6 h+ M" m# W6 z4 h( }7 ?6 `7 t
- {9 t' R! G5 `. i
- perror("accept");
% z! g3 @6 M5 d. [, x2 j - return -1;
2 f1 F9 v/ J8 b% {# M/ \ - }% J$ V" i- y" t
- printf("accept..............
& p+ ~8 e5 H. C+ | - ");; C! {" G, t& T" M$ l8 A
- char buf[100];//定义一个数组用来存储接收到的数据9 v W2 V7 m% e' x
- int ret;
) O' V+ ~- K! u3 l8 g - while (1)
: y0 Y& {1 Q( y+ F - {
8 u5 {: Y Y6 m - memset(buf, 0, sizeof(buf));3 D8 p" ^9 Z4 o4 J! K/ p5 R! V
- ret = read(connfd, buf, sizeof(buf));
) W7 C# f* T, }' ]) w - if (0 > ret)
Z- \. J! L0 @( o8 W. z8 ^ - {
0 O6 J& p r1 F" I0 M - perror("read");
: Z, L' [ e4 d) Q - break;
; z4 U. B7 S: W3 C$ I2 i& p! I - }//执行while循环读取数据,当. r( B( y( q. N2 o( _5 B
- else if (0 == ret)4 F. W. N+ L4 ^- i$ k
- {3 l$ [" Z; g$ @, y2 f
- printf("write close!
6 X) F2 Z2 W7 H* y* E - ");
E2 n* I" O. |& u1 k9 [ - break;4 Q9 v3 M! ^% j
- }
, X- X. n6 A2 |/ N, E) [* R" m - printf("recv: ");
9 ]) `. c8 b4 [7 n/ w4 l - fputs(buf, stdout);//打印接收到的数据
! A, ]9 d: j; p/ e. O* J - }
, k4 X- S1 D# `) Z/ ` - close(sockfd);//关闭套接字
) \ K! R( N2 [- m - close(connfd);//断开连接+ r0 X5 B- K$ K! o
- return 0;
5 o& x! A# ^3 T- U- K - }
复制代码
$ ]5 \2 {! a y0 ^+ U w& h& e! v- |: S, \$ Z1 Q
- /*客户端*/(具体功能和服务器一样,所以不再加注释)& w% l( S( F, x2 s' ?
- #include <stdio.h># t0 X( K+ }6 {. D2 n% W, d/ f3 P
- #include <string.h>5 i# D4 N7 }1 l, z/ I* H4 U
- #include <stdlib.h>
7 n9 z+ [" C% ?7 R6 H. w# D, z - #include <strings.h>/ Y& e5 p$ s6 [9 U' }: W* u% n( \2 F
- #include <sys/types.h>4 y$ B( T4 Q: d
- #include <sys/socket.h>8 h" B" N7 l2 A, t M
- #include <netinet/in.h>7 q6 ?2 \' c2 x; X
- #include <arpa/inet.h>
0 f2 u$ G, x7 `2 ]6 L1 n7 w8 ^, `9 L" L - int main()
, s7 S: p6 B. [, R9 m - {' l. r- t$ F( k2 A0 i" r, N
- int sockfd;
' |( V+ [$ N8 G* |1 W& ~; Z6 \ - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))' r- K: B, N0 ^ T7 K" A N
- {
7 U3 b# S( q* W2 d* u( C; m - perror("socket");2 ?& _/ L# r: ~ C2 I4 O3 Z5 r. T
- return -1;. o6 m" H/ i M& }! O
- }
8 z5 \ V% e. b# j4 ^ - printf("socket...........
7 I9 Q' O7 ^6 T( h" J. p0 A, q - ");
. ?, H* i. L: f5 A% B -
6 L9 c) V; r: Y6 m' i3 k - struct sockaddr_in srv_addr;
( S" I" H; C5 t - memset(&srv_addr, 0, sizeof(srv_addr));0 T' z9 _6 A9 I, N
- srv_addr.sin_family = AF_INET;
4 |% f/ E( i4 u& i3 t' x - srv_addr.sin_port = htons(8888);
" p# F# p. C i9 V! R& l( H - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");! ~6 |, p0 f0 Z' b
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
9 x" P9 d6 y3 a. Y' S, J1 T - {# E2 o5 B: z) b( t5 k: D
- perror("connect");
, A7 g. Z: V4 V- S! c4 @ - return -1; //exit //pthread_exit
5 ]) k- A7 e5 A2 g" W( }! x5 l - }$ A1 x6 J: s1 @; J5 a; G" m
- printf("connect..............' |! `, f$ H$ L; Q
- ");
9 a7 m7 s2 C( r$ g - char buf[100];& K; q7 t( c$ \8 R3 M' k% f
- int ret;$ b" ?+ k& @ J3 _
- while (1)
. G2 a3 [. D) _! A3 v- H5 H - {0 F4 v6 E" j5 K/ X6 |8 c0 G- }
- printf("send: ");' I! w- p3 b6 l3 z; b
- fgets(buf, sizeof(buf), stdin);: ? i) g; F& }' @3 q8 D
- ret = write(sockfd, buf, sizeof(buf));% W, D) i, T1 W1 t" U
- if (ret < 0)
, Y; s) D; Y( X0 ?' Z - {# y6 ]* G6 E U
- perror("write");" @# [4 s; c. N* U Y
- break;
( s4 \5 Z1 j" N2 h9 \( i9 p - }
8 I% n: L( o! \' H# J6 ?' { - if (strncmp(buf, "quit", 4) == 0)
: i4 m) I$ H( m1 D: C0 k! w4 l - break;
1 C4 h( a- ?, s7 D$ X - }
3 l3 S3 p1 K% K. V1 u3 O - close(sockfd);
$ e! ^# `7 N. }1 p4 J- Q6 J! g - return 0;% H v4 ^5 Q q
- }
复制代码 7 ~* |9 ] Y1 j! Y7 Y& T2 \
1 t2 [- I: f$ s6 g1 H w D1 x |
|