管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
+ w$ J4 |% t+ j8 X F' Y% d' n5 ~# `" C/ Q
" c' E2 f) x/ ?# ^8 esocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
i( D6 B+ ~. A+ [+ I4 f4 V' }. y+ R( L b$ ?% S
8 g; e5 y Z9 W# S8 f$ E) tTCP协议$ ]" ~4 j) d" ]0 \4 x# X
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
1 f% L' @7 q; G/ J0 g' ^
* Z+ @( j' t- e) v" X; M! [1 A3 p
7 W: ^6 V6 f2 O% x关键词:三次握手,可靠,基于字节流。5 M; c5 w- ~! b
% W# S P( j0 a+ {3 C0 j6 k6 P
' ]$ R' v# g; B8 H" A可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。/ Y* Q/ u F) k4 g* X- n1 j4 X
I. r# u2 i: _0 o6 |* KTCP服务器端和客户端的运行流程
( D/ C1 t3 V/ ~5 h- Z5 Q
1 K( o' b3 N( [( t" @
/ v+ s9 f" u6 b9 p" o' b: \. u如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?: `/ L% L! B* T9 Z
; P& q% Z$ Q2 q% |$ P& c# H
1 X( V5 g% G$ Z1 \
1.创建socket( j# Y' i( ^# x! |8 K
socket是一个结构体,被创建在内核中
! c7 J. A" v/ E7 V sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议! ^4 v9 u% s3 w+ h' O/ K
2 S) v1 |/ R4 W( g6 i/ ~$ k1 m" Q+ C, I3 z
2.调用bind函数. C; _4 i k. s3 U2 J+ j
将socket和地址(包括ip、port)绑定。
) u" d: {+ O* ]( W# o8 i9 H2 E 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
; \0 ~+ a9 N" C8 V: ~' V struct sockaddr_in myaddr; //地址结构体# P- H. C8 l) u& w$ m3 X& J# _
bind函数& W! b2 w4 ^8 s; |
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))7 t* a1 x7 H9 C0 C) }3 W5 @' P& U
w. ]# E; T1 {- M! ^
5 S6 ~, M P5 S: H+ N7 \+ v3.listen监听,将接收到的客户端连接放入队列" o& J& J4 N9 z% }* m
listen(sockfd,8) //第二个参数是队列长度# Q5 m; X- r* L
4 g4 x T; j5 C9 R/ L& |
3 V# S f) |( T, [. Z' A4.调用accept函数,从队列获取请求,返回socket描 述符& u# v6 T$ q! N3 Z ]5 T8 v) x+ Z# T
如果无请求,将会阻塞,直到获得连接
" @% F2 f& P, L int fd=accept(sockfd, NULL,NULL);//这边采用默认参数6 K6 }( q' Y' A# k- E1 w
2 u, P5 j3 s0 R6 P3 l6 f$ B5 o2 x# P/ h1 i8 N& X7 l2 n
5.调用read/write进行双向通信
8 A9 ?# ?" a4 ?9 ~$ o4 z6 c
3 j) g( O+ T: i+ B! E! w* n% U! D2 p( a% h9 y o0 i8 j
6.关闭accept返回的socket5 Z! J1 W8 }3 r" q4 O& [
close(scokfd);
6 M$ i x, b; o- i
2 \& w9 a7 E: s0 J1 C1 [8 F) g4 Z9 Q* a3 s5 w, ?
h! G% e4 K" V4 }
% z; C( ^, D, C1 Z
下面放出完整代码
9 N6 H: l( Q) u D' c p* d" D# W$ ^ N6 r
- /*服务器*/& m+ H# l% p6 e3 y* k
- #include <stdio.h>
$ ?$ F3 B; M: a; `1 ?1 Y - #include <string.h>" e" i9 n8 D/ q6 Y, Z
- #include <stdlib.h>
+ K% K- m* F2 B% x% g - #include <strings.h>
% T/ X! s$ N" p& l5 w# Z# b f - #include <sys/types.h>, c' D5 |7 n8 \9 g5 p
- #include <sys/socket.h>) l" D& e' [8 f- }% i9 H, A. G
- #include <arpa/inet.h>
" c. s: r( t0 P; o0 v& {$ p# R- `4 n - #include <netinet/in.h>
* y+ B" K& Q E5 f# b9 m% Y7 w0 \; p - int main()3 F3 k4 x1 \2 j
- {
8 J' c5 f6 {! A3 [$ B - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字 `; }9 V0 v0 L/ {/ ]
- if (sockfd < 0)7 O z/ z' k X7 I
- {
7 j+ X" o, D/ ^& H2 x2 U! H - perror("socket");6 E, R2 B- m, u0 |% i
- return -1;
z `0 Y: W- g6 ^2 H- F$ T3 j - } //创建失败的错误处理9 D( ~% Y6 S6 `0 b# {/ q" B; {. s' G8 o
- printf("socket..............
; c# |1 {5 P5 H' P - "); //成功则打印“socket。。。。”6 r/ B- T& ?0 ]) N/ ]: O& ?
- " C. C k9 s' Z; I. K, x+ c; L. [
- struct sockaddr_in myaddr; //创建“我的地址”结构体, ?0 r8 v, K" Y8 _* X
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)! C) ^* y( B2 | h$ `$ @% V
- myaddr.sin_family = AF_INET; //选择IPV4地址类型$ _0 W. }) G- X! k
- myaddr.sin_port = htons(8888); //选择端口号3 U! F+ Y: R: J7 Y' U% [; N) n2 J
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
0 j r4 b v- N# a4 U G9 U* T - " r* V1 J) p, a' Q1 l2 `
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字4 J+ z8 ?3 K, j2 J& V
- {
- d4 y. Z1 B v2 Z' s - perror("bind");2 D9 _0 T5 V, _/ @, p4 [" d) s* L
- return -1;/ @0 ~5 Q4 U) U
- }$ s: q/ @8 t; |! A% B1 [5 f
- printf("bind..........
# A2 x2 \0 }/ Z7 o8 S - ");0 g: v" _9 B7 R z" K& m1 u( Q
-
8 O" R* O9 R7 K/ J( ]' U0 p - if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听3 w: {/ ?* Q @: s! i) \! G
- {
, G$ ~( O- E0 K1 C t* O9 v+ P - perror("listen");
) X/ y* ^; d7 U# E1 v+ Z - return -1;* D S. Y7 Z3 c- u1 ?0 a& a
- }
, \9 ? K$ D% b. W - printf("listen............. D3 S* z6 j* c/ S
- ");
2 c _. x, v% I3 r# O7 ^/ K -
- n! ` _5 ^( w8 z/ \0 g! {4 q - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求2 P" {# `: b$ w3 \3 U
- if (connfd < 0)
' s0 d! _* ~# x - {* s! w7 Q- \9 Y% ]3 G0 I
- perror("accept");
3 r7 R7 C4 }0 F( G5 V Q/ z" k - return -1;% |2 H$ I' Q; C% E" g5 M+ l: @
- }0 F) j/ e6 s+ e+ y4 i% E
- printf("accept.............. i. _" u4 q" @/ O
- ");
3 L# m; k/ [* K* a9 K) H - char buf[100];//定义一个数组用来存储接收到的数据+ U% |- G; P: m7 r+ Y
- int ret;
$ P3 a$ s1 |: X# Y& A - while (1)
5 v2 O& b9 [+ l0 F - {5 j! A# x8 S* y# ^! Z
- memset(buf, 0, sizeof(buf));
. K/ Q* d" E3 s( R% _' n6 e - ret = read(connfd, buf, sizeof(buf));
/ S0 D5 _5 `8 ~/ d2 Y9 L; s3 V - if (0 > ret)/ p3 l) h- m3 v: i, A; Q2 x+ ]
- {
% y: o' c# I; P1 @ - perror("read");
. s3 H' X$ o2 f% T - break;
q H; g, ]: B+ y9 t - }//执行while循环读取数据,当
8 ]. _+ M7 G* ?/ F2 z - else if (0 == ret)
3 L } n9 I. R a2 k - {+ p/ s% U7 f3 D. b1 ]$ d8 D
- printf("write close!: Q( B7 y, O% i T" E
- ");
1 G& `# ~6 o$ a* j0 U - break;8 c. ~0 v- `8 c6 l1 Z
- }- e, ~7 U$ ^$ k" P$ X1 m% W
- printf("recv: ");9 J- N* \3 J7 F8 P/ \/ { v3 a& P
- fputs(buf, stdout);//打印接收到的数据' y/ l" I1 i( o
- }
% M& R/ R4 [0 Q' F% g! V2 N: S - close(sockfd);//关闭套接字
2 ^' L( |$ I; I. e - close(connfd);//断开连接
% J: p: b0 X) F+ t5 y1 k, n( f - return 0;
X" I$ U5 ?( ~3 o3 ` - }
复制代码 3 I( I: D: X4 f7 w/ F" B7 a$ b2 S
8 j w. g K9 @8 D Q9 O- /*客户端*/(具体功能和服务器一样,所以不再加注释)7 z0 O$ R+ A+ U9 N) |- K& d
- #include <stdio.h>9 Y- a) v* h' u- Z
- #include <string.h>
$ M* b- o! d2 ~: U: \; B1 r - #include <stdlib.h>
' j; Y% W: d+ p5 c) J3 M - #include <strings.h>: Q( L8 x( t$ T P8 r
- #include <sys/types.h>4 e4 t+ c% M5 `. N; F6 a- d
- #include <sys/socket.h>
4 M" j" p6 R, r' ~; ^7 ]7 @0 c - #include <netinet/in.h>
7 ~5 O. C% T! k2 q8 X' K1 D- k - #include <arpa/inet.h>7 U: p& d0 |! Y( x
- int main()
0 @- t. z7 J" k8 L( D - {/ y$ R S- W" n9 \+ k% u; a" V
- int sockfd;% }8 C5 K& s' a1 S& c
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))2 J- H- E2 t( D4 \8 T, G
- {& P* N& T5 g' ^
- perror("socket");
: A3 V( { r: H( y- W - return -1;
7 k" H) [$ h" w. J: U8 N8 m - }: `8 ]' N0 z! {. v( Z" b. D2 @3 Z1 _
- printf("socket........... C& Y# t3 s4 p: m, |4 [# g
- ");
, d! ~4 s9 L7 ?+ s0 v, W7 ?9 @ B# B - g; C$ U, h. A7 Y
- struct sockaddr_in srv_addr;
/ [% `9 m: O A' x - memset(&srv_addr, 0, sizeof(srv_addr));
1 @% M: i; [6 O( H) {$ _$ { - srv_addr.sin_family = AF_INET;
' @, _' j+ ^2 f- c. i) h - srv_addr.sin_port = htons(8888);
$ Q0 N6 }/ {0 A - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
5 @/ z7 y. J1 l/ z- a - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))+ N% J% n! g8 N+ p% [. i, k. d
- {
% \, C3 X5 p) Y; `8 o0 U - perror("connect");
) u9 G6 y. p, T, q/ e4 Y) O - return -1; //exit //pthread_exit
' ~9 a) f1 g* G6 D5 N - }7 c% w! z$ e- n% n. |
- printf("connect..............1 [- c- F1 M1 Z) y, f# u" {
- ");: e" F, [0 C' A2 S
- char buf[100];
5 V' ~' \ B! Z0 [+ V4 D - int ret;! s0 p2 ~8 _6 A& m: ]
- while (1)
& P+ w) q' D) ]# q0 G8 I! } - {
( ?1 N1 c9 e5 H- g3 c - printf("send: ");
% f+ L9 p; B/ | - fgets(buf, sizeof(buf), stdin);+ J( |. ^. y+ y% D! H7 d/ S- f0 M8 x
- ret = write(sockfd, buf, sizeof(buf));& c4 H7 c. ~* y7 b d
- if (ret < 0)1 M: p8 a/ w* j! K
- {( v% v2 r: K+ t# G/ i* a7 o: f$ H$ s/ W
- perror("write");
4 ^2 ]. m; r; `: `1 ] - break;
/ Q3 T. y. L, C( F( q - }' W0 Y( @$ D) O3 \& K3 y7 k5 i. S
- if (strncmp(buf, "quit", 4) == 0)8 V/ m# K0 F' r0 ~
- break;
$ C& A/ s1 N" @0 m9 P - }8 {% s9 |! P+ q8 L# _: ?
- close(sockfd);
8 o2 R* j8 k4 K6 @' \' [ - return 0;
G% [# D# d& l: \ - }
复制代码
$ Z+ Y( d& J8 E: F# U2 V4 i9 L6 m4 z* |+ ^
|
|