管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
7 a2 r( D5 L9 D$ M
' p% ?/ B1 W ^- ?/ I# A% Z7 w* C& L v2 D S
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
4 ?$ V% F( ]. k4 r- u" y6 ]- ?1 e& Q4 ~. d# D
& t" Q' l! V4 W. tTCP协议8 _- @, P z! _ }( ^
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
4 g& Z8 R6 e' t3 q# ], @7 V2 F: \$ {" K( q
5 T" t* B \( @4 s! y+ ]4 w
关键词:三次握手,可靠,基于字节流。
7 k) W7 B+ t ~* x0 T2 h$ p
) n" n% q: W* H# i4 [ w
0 h' d* P0 c% J9 Y e! G( u$ J可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
; ]+ J+ q, u2 G8 m( \9 d
+ p4 A9 w, A4 f# b7 M
TCP服务器端和客户端的运行流程
/ d" d- y8 c3 p+ x3 _. p1 L/ { H
6 j; |* V5 J4 H1 }- O( D
/ r+ U& W4 |& a1 _; o如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?( t- b2 n9 f" {2 W% G V2 g& K
4 ?. K; }. S9 W* H) N; Z; j' H7 d7 P* K r
& J- i8 f* _# `8 e1 p; {
1.创建socket
+ v$ r3 I- k9 r& Z: r' a, l+ `7 ` socket是一个结构体,被创建在内核中
8 y9 n, t4 n& B) G6 X8 x sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
, P1 o8 j( \9 H$ K( Y8 M; s' K+ V: I- C( o0 X
/ o$ ^& X4 G0 ^: |) [1 s
2.调用bind函数 M2 |0 R( t( X& y
将socket和地址(包括ip、port)绑定。
! {- @+ U6 A' a; z! Y& ^" I 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序! M& K, j* d, u
struct sockaddr_in myaddr; //地址结构体
8 x+ [8 V3 R% X7 @2 X0 e9 y7 U bind函数! s2 A/ m' T1 T
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))7 ]2 V, g$ O- g) c6 E
1 P: I( Q8 k2 K3 [3 Y! t0 B
' a. T# A& a% s* T3.listen监听,将接收到的客户端连接放入队列
( l/ o9 j- P# G listen(sockfd,8) //第二个参数是队列长度
8 h/ I) B5 X7 }8 c7 r+ h: @- E6 n* H7 h0 j6 D( M1 d8 |
. l7 y# N! K7 J ]9 I
4.调用accept函数,从队列获取请求,返回socket描 述符& X8 ]. q/ W! {$ ^
如果无请求,将会阻塞,直到获得连接+ ?4 y' @) n, u5 m8 Q d3 T
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
% R" l5 N2 z- ^8 F4 \7 L0 P. L( q+ E: e- p* O" {
+ \$ v2 O8 P; d0 t6 z' f
5.调用read/write进行双向通信
8 }: P4 X- u. W
: Z/ M+ l' }- s* y) n- Z$ F( j9 `8 l" ^ L1 Q5 ?
6.关闭accept返回的socket- g. _3 V- `8 R% n+ O5 Y5 p
close(scokfd); k* j) Y' H( a( U8 [. R2 X5 }
; \2 F* x( o* y) d
: z: Z9 j: v% a& a9 O
8 m: H; T: H6 t. Q% w$ z* D& m5 | p) U- V
下面放出完整代码2 S0 W0 s, [: x( J+ }6 Q
/ Y! l7 C5 o5 c2 Q b$ ^$ C& y% x+ \ Q
- /*服务器*/
% O4 e6 ^' S7 s f1 c* c - #include <stdio.h>) f5 e8 V4 n4 j$ p" b
- #include <string.h>+ Z7 h b4 P& E
- #include <stdlib.h>
1 A$ K3 ^: K" f/ O2 h7 n - #include <strings.h>% L) i9 k) V. ?1 q% m: P* s1 r$ T
- #include <sys/types.h>( G) w1 ?4 M1 Y
- #include <sys/socket.h>7 Z* q' X- r" B6 D3 A' x+ h
- #include <arpa/inet.h>/ g( v: x$ T* N8 ~9 C& q& e2 ]
- #include <netinet/in.h>1 G/ f0 B# l- t( G; [
- int main()
8 A U; @1 c* {/ V - {
N& U0 Z& b' h+ r - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- [- f2 w" q2 {& ^- f5 W
- if (sockfd < 0)
# @. I6 y( \$ a) n - { A) V: {# R2 k' @$ z6 r( b* J% v$ G, F
- perror("socket");
9 t- ~! I' f! S9 d - return -1;
]6 G: M& o: j( Z! J: Q. y( O - } //创建失败的错误处理- m8 R6 E; u0 P+ |* R5 l: g" I3 A
- printf("socket..............
- M b" n! K9 p" N6 o1 @; ? - "); //成功则打印“socket。。。。”
" `& I* o: m! p - 1 z. v6 _2 G3 T5 a* s; F7 H
- struct sockaddr_in myaddr; //创建“我的地址”结构体
. B; \! \9 {6 {! [. U% w/ Q - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)% I+ b1 |7 x+ q! ] r
- myaddr.sin_family = AF_INET; //选择IPV4地址类型7 U( [6 A; N( K i9 ]
- myaddr.sin_port = htons(8888); //选择端口号
% c/ z( K( ~" U - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
9 ~5 {' w* i6 v5 F8 o; f( C8 s/ x, b -
0 i, J6 u' {# d* H5 }% i - if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
& K8 N" \' t, Q2 P& \( K* b0 [ - {, M+ g# V( [. C) z6 O1 d" [9 \, M
- perror("bind");
, J& V' [+ b3 C) Z/ k1 V) r6 ~ - return -1;
3 F" t6 `+ T7 F" T5 k, z' W - }8 w4 K f: b& x) H' A* T8 R
- printf("bind..........4 }) b$ G" e6 a: S- x" J8 L% l/ W
- ");
U. y+ z2 K0 c1 H, f1 u7 g -
: O* W2 s% V; E - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
$ H2 O) e$ R. W7 [3 U - {8 r8 `8 v8 {$ `5 l5 |! J
- perror("listen");
4 R, _7 C/ w3 {2 I) n s% t3 o - return -1;
! Q) ^3 z3 ]1 M2 H; R: W& i' T# z - }
9 ?: s" F5 q3 K& P) v) {, E - printf("listen............/ H1 m+ s* y/ r% i
- ");
5 k: |& }& b5 h2 T, P7 g5 M" r S2 F - 5 l b% y5 ~% S2 M8 r
- int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求. f$ B# ~6 c' x
- if (connfd < 0)) o/ }( d3 a8 m+ \5 R6 J
- {
2 o$ m& b* v" q) N: n+ A( y( f) C- \ - perror("accept");0 x$ M' M$ i: V, b4 _! }/ p. c* {( f
- return -1;0 b* c/ q5 L/ { q
- }0 j( w8 F2 D1 f( N
- printf("accept.............." B$ ?0 x- g7 E# e0 Y) [& ?
- ");
8 V3 \) D5 x5 T5 M4 Z( w# m6 l - char buf[100];//定义一个数组用来存储接收到的数据9 r/ V; O1 f9 l! j+ o
- int ret;
5 j8 _* q& B$ m u; z0 M6 x - while (1)0 H/ I4 ]' ~: K, r7 P& a9 x6 v9 P
- {7 @. X0 \7 y9 u3 y8 e+ @
- memset(buf, 0, sizeof(buf));9 p* u* s) ]( U d& w" y2 G9 x
- ret = read(connfd, buf, sizeof(buf)); P; z8 z I' J
- if (0 > ret): a1 u/ Z' B/ R5 R
- {: M# U, B1 o. G# p( N v
- perror("read");
, U! y( B! o3 { q0 I/ c$ l - break;
3 a: c9 o q9 h) @) z( Y2 x - }//执行while循环读取数据,当
8 W: Q: T4 L" n" V4 K) ] - else if (0 == ret)
4 _) `# y3 B6 l, L - {
8 l* o6 K( |# o. C4 U; ~8 c) u# B - printf("write close!
# e/ b% d$ y6 N - ");
1 g7 h7 \. M5 g! e/ d' _ - break;
1 w7 Q' s3 [; v - }
" n9 ^ z" j9 s0 ^6 w9 q - printf("recv: ");
m( R- J( S$ v/ z# n% _ - fputs(buf, stdout);//打印接收到的数据# H5 i0 N1 v: L* a+ Q
- }% G0 H2 ]4 U) e( f& a) ^: G0 G
- close(sockfd);//关闭套接字
3 _* V- b4 C' B" ^2 | - close(connfd);//断开连接
3 f: ^" I7 T# {$ c7 \ - return 0;: [+ `1 s. K# b; H
- }
复制代码
, V" F* y. d" g- [6 W) P5 j: |
- D% F: T# r/ v; q3 F" [9 S- /*客户端*/(具体功能和服务器一样,所以不再加注释) ?) P' l m+ x# \0 O
- #include <stdio.h>, c: m+ z$ {% M* f/ x
- #include <string.h>
0 R3 K) |# {8 v5 V, [, l - #include <stdlib.h>
* J5 {) W2 `1 z - #include <strings.h>$ F2 f) r9 J( l
- #include <sys/types.h>$ D# O# |8 s# f: L1 O
- #include <sys/socket.h>
: m d: M/ q9 u* e9 d - #include <netinet/in.h>
1 o8 a& p6 H7 i. T - #include <arpa/inet.h>
5 [# W7 s9 M* z9 j6 i0 u3 K+ ?6 [8 t - int main()
b6 |' z' W: y' L1 p) I7 p: u* h, \# L$ ]( a - {
, x1 r7 b+ [4 u! k$ K - int sockfd;1 r! x! s# B. j: _2 }# G
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
+ I" f2 b% b ` - {/ s2 Z2 d+ l3 J7 J
- perror("socket");, |/ w7 ^ D3 T- q
- return -1;1 |+ I2 u( o+ y3 {- l S/ g
- }
8 X3 B( V1 K! ^ - printf("socket...........
0 N2 s5 H0 _1 i; D - ");1 H5 u5 y8 `" t, q7 I. r' ~+ ^3 H
-
/ `# |: @: M7 U E4 u1 o3 p- e - struct sockaddr_in srv_addr;. o1 r, |$ _0 B$ l1 M$ q
- memset(&srv_addr, 0, sizeof(srv_addr));) t) f* `: {, H, d( Q
- srv_addr.sin_family = AF_INET;6 Y% s [- x* {0 c& D
- srv_addr.sin_port = htons(8888);9 m. \0 D( A" s4 X
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
7 n/ r4 D2 H4 f8 Q6 C% ~ - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))% P1 {) j1 v$ _/ O* j: ]
- {; G! G8 {9 R- p0 a. J5 l
- perror("connect");$ ]6 \' d4 c& |" E2 N7 R0 A0 \. i
- return -1; //exit //pthread_exit
7 [: \( |; n- v& X - }
5 ~5 A y0 ~6 K* G - printf("connect.............., H) r7 ] ]% Q$ g* d& Q( A" O5 ]
- ");, d% n; k7 u" G) k+ y6 i# N
- char buf[100];
# k# c/ [* I' `6 O - int ret;
* K% F/ m" @. j) y - while (1)4 L) H# \+ I$ T3 F- D6 f
- {6 h$ w. c2 o& Q8 q; Y
- printf("send: ");0 l5 g( u, Y: L+ g- O
- fgets(buf, sizeof(buf), stdin);
% H/ X2 n6 N1 W: W, Q# P7 B7 r - ret = write(sockfd, buf, sizeof(buf));( z, Q _6 d5 O$ }5 D
- if (ret < 0)" ^% [: i3 t' h( w6 x9 {
- {2 q' H$ R+ C* h2 i) D; f! ?
- perror("write");9 k5 G/ s G& o# w9 ?2 V2 ]
- break;: @- a; a$ X0 a' Q9 r4 e
- }0 Q% F2 q+ `1 H; g. a5 N& X7 e
- if (strncmp(buf, "quit", 4) == 0)
: w; i% ?! g$ A* B% {6 n% G j - break;
( n7 j8 {, `: o) Z6 f1 ] - }
5 i& |, F' h4 w& u* z0 t; } - close(sockfd);- U& W% \% k; V* D3 q. A/ a9 ?9 h
- return 0;
* U- M) |; q) q: ]3 {8 C: r - }
复制代码
, e9 q1 J( ^. s3 Q( ~+ C5 C3 v
1 T) O+ G: N; q! z0 b- R! z( O |
|