管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。' J( }9 Q/ S2 D' z" E
3 C- l- T0 {* c! T0 E
+ @4 b+ ~+ u7 T y' k
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。3 D, g+ f% {* @
, K2 _3 J) |* }
: O, H$ ?9 _! {3 G' I, [
TCP协议
! U7 H5 I; n p9 {9 E6 s2 ZTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。$ I( Z5 E6 J$ _' c
' d& B0 V1 E* q4 ~* q N
; [: K! ~/ j$ c+ }# @. m% \关键词:三次握手,可靠,基于字节流。
$ {$ W5 m) M5 p3 \: l" n: ]- L/ B m1 ^! m; E: W1 G* e. t
$ @' r9 Y) }0 b: g
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。7 H! q/ r, M) T( j' O. t' o3 g+ k5 T
4 T6 E8 |( v) N: g% ~+ [! }7 f9 A
TCP服务器端和客户端的运行流程
/ b* @! Z1 y( Z0 a# c7 @& ~# \! [4 O2 ^8 G2 ]* \7 Z
( w9 I0 h' ? y
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
8 U# {; r4 e* \3 r: g& |1 K: Q* G8 k b
0 h9 t6 Q$ S% ?7 ~7 v/ p# A0 f1.创建socket
. M" k9 e d$ V socket是一个结构体,被创建在内核中% D; P& B8 H6 |% }5 a: _
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
' Z c2 n, l. j; n) s T' i2 o* Y9 I/ M+ M! S$ H5 \$ y) K* I) d
. S$ e8 J# p3 t8 e6 W! V2.调用bind函数
$ R' |( O3 u' d% W. r! H 将socket和地址(包括ip、port)绑定。
' r" i6 v- ]9 J" k 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
* g g: R3 }- u; G struct sockaddr_in myaddr; //地址结构体
Q* ~, N0 o* r% F5 O# T5 j: p4 S2 w bind函数
' c; d$ M6 U2 r0 v( }/ @5 r: n bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
$ o/ E# |4 x! i1 |1 Y
" D. w* h3 z, F: _$ T
( T! ?2 y: L. B# S6 q7 ^/ i1 k/ J3.listen监听,将接收到的客户端连接放入队列: H* [0 S7 d$ w/ @: _ g9 o
listen(sockfd,8) //第二个参数是队列长度
, A9 R5 ~0 o6 u2 `( ?) G: z( f7 ^5 w$ J
- R, l+ ]2 V) D4 M9 j# R7 b9 e4.调用accept函数,从队列获取请求,返回socket描 述符, `0 d% L0 y S+ m! W: i1 P! F
如果无请求,将会阻塞,直到获得连接
1 {0 b; i6 h' I7 {0 X1 ^: g8 E' t: A int fd=accept(sockfd, NULL,NULL);//这边采用默认参数' z5 X+ U( ]- e5 L+ |
% p( K9 l* y6 Y! X* ?" {5 R
& H. ~& h! s5 h/ g; N) i" |5.调用read/write进行双向通信
& o: J+ h8 m/ U/ Y2 s L7 \9 d' V0 i/ V
! W6 c; K$ \9 a; X# y1 L4 y6.关闭accept返回的socket
. ]4 T: ]- ~' L! a close(scokfd);0 F! L$ l$ y' Y: j& E& l
* H; L6 x- S4 I! E' g8 y
; ?# g3 G. \- h# `
- j, z' }9 x! n3 M4 e7 W9 V" ?6 X! P# M! n; w: f% | S
下面放出完整代码& }4 l4 A+ l% ?5 L4 a9 y
3 P: h; i5 y8 [; Y9 k
- /*服务器*/
6 q' n; k* |0 X, [/ w k: c+ L - #include <stdio.h>+ L) _, T6 D/ b: v+ ?7 o c
- #include <string.h>6 m% B1 Y( {/ a% U9 w* n0 F9 {
- #include <stdlib.h>: N) x/ V( W6 K d$ V
- #include <strings.h>" S3 ? s1 C9 q+ n
- #include <sys/types.h>2 D ? S& f) f! \/ N
- #include <sys/socket.h>: A& @3 p" f( s, T7 Y/ g
- #include <arpa/inet.h>
4 [! w0 m& p! h1 z( F - #include <netinet/in.h>
2 f2 F: c! Z( h - int main()
( }$ F9 }8 u7 g - {6 m/ ^# f' f0 H3 K* \5 _: ^1 U
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字/ o, R8 H" U. p1 O( q
- if (sockfd < 0)
9 E8 ^+ r, `9 M1 P& G2 x" N# R - {% O* J- b' r) H. _$ |2 D
- perror("socket");, y4 w& q3 S; C0 _& U
- return -1;
+ L& B, C1 V! J7 g+ r ? g; ] - } //创建失败的错误处理
! u# ]% r4 B' u: J$ S - printf("socket..............
: p' g' p/ W- h - "); //成功则打印“socket。。。。”) a3 \4 p: E* u: o/ @3 R
-
& A% Q/ Q4 L. Y6 q, D5 s - struct sockaddr_in myaddr; //创建“我的地址”结构体7 S: v. B5 W; p, }
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见); `& Z# X+ e6 V9 F: e
- myaddr.sin_family = AF_INET; //选择IPV4地址类型( W% n$ g; {( b& o# ^9 g A Y/ c; z
- myaddr.sin_port = htons(8888); //选择端口号) I0 o& F r+ }4 a# ^
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
5 r9 I9 a9 v' U* u0 ~' _ -
% U* K1 p, Z: D) |( g! @8 S - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
- F5 L" P" Z2 l$ I5 ? - {
/ [/ P4 u0 A+ s f! h - perror("bind");
7 N4 [* O) G, ?- A/ q. T - return -1;9 V/ [0 B2 l) M
- }$ V6 k3 Y6 k4 k: F. |+ O0 b
- printf("bind..........
+ [$ {) r4 v; U6 S# F u - ");
6 f0 |9 j0 h& K5 }" c/ j3 \6 i -
6 B$ H, G8 y, I0 L - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
: `' T: d' `* F - {
- \# z2 N( @; ^: j2 h - perror("listen");
# @# R+ }, G* c& @, K - return -1;4 |& `# @5 d( q2 g
- }
! I* ~! P" m1 q# ?9 J8 l% l) c6 f - printf("listen............
$ D2 h! l' h/ \8 i8 O. u {$ U - ");
3 R" O1 @; n3 M p: X -
6 @/ y4 v/ s( I' Q, G4 k' w - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求3 x$ Y( `/ Z% z' r7 c0 B, y
- if (connfd < 0)
' ~9 D, \' v f- ] d# c - {
* Y: Q! Y! k( f* ^ - perror("accept");2 y2 X: _& w# u, I0 V; `- @
- return -1;
4 Z: j2 F4 o. Y - } u8 C" r4 @% D! u; t
- printf("accept..............% k) N/ [8 c1 d: M0 }
- ");
3 c& X5 B& F! k! v# e; i - char buf[100];//定义一个数组用来存储接收到的数据9 H1 {1 X7 D9 Y
- int ret;" _' K7 e/ y4 p. U; W
- while (1)& Y; E+ l- K; A2 d U6 ]- M, V' l
- {( p' o+ j. M! T0 v2 V# v% l5 ]
- memset(buf, 0, sizeof(buf));
, Z+ s3 _/ i7 h - ret = read(connfd, buf, sizeof(buf));5 z2 L! H) m9 d4 q/ R
- if (0 > ret)9 r! {, s4 ?8 i" c1 W- f
- {
. n# N; x3 l, v. Y% W4 Q - perror("read");1 k7 B' w( k) y2 K
- break;
# W; a1 [8 n& X2 K7 a - }//执行while循环读取数据,当% G0 [% G: C. p7 |. D N* s
- else if (0 == ret); }3 Z" M6 {0 T& ?) F$ f
- {
8 l) ]/ G! R, s% ?" P7 c" P; K - printf("write close!
! r, f0 ^! d" H' q$ @ - ");) i& C$ ]: k' h+ B
- break;0 D# w: S) v1 z8 L% @5 C
- }# B T9 J; k3 y* B# R% ~
- printf("recv: ");! Q0 C& i! w, G# Z7 A% c' o
- fputs(buf, stdout);//打印接收到的数据! l- ]) ^3 h/ f
- }
4 N; ]% I. f; o - close(sockfd);//关闭套接字
7 {8 N+ Y5 Z. J4 ^0 A! D) L" A1 D - close(connfd);//断开连接2 K8 r3 q$ r) ^
- return 0;
/ H2 i9 B( `: R2 S3 m1 _ - }
复制代码
6 [2 r, B, ?' V* `+ Q) k" j
- P" e+ U3 ~* c i3 X- /*客户端*/(具体功能和服务器一样,所以不再加注释)
3 J" N6 z1 B8 p) y9 V' e. A+ Y - #include <stdio.h>
/ N8 X* c, V! D |/ q - #include <string.h>
0 l. z+ q" v( e' D) g - #include <stdlib.h>
( R: O/ N5 s7 ^& l- q1 @ - #include <strings.h>0 ]+ Z' j, e: ^- X
- #include <sys/types.h>
2 l5 \0 B$ g/ j; Y5 i! w - #include <sys/socket.h>
2 {; d4 r8 X- y. b5 w& ^ - #include <netinet/in.h>
. m/ S6 S1 |5 T5 e) \- q: j! U - #include <arpa/inet.h>
|- O8 g3 ^5 a N) ^ - int main()) V& o; f6 l. D/ d v, M) v
- {
- s: `1 T5 C9 E4 N! A, B, k - int sockfd;
$ {8 p1 D& q- V" o$ e; I - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
; A: W0 k2 e( M! G+ ?4 L3 s - {
( @2 [* }/ X/ X) C; b0 w& C6 H: j - perror("socket");
& b" C Y/ L2 ~ - return -1;
! r/ I% ?2 Y; g( O' m% I - }0 _( g8 X! F, e' l' y9 W
- printf("socket...........
' O+ l8 U; n% R- r3 x' y* { - ");0 O! X( s/ U+ g; W/ N" p
-
7 o9 u( d9 c$ Q6 U' S - struct sockaddr_in srv_addr;8 F: m$ Q3 u6 ?* u
- memset(&srv_addr, 0, sizeof(srv_addr));
% o& v/ T3 q* n( U - srv_addr.sin_family = AF_INET;
N9 ?! E2 f; V. u - srv_addr.sin_port = htons(8888);
0 j! B& `) N7 Y3 f& I5 [8 s( m" Q% R - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");2 g7 f- N+ T' {7 Z i1 V
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
1 w" {2 t8 \+ }, K% `" H - {
" Z. n& [+ r6 l( c1 L4 t! p% i) Y; V - perror("connect");6 b5 O: W! j0 H/ X4 {% l
- return -1; //exit //pthread_exit
& {: Q! `6 z7 b5 d+ z8 F - }. N2 `" u2 d# A
- printf("connect..............
! q+ N3 J0 i& Z) q! K% j - ");
% S. L' F* n# j0 L# ~ - char buf[100];
, ^6 ~' p% ]) d/ ]' a - int ret;
: D1 _8 |0 L7 ` - while (1)& H/ l+ q0 i1 v
- {7 ^2 [' u; O+ n6 O N/ `
- printf("send: ");
4 h7 `1 y6 w& Y) ]+ Q J5 K' ]& V- N( i - fgets(buf, sizeof(buf), stdin);4 Z, W( R8 C( w) G9 ^- f5 f- V8 E
- ret = write(sockfd, buf, sizeof(buf));
# [) ]) {5 _+ p1 A3 n | - if (ret < 0); T7 \ D }$ I, D
- {
$ d, r9 {1 T0 a2 F) [, F - perror("write");+ c; J, ~8 F5 X! n2 o
- break;% D* Y8 [; ^! S' A
- }- c% @5 N0 m# K$ i: C( `1 O) s& D
- if (strncmp(buf, "quit", 4) == 0)
- A5 \! ?2 [( N v9 c - break;# h% q( E6 f3 B% @% \* ], d
- }
7 Y5 C/ a! B* w8 a9 ^6 B% S; ?$ R - close(sockfd);, V/ r5 E+ w2 O) K/ _
- return 0;& U8 d! g+ J# m* E/ V! S/ L, B, Q
- }
复制代码
0 k- j6 @ d7 l1 V y z! x; m( r2 J9 _
|
|