TCP

本文介绍了 Linux 下的 TCP Socket 编程基础知识,包括主要流程、关键结构与函数,并通过一个简单实例展示了服务器与客户端之间的通信过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

Linux C Socket TCP编程介绍及实例

标签: socket tcp 网络编程
3529人阅读  评论(0)  收藏  举报
  分类:
C学习笔记(13) 

1、TCP网络编程主要流程

图1.1

注意:图1.1中可以看到close指向read并且标有结束连接的指示,可能有些人会有疑问,这个标注的意思是服务器在处理客户端的时候是循环读取的,如果客户端没有发送数据服务器处理客户端的线程是阻塞在read这里的,当客户端调用close后,服务器read就会立刻返回-1,这时服务器处理线程才会继续向下执行。如果客户端没有执行close而是直接异常退出,那么服务器端的read会立即返回0,所以在read后面一定要判断返回值,根据不同返回值进行不同的处理,这些是在试实验中总结的经验。

2、结构及相关函数

(1) struct sockaddr(套接字的普通C定义通用的地址结构)

structsockaddr {

u_charsa_len;//长度

u_short  sa_family;//协议

char  sa_data[14];//数据

};

(2) struct sockaddr_in(IP专用的地址结构)

structsockaddr_in {

u_char    sin_len;//长度

u_short   sin_family;//协议

u_short    sin_port;//端口

structin_addr   sin_addr;//ip地址

char   sin_zero[8];//数据

};

(3) struct in_addr

structin_addr {

           u_longs_addr;

};

用来表示一个32位的IPv4地址,其字节顺序为网络顺序。

(4) int Socket( int domain, int type,int protocol)

功能:创建一个新的套接字,返回套接字描述符

参数说明:

domain:域类型,指明使用的协议栈,如TCP/IP使用的是 PF_INET    ,其他还有AF_INET6、AF_UNIX

type:指明需要的服务类型, 如

SOCK_DGRAM:数据报服务,UDP协议

SOCK_STREAM:流服务,TCP协议

protocol:一般都取0(由系统根据服务类型选择默认的协议)

(5) int bind(int sockfd,struct sockaddr* my_addr,int addrlen) 功能:为套接字指明一个本地端点地址

TCP/IP协议使用sockaddr_in结构,包含IP地址和端口号,服务器使用它来指明熟知的端口号,然后等待连接

参数说明:

sockfd:套接字描述符,指明创建连接的套接字

my_addr:本地地址,IP地址和端口号

addrlen:地址长度

套接口中port=0表示由内核指定端口号,设定sin_addr为INADDR_ANY,由内核指定IP地址。

(6) int listen(int sockfd,intinput_queue_size)

功能:

面向连接的套接字使用它将一个套接字置为被动模式,并准备接收传入连接。用于服务器,指明某个套接字连接是被动的

参数说明:

Sockfd:套接字描述符,指明创建连接的套接字

input_queue_size:该套接字使用的队列长度,指定在请求队列中允许的最大请求数

(7) int accept(int sockfd, structsockaddr *addr, int *addrlen)

功能:获取传入连接请求,返回新的连接的套接字描述符。

为每个新的连接请求创建了一个新的套接字,服务器只对新的连接使用该套接字,原来的监听套接字接收其他的连接请求。新的连接上传输数据使用新的套接字,使用完毕,服务器将关闭这个套接字。

参数说明:

Sockfd:套接字描述符,指明正在监听的套接字

addr:提出连接请求的主机地址

addrlen:地址长度

(8) int connect(int sockfd,structsockaddr *server_addr,int sockaddr_len)

功能: 同远程服务器建立主动连接,成功时返回0,若连接失败返回-1。

参数说明:

Sockfd:套接字描述符,指明创建连接的套接字

Server_addr:指明远程端点:IP地址和端口号

sockaddr_len :地址长度

(9)int send(int sockfd, const void * data, int data_len, unsigned int flags)

功能:

在TCP连接上发送数据,返回成功传送数据的长度,出错时返回-1。send会将外发数据复制到OS内核中,也可以使用send发送面向连接的UDP报文。

参数说明:

sockfd:套接字描述符

data:指向要发送数据的指针

data_len:数据长度

flags:通常为0

如果send()函数的返回值小于len的话,则你需要再次发送剩下的数据。802.3,MTU为1492B,如果包小于1K,那么send()一般都会一次发送光的。

(10) int recv(int sockfd, void *buf, intbuf_len,unsigned int flags)

功能:

从TCP接收数据,返回实际接收的数据长度,出错时返回-1。

服务器使用其接收客户请求,客户使用它接受服务器的应答。如果没有数据,将阻塞。如果TCP收到的数据大于(/小于)缓存的大小,只抽出能够填满缓存的足够数据(/抽出所有数据并返回它实际接收的字节数)。也可以使用recv接收面向连接的UDP的报文,若缓存不能装下整个报文,填满缓存后剩下的数据将被丢弃。

参数说明:

Sockfd:套接字描述符

Buf:指向内存块的指针

Buf_len:内存块大小,以字节为单位

flags:一般为0(MSG_WAITALL接收到指定长度数据时才返回),设置为 MSG_DONTWAIT为非阻塞

 (11)close(int sockfd)

功能:

撤销套接字.如果只有一个进程使用,立即终止连接并撤销该套接字,如果多个进程共享该套接字,将引用数减一,如果引用数降到零,则关闭连接并撤销套接字。

