管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
4 i( o3 V2 p7 g4 |, c" K
" }8 c/ [6 e. q, ^) ?6 ]/ A
; H# a. ^% U, U0 Z* F7 M2 gsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
" ]3 H: c7 X! }2 k4 l' y. ~% F ]; V# U( m( C8 X# O* ]3 Y
( s6 P: M) i, n: S, uTCP协议
@( g9 V, p6 k. F2 CTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
1 [! _% r7 V# z$ s6 b, b( {8 Z8 o5 J; P) i* a+ t. t
- O) y8 \5 ^/ ~' ~' t
关键词:三次握手,可靠,基于字节流。8 U$ f5 Y: q4 l- Y5 `
8 d9 N9 G" ^5 R8 S! g
& ?# g; a- a2 f0 Q# \
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。. e0 |) S3 P8 V: f* U: F
- y4 U5 _) m1 r( A& p$ P# G5 @
TCP服务器端和客户端的运行流程+ A0 s( E3 E" j. W m4 S
; l' ?1 d9 ]+ ?1 d+ N1 f
/ T% r1 x( m6 k# ]) q$ T- j如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?+ @4 _5 ~ A6 W5 X* W1 m
/ ? \& r9 P9 Z: F2 C2 }
' V% V' U0 b! }% C+ H: C1.创建socket( }# H/ Z N' m B0 m
socket是一个结构体,被创建在内核中5 Q1 D+ p7 d" |
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
! v: ~+ L# l% O7 L- H
+ K4 r$ h& F# @
3 m' b8 \# ?( f8 v/ g2.调用bind函数
& C9 ]2 y* v" |: F 将socket和地址(包括ip、port)绑定。4 A c9 A$ }* Q8 w2 X7 g* D
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序2 t, M3 C+ b) P, x) x
struct sockaddr_in myaddr; //地址结构体
9 l, ^3 f8 P; t5 b6 j bind函数
8 U" V5 M- f! h$ x bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
9 e8 F! V4 w; h$ Y# k6 h3 J6 a! L u; k ~$ c& _
) N, m( P5 r& R+ C
3.listen监听,将接收到的客户端连接放入队列, K5 \" f( j7 ?1 a7 t
listen(sockfd,8) //第二个参数是队列长度
; r( E( f5 K; a8 M2 ]9 |6 L; v1 g0 {4 T A2 B2 H @
$ p: Y3 B A1 f2 r; I! o1 p4.调用accept函数,从队列获取请求,返回socket描 述符4 J' R% M& w% e) A' `: G2 k. N
如果无请求,将会阻塞,直到获得连接4 q% T) |3 L% Y* M h# q5 V' P8 a9 |
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
N" T9 x; ]- Q0 R; N8 @0 {# n8 n% c. H/ y& \8 o e7 ^& k$ T7 X
* |2 l* O. ]/ c' i# ^( @, y7 z1 N$ o, M5.调用read/write进行双向通信
% u4 Q* w' o& v4 O
! u3 |9 n$ I, @* Z# S2 M
% C4 N. r+ s9 D& z8 p6 R6.关闭accept返回的socket/ f' a. ]: F1 k% P. |
close(scokfd);
$ s& I+ M) [2 |. D9 L6 B v4 z7 `. Z' w7 S( k$ d" \
; b8 S# e+ ` a+ V# Y3 K3 A
$ b& R5 F" g7 \4 p) j6 |) u& ^' W
2 E# x: M% Z% Q- {8 ?
下面放出完整代码2 a T* i4 O; }8 v9 [: W
9 p8 Q9 y, U( h0 c7 @0 U$ ~7 k- /*服务器*/ G! f, S b* Y9 U0 l' ~
- #include <stdio.h>
; `3 w8 A( d3 v - #include <string.h>7 n/ Q" r9 g( R6 `& V' V, e
- #include <stdlib.h>
! |" g' N, L$ Y1 R/ H/ L0 R! ] - #include <strings.h>
6 L, {; K: I( @* e0 M& m - #include <sys/types.h>- @$ Q; h- T# W1 l
- #include <sys/socket.h>
3 [$ T* ?- u5 ^( l8 Q5 s - #include <arpa/inet.h>
9 a" n0 I& N) E- z5 C' R - #include <netinet/in.h>0 Z0 H) b$ j( Z J, i5 {
- int main()6 x/ x' A8 X2 |2 ^
- {
3 v4 _8 _1 k/ r5 D1 V - int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
* n+ Q# a3 G# U - if (sockfd < 0)
}" G: ^ G* H9 @ - {
, k6 E8 B" G9 c. w, \9 n - perror("socket");
$ X; B1 D4 G& Q v5 W7 Q/ r( g - return -1;
( U, {3 x8 x m0 e - } //创建失败的错误处理- g1 l$ L9 O% ^/ |
- printf("socket..............
+ g9 R9 Z# C1 v - "); //成功则打印“socket。。。。”1 L3 A, j7 ~/ M; t
-
# [' r9 y* w. @ - struct sockaddr_in myaddr; //创建“我的地址”结构体
; [2 G" }; O4 ]" m - memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
9 P% ]- `; j% |3 o8 A# Y' e - myaddr.sin_family = AF_INET; //选择IPV4地址类型
8 H2 ` e3 ?% k9 k; C& m - myaddr.sin_port = htons(8888); //选择端口号$ I( V4 Q; N: p7 j+ t
- myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
g3 D7 D$ _6 a' I) Z4 I0 t - 2 }# B9 A S) ?
- if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
; }5 O" i1 n5 ? - {
. m: i% V; ^" c$ s u/ R - perror("bind");
4 }7 [+ R+ C$ r5 A5 _* |) A& n( Q - return -1;
. _6 f4 A9 Z8 y7 ~ - }5 u( ^8 e7 i+ c0 c
- printf("bind..........7 `2 K- v8 r4 @
- ");
* m5 P6 \' d& E r9 i - ! ?6 f$ W" W( Q& a5 Z2 I
- if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
0 B3 j! Y5 u5 p3 ^! }: [ - {
3 D6 N( ~4 Q4 X9 a( I - perror("listen");# B5 x p9 e- x1 r; {* A
- return -1;7 p4 f' A1 N& N" T; ~( H
- }
' u+ [- @. z. \& X - printf("listen............
5 x6 v! ~6 o( r) s8 ] - ");; R7 M& p ]/ G, L
-
% ?2 g" a. [# Y% Y" I2 h/ z, C - int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
& k0 h1 U- @/ i$ n9 S* ^ - if (connfd < 0)* ?1 | J1 y9 }7 O$ c6 I* b4 o
- {
. ^/ ^1 `6 q8 D; @ - perror("accept");, p# E8 b- Q5 T
- return -1;, m1 b6 S4 \+ t- `, ?
- }
! u5 H: T/ h% I! L - printf("accept..............
8 m% S! Z- i8 a& H - ");
3 e: T: E+ I$ h3 a n6 t. k; @1 I% k - char buf[100];//定义一个数组用来存储接收到的数据
0 i7 F F. t6 T - int ret;+ s6 ~7 ?: o* G l
- while (1)7 p# M$ V5 A6 _
- {
; R* m1 ~; L! u3 N) Q3 _% s - memset(buf, 0, sizeof(buf));* A! O, Q5 N z1 t$ m
- ret = read(connfd, buf, sizeof(buf));
$ s$ X/ i+ _; w' e - if (0 > ret)2 n e( a/ q7 C1 R1 B' o
- {
* i+ q+ U, G. X: [1 W - perror("read");5 y, l1 h& d4 f/ B2 A6 D
- break;
/ J2 y9 f1 Q, ]1 L. E4 N7 ^1 s - }//执行while循环读取数据,当
. k# B% e, W8 a7 [$ `* ]# j- { - else if (0 == ret)
$ K6 k6 S( ^' Y6 T; X/ Q% _ - {
9 G, S; z3 b. S' A - printf("write close!- M/ F* e% C8 d: s- d$ A- f
- ");. k" ^6 \! _0 {" n. ]% w& \
- break;; _% A! T0 l% O
- }; z( C+ n& t# T6 p
- printf("recv: ");& O* E* G5 a! w; Z8 l
- fputs(buf, stdout);//打印接收到的数据
: v3 |+ J' g; t& o - }, ?" i1 R0 m& F; f8 [9 J
- close(sockfd);//关闭套接字% T% M7 X5 B$ s! l* N2 t; m
- close(connfd);//断开连接
/ q- z7 {3 V; T) ]* a9 B) j - return 0;0 r$ B$ Z5 A! p' h h
- }
复制代码
) @1 i2 @4 T! i Q
; v1 t& S6 V9 A( K7 O- /*客户端*/(具体功能和服务器一样,所以不再加注释)
0 {6 k# `2 Y# j) ]/ V - #include <stdio.h>+ `7 Z7 _% b3 N0 h* x
- #include <string.h>
9 J$ n& H8 s8 K8 J) ?+ c$ z - #include <stdlib.h>( M# U( t2 x$ {
- #include <strings.h>
: \, A4 W- |8 b. G& [4 } - #include <sys/types.h>
+ c8 J* y. C5 P# ~ - #include <sys/socket.h>
. A3 A% A8 w* g5 x+ N - #include <netinet/in.h>
1 H r: z# y* E - #include <arpa/inet.h>
3 c8 l% [% u; M - int main()+ f, @/ [5 j) F& q
- {
" T+ N/ Z; ^3 Q5 P9 g, D" y - int sockfd;
5 _4 J3 X, u$ z0 F# h* m5 D - if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
+ X. Z; i; F" y( P9 v$ R2 Z; h - {9 m" n; K8 `* X2 [- [$ p: Q8 u
- perror("socket");/ |8 R- D. ]# v+ `; n0 o
- return -1;+ E3 r. r1 Y! p. _( A0 s
- }) b7 z1 a9 m/ ]/ d5 |. j9 x7 }6 Y
- printf("socket...........
) {$ J% Q. h0 a& h - ");. w9 T q% b+ x
-
: G/ ?8 p8 B! D9 A D: _ - struct sockaddr_in srv_addr; l8 f7 t" ~, m6 Q% Q; K2 A
- memset(&srv_addr, 0, sizeof(srv_addr));8 B3 l' v$ o- q p7 S8 c; M
- srv_addr.sin_family = AF_INET;' u; l: N; l# h- x& c% G
- srv_addr.sin_port = htons(8888);
6 M; B& b' m0 x5 N1 X - srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
4 ?: ^( ]" L! H" }/ Y+ \$ X4 r7 q - if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
- Y; L% p7 r6 F7 A/ Z - {/ T) ~1 d6 D) u% ]) l
- perror("connect");
" O2 x. |( u V' l& D - return -1; //exit //pthread_exit
, s+ f6 n+ O$ F" a0 [4 J! R - } V! V. f# W1 ^1 k3 [: y$ E
- printf("connect..............& I2 X: T7 G3 G2 r( o/ h( M
- ");
$ F* X. j' n6 K! y/ i/ V- F' M - char buf[100];8 W9 U2 l9 _' k9 Y7 ^. f: E
- int ret;
4 w+ K- g! A+ [5 K6 o% C& d- i) f - while (1)
9 @$ H& h7 q- t- T { - {
! ^/ {7 m7 Y- W- C$ J# ] - printf("send: ");0 D7 Z3 U3 Z; l# S: n5 ^
- fgets(buf, sizeof(buf), stdin);
0 W" b7 R' I. W - ret = write(sockfd, buf, sizeof(buf));) W# [. Y2 i) ^0 n# W
- if (ret < 0)
0 V g) {7 m( H) Q& y - {0 z. q& D4 g: ^9 {+ r
- perror("write");
/ U* _- g1 ~8 z - break;
! @8 d# i7 ~8 F2 F7 z3 k - }
8 T6 m4 P$ `' a; O - if (strncmp(buf, "quit", 4) == 0)+ [# R) a+ | K+ R6 B' ^1 K
- break;
1 ]0 d" Q2 B: ]0 K& x - }1 a: u) v' j) z
- close(sockfd);
+ A8 S+ ?7 V3 S& p! K - return 0;( ?. M( h: Z }* F9 d: f; g
- }
复制代码 # z7 |! G$ X$ V4 u9 E
# L& T; I# q/ v! {' ~; B" y& ` |
|