管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
- i" r h u2 R, c4 S0 j8 r( b9 {0 ^/ A% [1 S7 s7 {+ |/ H4 V
0 y- l* \0 S$ u4 d r7 Lsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。9 U4 t7 C3 A7 M3 E
+ c5 {' J/ D3 j2 W4 M" Y" m8 h6 O
( t! J& Y" M* r$ ]9 s: q! oTCP协议8 W$ w4 \' N2 _
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
8 d. V6 B+ e! ?' k7 n$ \1 k) ^! v$ q. ^# f- [+ w
, ~; b X2 g% z3 g4 S
关键词:三次握手,可靠,基于字节流。
& Q, C8 y& J3 }: ]" }: N( i) t" m# ?" t' ?- G
. S0 X% ^5 U) k
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。0 o( N3 g6 C" E. `1 x7 O
% t& t7 e% e! @3 Z4 x4 p% }5 |
TCP服务器端和客户端的运行流程4 Z' e" K4 F' C1 t* J$ k
' q! r& ~% ^( R4 m- |
! s8 F) |" O7 h* m8 z6 Z如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?, y" w$ ?& n/ h, L% f4 n
s3 w4 q0 Z: o1 y$ ?. s
& ], v' f* y4 y* E3 p& s/ a( B1.创建socket% J4 ~% d8 E% p
socket是一个结构体,被创建在内核中
! ~. U% M { b6 d* e sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议8 S% z. a( ]& Z6 f# \
* P% v! L7 d2 V5 V f3 G3 C+ U, {% ~4 P, y$ ?- M' ~7 `# H
2.调用bind函数
( Y* X% m1 R+ ^. m3 Q. H 将socket和地址(包括ip、port)绑定。( Q* N8 D, [: H9 R: u, S; y
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序: E6 O+ L0 @: |0 }( I
struct sockaddr_in myaddr; //地址结构体
m1 ` R1 }8 i! `, U4 K1 c1 d bind函数
3 N, [& f, Q) S9 P bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))5 Z0 a5 S$ e, U% z: o7 Q
5 T5 o/ Z" B! {" }3 x
1 H% q O" x& {: e3.listen监听,将接收到的客户端连接放入队列
% f2 a$ Z9 ?+ @ listen(sockfd,8) //第二个参数是队列长度
' t9 S: f2 W- a' n/ R' {. a& d3 \$ |+ c* z5 ^+ m% f
3 R- ]3 R& x5 k
4.调用accept函数,从队列获取请求,返回socket描 述符
4 ]6 D+ U$ T6 @1 k0 D2 ~ 如果无请求,将会阻塞,直到获得连接
" V1 |1 l' P, Y/ I. Q int fd=accept(sockfd, NULL,NULL);//这边采用默认参数$ @+ V' G4 q# h( M
2 }# F* |) ?( y* @9 |& A# T% u Z% P, i) h" _0 z
5.调用read/write进行双向通信
8 j4 l4 l/ ^8 {) X6 L; H {/ C8 U7 Y( e( x2 C0 Y
8 }! j) {3 t. K7 Z8 R9 Z0 B6.关闭accept返回的socket
2 m$ N$ ^% L7 k4 c c; K4 { close(scokfd);7 Q" T9 \8 I @: v9 T0 F
' h7 z/ Q0 f7 J+ q+ a' M5 f: G4 J2 @: ~2 L! c( Q; b
7 O. t% b! P' w* G0 J) k
, q. z$ u1 R7 t下面放出完整代码% o% g1 C- ]" L& i
, e1 {" M, ?. |8 G- /*服务器*/
! J! `% d8 p9 f8 \$ L( ]5 y5 ? - #include <stdio.h>
: o6 R. g0 S$ S) K8 w, i - #include <string.h>. t" P( y# g' R* W* ^
- #include <stdlib.h>
/ q5 I4 m8 G: A$ J4 a6 @ g" X' \ - #include <strings.h>; M( P3 x; {, g! ?5 Z
- #include <sys/types.h>/ G$ P4 R* A- S# ?! e6 {( Y* u9 D
- #include <sys/socket.h>
% W1 Q: l5 }* h6 p; }* o/ C - #include <arpa/inet.h>
# |) A, G) R, Y/ t. n9 e - #include <netinet/in.h>
# e! X' ~7 a0 L0 e* E1 k9 O - int main()
4 N0 D, A- T( J/ N- r+ z+ o# { - {* E" K0 e J: [
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
4 V1 n2 c+ {7 Y: Q - if (sockfd < 0)* x5 {, p: R8 g
- {
1 n9 V& J- P6 v& B - perror("socket");9 P5 p y5 H6 \
- return -1;
& D+ }5 s: `9 c, q8 V, q - } //创建失败的错误处理! W6 p Z* {: s* a& O. U
- printf("socket..............
M% r' {, t$ k9 ] - "); //成功则打印“socket。。。。”3 ]) I3 S" x; A' F
- ( ?$ U, g. S s9 h* E) M
- struct sockaddr_in myaddr; //创建“我的地址”结构体$ G3 P- a8 L$ Y. j. e
- memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)" d4 |1 ?5 j. N/ l$ e4 A2 V
- myaddr.sin_family = AF_INET; //选择IPV4地址类型" ]! E" j( ]) n+ J5 l1 ?0 N% d
- myaddr.sin_port = htons(8888); //选择端口号
S% u1 y m+ m1 N0 A3 g0 F: x - myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
2 q' A- P2 S9 \* Y5 c( T1 ~ - 7 R) t- a; d# Y8 |" r
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字+ z' I& X8 l" `7 i, s' R
- {1 S; a& \! e Y" i4 {
- perror("bind");
, O' h& V, A* H% `9 T - return -1;
% N- i2 @ M! K3 P - }
) a6 {, Q) t9 B8 H5 R9 J; ~! p4 m2 f4 c - printf("bind..........( l! |' B4 a5 i9 Q
- ");
, P! `! Z" l7 Q& K0 K$ ~ H - X8 I4 t" d5 O
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听, R* y% G7 M0 r, u8 d
- {
- V3 C) c; o. x- P0 S( s+ |' S - perror("listen");
) g! V4 p F) W2 [ - return -1;
' m. z) p# P$ x1 ? - }# R7 [! ]& R) L& F- y
- printf("listen............
e8 i3 i; b2 _ d1 _* G - ");
6 G/ P6 q( U& g' c -
: G' \3 u2 x" l1 a - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
$ w( I) x7 h6 r - if (connfd < 0); H- X! S' B- ^+ ~7 l: Z9 P
- {
& P4 Z; ~$ r: L9 B* C - perror("accept");& S+ v* p- k8 O( |2 {
- return -1;
. D& u7 O7 o$ U! j' Q - }
3 s8 f/ w" b( N; [ - printf("accept..............0 y" {/ |5 [* U5 |$ Q* V( a5 D! o
- ");7 K7 z8 R. x. l+ C; ?3 K7 _/ i8 C
- char buf[100];//定义一个数组用来存储接收到的数据. Q0 `* e$ Q# d1 X0 q) U. ]2 Y
- int ret;5 Y$ m: M5 X$ \! ^% S: @
- while (1)' o& A; q0 k& ^- p! }0 {
- {& L! p2 n* `4 I) e9 i! A
- memset(buf, 0, sizeof(buf));9 h% Z5 X3 O5 e4 }
- ret = read(connfd, buf, sizeof(buf));
' c5 g" e' _. O) k; `% ~3 { - if (0 > ret)
& j3 u4 o6 s' r3 w5 _2 n; r - {
# } V) |, m- V5 |6 Z7 d# D - perror("read");
$ c7 F' s _. E" @1 P0 E5 o5 P - break;
. g7 k r( A" x- U9 ^3 n - }//执行while循环读取数据,当) G, t2 C, N' \/ z* Z
- else if (0 == ret)* e$ @: c1 _' m3 `; B L" a
- {8 O2 U! @2 H. Q2 Z& s7 M6 R' e+ y
- printf("write close!
P& r1 D" H3 ~3 |) ]; G - ");
* `- m% x$ Q c! p7 O - break;: T& X& v" v- X( W9 z% r
- }
5 M" H$ J' F# S - printf("recv: ");# N* V( b4 y$ k) D3 X9 [
- fputs(buf, stdout);//打印接收到的数据. _ r* F% J' ^6 V2 @% z8 m: v
- }
3 a3 T* @0 w5 _% T0 o1 J; {9 | - close(sockfd);//关闭套接字
" z* Z- A' a# r$ b/ C - close(connfd);//断开连接6 X, }" n; C& d/ f
- return 0;
/ c% u3 ? u: |& \ - }
复制代码 P# p" e3 R( O0 ^
( p2 u! T( \2 b6 o4 Y- /*客户端*/(具体功能和服务器一样,所以不再加注释): T) ?$ C5 M. ~
- #include <stdio.h>
9 [: s* ]* p+ |. X7 F& d - #include <string.h>$ \ T' h* o' x" X+ r1 o
- #include <stdlib.h>
6 O" E# h4 L# o8 ]" S4 W" i; w - #include <strings.h>! ?4 t2 J" G) z
- #include <sys/types.h>
% S7 q4 ~/ d. ~, |& N, t6 t - #include <sys/socket.h>
7 r; c4 p- U$ ?" l6 x3 {; { - #include <netinet/in.h>
l, y- e: b' y( i - #include <arpa/inet.h>
) e. I7 j. D5 W8 E+ H& B t8 [' x - int main()
3 t) k' h2 k$ L/ E0 u0 a - {
1 X; x5 P2 M6 [ ^ - int sockfd;/ E- q) V* h8 e& x5 t8 }* E
- if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
: P3 n; [1 q$ t" q) s$ s - {
G$ _7 V- I; P% [- o6 r; i5 G5 A - perror("socket");7 F; o" R, ~ D2 i
- return -1;
+ F6 A/ E9 T9 c! Y( T/ k - }( X7 o2 l, F" c' O6 X
- printf("socket...........0 o/ b$ ~6 |8 G, I+ k, ?
- ");
( s0 _1 N% z% f% Q: G - 1 T& \6 X2 E$ [$ r2 T3 T) T/ Y9 w& a6 _
- struct sockaddr_in srv_addr;
* Q( j4 ~- |+ c. M4 i# e; p - memset(&srv_addr, 0, sizeof(srv_addr));
# c* X% }3 V! m$ A - srv_addr.sin_family = AF_INET;
) y% O, S u0 Q9 j9 M; ` - srv_addr.sin_port = htons(8888);4 U7 ~8 \; M0 q
- srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");5 J0 n& A4 j5 G0 b2 {' z, G2 D ~& ~
- if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
: G+ m0 @8 Q: o: e4 _+ L - {3 V4 Q1 O- B+ w$ X
- perror("connect");. Z5 J1 J/ @) Y- G9 I& r
- return -1; //exit //pthread_exit
# Q* k( j3 O. J5 f/ T' s - }& t% l* T* N0 F: T: w
- printf("connect..............
0 m4 ]5 t5 [6 \, c1 M - ");; v+ w4 `5 k, E4 H& L7 b) R
- char buf[100];) w! y1 U( ?, }4 q
- int ret;. z( N' J4 u5 K6 W D
- while (1)
# b" E9 w- w! j, X - {: k/ M' D% Z/ N/ d& f% O: `
- printf("send: ");7 ]8 C, c9 C! j
- fgets(buf, sizeof(buf), stdin);, ?. ?- D& b6 \9 m' q) n
- ret = write(sockfd, buf, sizeof(buf));
) d8 ]1 a2 B" b - if (ret < 0)6 v; c" @ ]3 D: w, n9 z
- {5 x' g2 t$ v" D! U
- perror("write");
& s' ^9 B D- Q/ S9 i8 q - break;
" J8 _) v" g1 n& n+ L - }/ O+ L0 M0 c8 V; J
- if (strncmp(buf, "quit", 4) == 0)
9 s9 J' g2 ]; l% t' Z - break;
. ]" v' J$ S# @6 V& i( X; e - }
) Q% I$ p9 e" @* t! c8 A - close(sockfd);; C+ I8 H& F/ n3 J9 B4 }7 p
- return 0;
5 t. r$ Q# [/ n" F - }
复制代码 2 O$ _/ V. G4 d3 a+ N8 m
, K& }# a7 Q/ e) z! P& j ^ |
|