参数说明:

sockfd:套接字描述符

 3、TCPSocket客户服务器通信实例

下面通过一个简单的例子来形象地演示图1.1中所示的过程,服务器等待客户端连接,连接后等待客户端发送数据,服务器收到客户端发来的数据之后再发送回去,一个“回发”的功能。

服务器代码:

[cpp]  view plain  copy
  1. #include <sys/types.h>  
  2. #include <sys/socket.h>  
  3. #include <stdio.h>  
  4. #include <netinet/in.h>  
  5. #include <arpa/inet.h>  
  6. #include <unistd.h>  
  7. #include <string.h>  
  8. #include <stdlib.h>  
  9. #include <fcntl.h>  
  10. #include <sys/shm.h>  
  11.   
  12. #define MYPORT  8887  
  13. #define QUEUE   20  
  14. #define BUFFER_SIZE 1024  
  15.   
  16. int main()  
  17. {  
  18.     ///定义sockfd  
  19.     int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);  
  20.       
  21.     ///定义sockaddr_in  
  22.     struct sockaddr_in server_sockaddr;  
  23.     server_sockaddr.sin_family = AF_INET;  
  24.     server_sockaddr.sin_port = htons(MYPORT);  
  25.     server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  26.       
  27.     ///bind,成功返回0,出错返回-1  
  28.     if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1)  
  29.     {  
  30.         perror("bind");  
  31.         exit(1);  
  32.     }  
  33.       
  34.     printf("监听%d端口\n",MYPORT);  
  35.     ///listen,成功返回0,出错返回-1  
  36.     if(listen(server_sockfd,QUEUE) == -1)  
  37.     {  
  38.         perror("listen");  
  39.         exit(1);  
  40.     }  
  41.       
  42.     ///客户端套接字  
  43.     char buffer[BUFFER_SIZE];  
  44.     struct sockaddr_in client_addr;  
  45.     socklen_t length = sizeof(client_addr);  
  46.       
  47.     printf("等待客户端连接\n");  
  48.     ///成功返回非负描述字,出错返回-1  
  49.     int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);  
  50.     if(conn<0)  
  51.     {  
  52.         perror("connect");  
  53.         exit(1);  
  54.     }  
  55.     printf("客户端成功连接\n");  
  56.       
  57.     while(1)  
  58.     {  
  59.         memset(buffer,0,sizeof(buffer));  
  60.         int len = recv(conn, buffer, sizeof(buffer),0);  
  61.         //客户端发送exit或者异常结束时,退出  
  62.         if(strcmp(buffer,"exit\n")==0 || len<=0)  
  63.             break;  
  64.         printf("来自客户端数据:%s\n",buffer);  
  65.         send(conn, buffer, len, 0);  
  66.         printf("发送给客户端数据:%s\n",buffer);  
  67.     }  
  68.     close(conn);  
  69.     close(server_sockfd);  
  70.     return 0;  
  71. }  

客户端代码:

[cpp]  view plain  copy
  1. #include <sys/types.h>  
  2. #include <sys/socket.h>  
  3. #include <stdio.h>  
  4. #include <netinet/in.h>  
  5. #include <arpa/inet.h>  
  6. #include <unistd.h>  
  7. #include <string.h>  
  8. #include <stdlib.h>  
  9. #include <fcntl.h>  
  10. #include <sys/shm.h>  
  11.   
  12. #define MYPORT  8887  
  13. #define BUFFER_SIZE 1024  
  14. char* SERVER_IP = "127.0.0.1";  
  15.   
  16. int main()  
  17. {  
  18.     ///定义sockfd  
  19.     int sock_cli = socket(AF_INET,SOCK_STREAM, 0);  
  20.       
  21.     ///定义sockaddr_in  
  22.     struct sockaddr_in servaddr;  
  23.     memset(&servaddr, 0, sizeof(servaddr));  
  24.     servaddr.sin_family = AF_INET;  
  25.     servaddr.sin_port = htons(MYPORT);  ///服务器端口  
  26.     servaddr.sin_addr.s_addr = inet_addr(SERVER_IP);  ///服务器ip  
  27.       
  28.     printf("连接%s:%d\n",SERVER_IP,MYPORT);  
  29.     ///连接服务器,成功返回0,错误返回-1  
  30.     if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)  
  31.     {  
  32.         perror("connect");  
  33.         exit(1);  
  34.     }  
  35.     printf("服务器连接成功\n");  
  36.     char sendbuf[BUFFER_SIZE];  
  37.     char recvbuf[BUFFER_SIZE];  
  38.     while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)  
  39.     {  
  40.         printf("向服务器发送数据:%s\n",sendbuf);  
  41.         send(sock_cli, sendbuf, strlen(sendbuf),0); ///发送  
  42.         if(strcmp(sendbuf,"exit\n")==0)  
  43.             break;  
  44.         recv(sock_cli, recvbuf, sizeof(recvbuf),0); ///接收  
  45.         printf("从服务器接收数据:%s\n",recvbuf);  
  46.           
  47.         memset(sendbuf, 0, sizeof(sendbuf));  
  48.         memset(recvbuf, 0, sizeof(recvbuf));  
  49.     }  
  50.       
  51.     close(sock_cli);  
  52.     return 0;  
  53. }  

运行结果:




TCP编程注意:

1、Accept()接收返回值为客户端的新socket,原来的socket用户继续监听端口。

2、Recv()返回0代表连接关闭。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值