cncml手绘网
标题:
自己动手用c语言写一个基于服务器和客户端(TCP)
[打印本页]
作者:
admin
时间:
2020-5-9 02:09
标题:
自己动手用c语言写一个基于服务器和客户端(TCP)
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
; R: N& S# G l8 w+ z
. |! M: E7 u: w0 |# t) E: o6 P
' e' t8 Z( n$ n! P
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
, N0 ]: A* ~4 h
- s5 O% X' |- T: c$ K) n
. t: v0 Z& C( Y
TCP协议
4 g8 q/ g' l- w$ \6 n
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
9 n5 A) _, b8 L0 ^
( E: K+ X5 t0 S
. |0 P* e1 z1 ]
关键词:三次握手,可靠,基于字节流。
/ b/ T/ o+ t5 z$ A- s' G7 j
; t5 Q8 V( I: D, t, l J
, N: ?- w6 |2 _3 P
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
6 ^& H7 Y2 w5 s2 o
微信截图_20200509015654.png
(175.4 KB, 下载次数: 5440)
下载附件
保存到相册
2020-5-9 01:57 上传
2 q5 t- z: k/ m
TCP服务器端和客户端的运行流程
3 A7 v- t/ K- f) x' A
$ `9 y* d1 k' R
/ [ x. W- z/ l8 n, J' z
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
( m. A3 ?0 ]4 e: O6 f# t
. x( X9 @- t, ^1 X/ a4 X; T
P. O; D4 v5 j
1.创建socket
$ v6 X6 w1 W; L& g/ {' I+ x8 d
socket是一个结构体,被创建在内核中
1 D6 G* Q2 W* N3 X
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
8 N0 u8 L$ L4 d- K& e; L i& {
1 _5 F, g" A2 B1 f( F
1 P% L/ w& O) T6 A5 b
2.调用bind函数
1 X U2 F, E8 u- _
将socket和地址(包括ip、port)绑定。
! [! W; C; Z: ?
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
/ b4 D* \# D' r1 a' G8 N5 R# Q' M6 s
struct sockaddr_in myaddr; //地址结构体
2 l x" n) c7 @4 R
bind函数
1 X2 g. c# K6 K% m9 g
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
- H( U% w6 A0 z2 P
' f& [0 B. L& g: S* v
" x- h& P" a6 k5 `; ^2 W
3.listen监听,将接收到的客户端连接放入队列
* m# h! X9 ` U2 w' ~2 F$ c
listen(sockfd,8) //第二个参数是队列长度
3 u) c. z2 S! M9 x& X9 B
% |7 p s/ o: y# _' r
/ J9 R3 }/ b' Z3 D/ W' L
4.调用accept函数,从队列获取请求,返回socket描 述符
4 k6 a1 v/ O S- n2 m
如果无请求,将会阻塞,直到获得连接
2 P5 D7 E) ^/ h6 ^( t" {2 I
int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
" g; i# M0 H9 Q. q- {* Q s B
2 i4 p' B5 L* Y' j5 h( w' r
5 H1 B) h0 b+ s3 N' u7 o0 ~
5.调用read/write进行双向通信
2 _$ Z7 e) m* M' ^
- ]0 ]2 n7 ]" S; Z* {& \$ K
9 K8 ^. ]2 k( N* R6 e2 W
6.关闭accept返回的socket
# Y. J) r4 z7 ]7 `/ z I7 x- [
close(scokfd);
" e% p" B) i, X2 n
0 x5 W- `( C/ L. A8 e+ r3 S
: l' \/ _' |) P4 }; X! @
5 z. c9 S8 w5 ~( }+ D n0 S8 e
; c/ X( k2 T) J* e' ?
下面放出完整代码
2 W0 _* m* M$ b* i2 g4 ~
4 y% n, S( g+ }1 f
/*服务器*/
6 ~! [/ n$ d) k3 [/ T6 _/ }
#include <stdio.h>
! S( y4 o/ x$ c, h6 I' v& b8 ?( k: ?
#include <string.h>
+ N4 S9 c( }4 W8 I7 P% \' h
#include <stdlib.h>
$ ^4 _8 Q8 _" m; E
#include <strings.h>
; A; M W' q) n7 C
#include <sys/types.h>
# C- B0 R* X8 h9 O
#include <sys/socket.h>
$ F# m/ h( S" l h
#include <arpa/inet.h>
" w3 [; M( O# S
#include <netinet/in.h>
1 U g; Z1 L$ b/ {! K \
int main()
# G4 x$ i( ` @ _9 K
{
1 ^- ]5 e. A8 L$ s
int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
! D; n) L) W# f3 I# w e
if (sockfd < 0)
# Y! k* k; C% T% o
{
, ^; o8 B3 S% P W) {( I4 z
perror("socket");
% m& l' _# n. f, |1 T
return -1;
- V! v7 }7 ^0 v8 \
} //创建失败的错误处理
+ O9 S9 \, B* b, D0 q
printf("socket..............
0 U* M6 U$ l- e0 F* \
"); //成功则打印“socket。。。。”
6 F( V) n k9 n: }+ Z; Q! p
8 _5 n, B& y8 e# o
struct sockaddr_in myaddr; //创建“我的地址”结构体
' }3 V- N( s. z+ C
memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
1 C' I9 N( T+ k9 ^+ P* ~/ }& X
myaddr.sin_family = AF_INET; //选择IPV4地址类型
( A, n2 L6 R3 C1 z4 \5 D- `
myaddr.sin_port = htons(8888); //选择端口号
* Q. |9 ]# L# g- n- D( j
myaddr.sin_addr.s_addr = inet_addr("192.168.3.169"); //选择IP地址
& u, s7 e S* Y$ H' {' U
1 J5 t" i0 m/ [ ^! m0 R
if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
" e! a, r' _9 A$ U) K
{
6 Y7 _7 h0 o, g! z0 J
perror("bind");
$ {" v! \* h C* A+ V: ]
return -1;
, P# o1 {+ X) j: D: e x
}
- ^( l2 w. X) p! h
printf("bind..........
: C) n2 P3 G9 S8 J
");
7 x$ i( p1 j0 j N" E8 m- `
4 A& [2 T0 @0 v6 C6 h
if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
$ l Q& T6 E- A; y
{
; }3 V- }. ~) }" x: y
perror("listen");
% v/ R, q. u0 g+ `% Z
return -1;
E7 T% L4 s# R# h4 Y8 \! v4 |
}
# o* w* d% d* J9 N/ @
printf("listen............
! l% ~& z1 v4 i: T, B
");
8 p( n) ]" Y5 c+ G$ F8 |
; R, ~' Y4 ^4 _/ S$ G+ u1 G
int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
6 T. F' p# X* s# k# Q
if (connfd < 0)
7 s0 \8 p6 {1 h) K( I
{
/ Q, l) w8 L6 W4 M
perror("accept");
3 z8 K* W7 d# x; f/ o; {( ?
return -1;
, L+ h6 f6 s& K+ W( T4 J6 P2 x
}
: w. m, }( y' j9 M
printf("accept..............
4 d. b9 A" l# D% \3 I& a! s9 {
");
' d4 ~& z) C, ~ {
char buf[100];//定义一个数组用来存储接收到的数据
) |; q: B* b" y# } [! {
int ret;
' ~3 _( Z: h5 N# j- H' U* W; h
while (1)
1 d: r9 H/ I" Z* j j9 H
{
+ m! ^: P1 i2 m8 J R
memset(buf, 0, sizeof(buf));
, c9 C5 H4 R! R
ret = read(connfd, buf, sizeof(buf));
( h* j$ U% U0 R
if (0 > ret)
3 F( ?9 d5 g8 v
{
4 a+ v# X h' P) c
perror("read");
2 Y) ?: E4 A/ h
break;
2 q4 ~) x+ K. f0 f
}//执行while循环读取数据,当
# b0 a% t' t5 e( f
else if (0 == ret)
3 R$ B6 N0 g# R H/ c$ I
{
7 X/ E& b; q8 d& ]- I( {
printf("write close!
4 |( a w2 q% b5 j8 k
");
2 @ d, K6 v) d+ o" E; r. I9 X
break;
- G3 w4 Z3 s+ c: E- q' T u+ v
}
2 ?1 Z3 U. V6 A* z$ v! U9 a9 y
printf("recv: ");
# `* e! i1 B+ G0 K
fputs(buf, stdout);//打印接收到的数据
; O+ E! u( b* I) ]
}
& K6 z( H. h e( O! `6 K
close(sockfd);//关闭套接字
0 ^6 Y; c. p( P' e6 P% `
close(connfd);//断开连接
6 }8 R/ c, Q/ R2 w2 q6 H
return 0;
& H: U. z* {* X2 n* h
}
复制代码
- l/ q& s; |, X2 R+ Y( W
- k/ W8 D" {2 n
/*客户端*/(具体功能和服务器一样,所以不再加注释)
8 S! R& ]/ }. h& T. A0 P" X
#include <stdio.h>
% t( N o8 k- I# p R
#include <string.h>
: r1 p5 C- \8 A# _% z' Y. \
#include <stdlib.h>
{: A6 v$ Q0 g9 z+ g
#include <strings.h>
! N4 Q5 z* a B, k! [
#include <sys/types.h>
" r3 ?) u. X7 }! f- @% y4 A. T
#include <sys/socket.h>
- s! P2 U# O( w! G: u
#include <netinet/in.h>
6 U: [0 ]. u7 D7 M4 f
#include <arpa/inet.h>
8 Q, t3 n! C+ l- q1 a
int main()
! h: m8 `! {% ?" Y0 G& g) E
{
4 D. i, O' j: ]# c" V2 ?
int sockfd;
: {+ ^9 k8 L. J' Q. {, r1 g: o& N
if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
7 y0 H0 W! {- q8 E/ q% h
{
, y {0 h/ q. |" Y2 P
perror("socket");
' D. @9 W* ^8 I
return -1;
8 W2 y; Y5 ^0 @7 j! l5 ~7 `% m
}
6 M, A5 w9 J1 X0 M" `
printf("socket...........
2 k- R8 z t2 ~+ o
");
4 C- o. ?% g+ u4 q
, F) f7 w2 o" T. Q7 C1 f/ d6 C
struct sockaddr_in srv_addr;
+ M5 }4 U! `: E1 \5 a+ r
memset(&srv_addr, 0, sizeof(srv_addr));
3 B" N; }, p# [" W3 X
srv_addr.sin_family = AF_INET;
- k, Z: S: b8 Z9 t, j: H* s0 u7 L
srv_addr.sin_port = htons(8888);
; G# Q" Y+ f% h P
srv_addr.sin_addr.s_addr = inet_addr("192.168.3.169");
/ v8 u$ y3 D W0 ?1 `( M5 D
if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
9 i3 H/ c: G# H3 N# u8 s& l
{
( J) A5 d# o; Q! @' |
perror("connect");
/ x% ~2 `$ P. s; t0 Q
return -1; //exit //pthread_exit
0 }5 N7 W- f6 `, T# V5 c# Y
}
% [5 W1 E; I7 l, J
printf("connect..............
1 [. x+ h/ Y' _8 C4 j: d2 ~
");
4 T: R, M) h# _8 g% }
char buf[100];
9 @# y/ G: S8 a' v
int ret;
0 U! g g' ^) }9 K Q N/ ^7 |9 {
while (1)
. ^4 n3 M7 s) ~& p, k: J0 ]$ I" r
{
' Z7 C- t+ u2 x) ]/ y* D1 O
printf("send: ");
; P. N/ I" F$ r* G* y m# C
fgets(buf, sizeof(buf), stdin);
. Y. b8 ]6 `4 i
ret = write(sockfd, buf, sizeof(buf));
! f6 e. k% Q+ J
if (ret < 0)
, u; @9 V; U0 X4 N
{
5 `: q I8 h# B: m
perror("write");
# c* o( Q- Y5 g/ _8 I$ l6 I
break;
; }/ u! w5 q8 d# S" p/ b
}
9 \3 T8 {4 I! }8 n6 P7 P' y
if (strncmp(buf, "quit", 4) == 0)
2 f& C" n; n, ]4 V1 ~( R* t
break;
8 @$ D) E; C* ~. ?3 `5 ?$ A
}
6 S( h! b1 Y* h0 q) O$ j7 g
close(sockfd);
( U# U9 |: T" {6 A' S. T( R9 C
return 0;
1 W Z) _/ Z- P' q7 `: \4 j1 s
}
复制代码
) }( U% P) ^2 g' h9 H
. ^( w7 O/ V$ L0 q0 M( J
欢迎光临 cncml手绘网 (http://www.cncml.com/)
Powered by Discuz! X3.2