您尚未登录,请登录后浏览更多内容! 登录 | 立即注册

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

cncml手绘网 门户 查看主题

自己动手用c语言写一个基于服务器和客户端(TCP)

发布者: admin | 发布时间: 2020-5-9 02:09| 查看数: 11126| 评论数: 0|帖子模式

如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
7 b+ `. J, u1 ]& P( U+ @" q5 l% |- \  q3 x1 }  @

6 q4 }$ C9 ^9 ^- g3 G# Xsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
5 z9 h% X9 f$ i  I( z1 \
6 e0 O/ T$ L* |0 U; B9 R
8 b- y& d0 @. z
TCP协议8 _9 e) w6 y+ N4 ]- ^
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。2 `  I. U7 G" D& e7 q5 j7 Q$ v9 f

& y7 E! R8 E4 e% }" }/ S0 b5 z& x
. I2 X4 {; u) S4 T0 W( W4 j4 F, |
关键词:三次握手,可靠,基于字节流。
, V( h5 ^) ~- ~. @
# v( x4 n8 C5 f6 a  c9 D
) Q1 U; z. H4 L- T& v1 ], z! v
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。' B! \# w5 L( [" ^
7 ]' ]1 {) o, N$ P* Q5 r; F
TCP服务器端和客户端的运行流程
* Z* k1 D* e2 N0 d7 Y2 e* T# S, `) Z. n

% G) }! l; {7 v, x  C# A如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
% I+ H' {0 s) s! h: R( _  i1 ~
9 y, T% ~: N7 y. j
3 ^1 C: V* G# [6 B( R
1.创建socket$ @3 I# b% _; ]5 \* c  Z
socket是一个结构体,被创建在内核中! T9 _7 v* q& g) V7 p% R
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
8 H0 n, J, ^/ ]3 U* ]& x  i& w, C" S1 T. A$ m
) }+ W9 B% _+ \! O1 s# M
2.调用bind函数
; R+ t9 h! Q, q8 t 将socket和地址(包括ip、port)绑定。
  N) a: R# S- a  o3 G% N7 a% F  i  Y 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序+ x( v* A9 S( _5 X% @
struct sockaddr_in myaddr; //地址结构体
9 R% [% O1 h( F2 U% z) ?: ` bind函数
+ Z2 n& g, H6 X; q bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
0 P$ W0 \9 f( e! ?9 ^  i
. W$ V6 [& F- @' K3 y( {' M$ V
5 B- y/ ~+ b! \$ }  H; {
3.listen监听,将接收到的客户端连接放入队列
0 e4 g# m: S. ?. B" X listen(sockfd,8) //第二个参数是队列长度3 [- @( [9 V1 P7 ?( F

( ^$ ^9 e  B* @
" E" q! i( o* f1 f- e+ V
4.调用accept函数,从队列获取请求,返回socket描 述符- N! `3 H  c1 Y
  如果无请求,将会阻塞,直到获得连接: a+ v" M* z' x* H0 `' {2 o3 v
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
. R# R6 k; Y2 z" v% }9 B
! F% i* a; w$ S! E+ M

, @: V8 o3 j) w" O% w5.调用read/write进行双向通信# k( e# B5 M! J3 N6 N, D  J! K2 s% A

% [- Z( F/ y; u5 x3 z: p

7 r" M) X5 K$ k' l) v& Q* p: E6.关闭accept返回的socket
+ f- W7 r% @. r/ k. D) ?  close(scokfd);
+ Z5 B+ [- Q( j3 x
! _+ O9 Y  _2 I& J

9 s1 x) \8 W' w# U$ u6 b
' @: j- b, M! J; V( A
! |6 ]7 v+ }" E4 z# x* q
下面放出完整代码
4 i6 b3 r: L: ~0 y
1 n/ ?2 ~+ c/ t) L. f
  1. /*服务器*/8 y7 x& B* Q" v* Y  k/ m) n: y3 U
  2. #include <stdio.h>
    : J8 |  k( n2 @5 o6 e
  3. #include <string.h>+ I5 }/ y9 Y4 s$ ~
  4. #include <stdlib.h>" r1 P+ r+ N( W' N0 P$ I' e
  5. #include <strings.h>
    + m1 z# F/ p2 Q- l" a1 V9 m
  6. #include <sys/types.h># x( Z  l1 o  |! {
  7. #include <sys/socket.h>
    0 j6 B0 a" Z- R5 }  ?# _0 R
  8. #include <arpa/inet.h>
    " L4 h& ^$ `/ n; ^- _
  9. #include <netinet/in.h>  ]. t; A1 {, V  h# O. y4 ^
  10. int main()* h; R7 c- N9 b; j
  11. {4 f! Q/ f7 Y! h9 e8 k% M
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字2 g5 m- ]! T# R4 j
  13.         if (sockfd < 0)+ ~: ~" l/ x3 W, M
  14.         {
    5 ^% ^# q) Q4 M7 q
  15.                 perror("socket");
    ) y& ^' r6 X$ b% R5 t2 [
  16.                 return -1;0 D3 o1 l/ E( r, A
  17.         } //创建失败的错误处理4 g9 r' V9 A! E" m- I: V
  18.          printf("socket..............
    % a, |3 t" @5 `
  19. "); //成功则打印“socket。。。。”! F" i4 U; V7 q' j6 i" }. E' k
  20.          1 n& a4 c+ M% ^0 P* _5 g1 s7 D. t% C
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    + s$ B7 ~3 h3 c- `2 Z
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见). m* f4 c- }. T# f) _. w3 H- h
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型' ?# i4 v% z' N+ Z- N7 `
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    0 T7 b2 P0 E8 I) I8 n
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    * z7 ^+ Z, t; @& }$ q+ v

  26. 3 M9 L' B6 h$ G6 ^  n( k. O
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    % @% D% B9 ~( G" M7 s* C/ Z
  28.          {
    4 \  d$ d' o$ [" R  m) M1 v
  29.                  perror("bind");
    * t3 D+ L2 w7 I; d& ~
  30.                  return -1;" S% o/ f9 P, o  |  g' P- v& y
  31.          }  H. H/ o) T4 G- R0 k* n* t
  32.          printf("bind..........
    9 l4 `; M4 P' h  q3 e& z# E/ V
  33. ");
    ' C5 D- I0 r* o8 `

  34. 3 w6 q  l0 o, z
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    ; A- X. ?3 ]) w
  36.          {4 c" \( l0 @5 S
  37.                  perror("listen");
    * @( Z8 R: G5 p& z( Q4 q
  38.                  return -1;
    8 M0 I6 k( q; ~8 Y/ Q
  39.          }
    % t- _+ ?' L( R2 t/ s- [# z
  40.          printf("listen............
    * h) M6 U) }/ t! o; h" O
  41. ");% T* H+ \2 s) E6 c/ Q8 X" H
  42.          
    0 y' Y! g/ U# F* [4 M% L
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    1 N! P6 r1 w- I5 h# N3 K
  44.         if (connfd < 0)
    7 L+ E/ b( o" P& C8 q! D
  45.         {
    ' u0 s/ c; w: Z+ `% ?; U" o
  46.                 perror("accept");
    ' O  y( ]- ]' B9 ~' g
  47.                 return -1;
    1 C) v, V5 ]- W/ p7 T
  48.         }
    $ M) }" e+ v* ]: n
  49.         printf("accept..............( d* P- W( h/ n; \
  50. ");
    6 f+ y  ]) \0 A
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    0 t+ C: @4 T. }# x9 P
  52.         int ret;
    ' V1 [! ]4 o. T) M
  53.         while (1)
    % A* U, }; V: U. ~* F9 v6 ^
  54.         {
    ( n, i7 {+ H, V* @+ y$ G
  55.                 memset(buf, 0, sizeof(buf));" ^( @8 K9 Q7 T: y7 v  I3 K8 o
  56.                 ret = read(connfd, buf, sizeof(buf));
    5 d5 Q5 ?+ m8 q: O5 I/ P7 \
  57.                 if (0 > ret); T# b4 v" _: [% @0 o
  58.                 {
    . b7 G; a, X3 n3 u' r8 ]! A# w  t
  59.                         perror("read");
    & S- V8 C3 j4 W1 S9 [0 `2 N# V
  60.                         break;
    ) s$ a1 M5 l/ F
  61.                 }//执行while循环读取数据,当
    . q8 B2 C7 i7 R# ?, {& j5 Y
  62.                 else if (0 == ret): R" I! I" r2 W  G- f& p+ u; P- M' T
  63.                 {. N# r7 C  ^7 F( ]4 w6 B" s* G
  64.                         printf("write close!
    3 ~% Z/ e8 B" W. f* _- ^/ r6 V
  65. ");8 H' _1 }( g7 h9 @8 d* V/ s
  66.                         break;' f- p2 k* o# N6 I- q
  67.                 }  e' S1 r! T" s; L
  68.                 printf("recv: ");
    7 j) r" j5 f5 p! O+ j6 P  |
  69.                 fputs(buf, stdout);//打印接收到的数据
    ) t& C+ D4 ?7 y, s/ t
  70.         }# B1 i! ~- i3 z& S' J
  71.         close(sockfd);//关闭套接字
    5 r; k; S3 ]+ [5 H' e: b$ j
  72.         close(connfd);//断开连接* G) T7 m. v' ]0 @+ v+ B9 ^9 A
  73.         return 0;& L. z  Q# s8 e; O( X4 E8 H; \
  74. }
复制代码

$ K3 e' g7 w1 {7 c- D5 G- q- ^/ N( K0 R; b9 |: w
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    ; J7 o9 [( x" X: ?7 v7 O
  2. #include <stdio.h>3 M* l1 W& i; n- |+ X! U" f& v/ G4 v
  3. #include <string.h>4 G! k0 y4 M5 H3 a
  4. #include <stdlib.h>7 `) {" j6 H8 W1 Q1 h; O& I7 x
  5. #include <strings.h>& b% x6 I, W: {1 B" a, A
  6. #include <sys/types.h>  v. u2 c9 E0 D+ ^5 M  G1 X
  7. #include <sys/socket.h>
    6 r- U+ T  \+ z
  8. #include <netinet/in.h>1 D/ k9 u  h: C1 u$ R4 N
  9. #include <arpa/inet.h>
    , W6 D0 Z) m4 z  k8 ^0 p" |& g: e
  10. int main()
    $ }7 Q% i# O3 L; k' p( G! c
  11. {' _# d* G. p" D/ [& J) s
  12. int sockfd;
    5 a: y+ j) \3 G2 F3 P0 t
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))8 k3 v. |. N% B: B5 _& N2 U
  14.         {
    6 C; t& t* b2 n$ z' U( t
  15.                 perror("socket");/ d. S3 G& j$ }6 U
  16.                 return -1;. U- U% M3 l' q; t
  17.         }$ \8 c* a5 ]3 B+ ^' N" t' O5 j4 U1 d
  18.         printf("socket...........
    + C: t. x' O6 u9 @; g
  19. ");
    ; k- |4 [% E! t. c
  20.         
    ' }) E+ p; N9 Z
  21.         struct sockaddr_in srv_addr;# A. X: r" Z) J3 L
  22.         memset(&srv_addr, 0, sizeof(srv_addr));2 ~0 d9 @* l) C! ~( [) y
  23.         srv_addr.sin_family                 = AF_INET;
    + b8 p* N5 W. c$ ]7 c8 C4 s6 P/ ]3 @
  24.         srv_addr.sin_port                         = htons(8888);" l  F' w" M7 g- A9 k4 O# M
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");: f7 b8 x8 ]5 c% ~8 {& T
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))# `; O# j; l4 l  W- W9 H
  27.         {
    % i# M2 B* @) o8 {
  28.                 perror("connect");$ H  }1 O) {1 u6 _4 U! c: p+ ~8 B: H
  29.                 return -1; //exit //pthread_exit
    ( X9 H8 z! C0 X. C
  30.         }
    , l# S/ k7 Q- B& J1 _/ W. L
  31.         printf("connect..............2 H7 y3 d9 o5 O# B$ B1 H  K3 Y4 e
  32. ");, ~: g* d! z7 p0 U4 A
  33.         char buf[100];+ q* A( X) }' `- c9 R
  34.         int ret;
    ' n% ?8 _# R8 z2 F
  35.         while (1). L, d* \" x" }- m0 }5 ^5 N
  36.         {- ^1 c+ v2 I% q
  37.                 printf("send: ");
    # j5 j# P! ~8 s
  38.                 fgets(buf, sizeof(buf), stdin);
    % ?& ?! s0 K7 k5 |9 P/ G
  39.                 ret = write(sockfd, buf, sizeof(buf));! B: g7 w7 l4 \6 F3 p) L  G
  40.                 if (ret < 0)
    1 @& w- `* [0 A, O3 X; B& L* G
  41.                 {
    1 ^- }4 m6 E' B( q9 Q+ N0 ]
  42.                         perror("write");) n8 h+ \3 k% O8 U, ?* t* _
  43.                         break;0 r+ f% i' K0 Z4 U( l
  44.                 }
    ; v  Z3 w; v0 p/ F
  45.                 if (strncmp(buf, "quit", 4) == 0)0 P" D: v/ G  q. o0 ^# e4 R
  46.                         break;, ^' v! w( R6 }9 r1 P
  47.         }
    5 S, i$ T/ |# i2 [/ r6 e! N
  48.         close(sockfd);3 I$ Z/ y  {! C9 R& T7 o
  49.         return 0;6 s; Z9 d: O: A
  50. }
复制代码

" G0 \$ K; ^8 ]$ F7 g- z" Q) x( F# O1 c& Z  S0 G& L

最新评论

GMT+8, 2024-4-26 13:57 , Processed in 0.145157 second(s), 24 queries .

Copyright © 2001-2024 Powered by cncml! X3.2. Theme By cncml!