基于TCP聊天:
clientA.c clientB.c
socket socket
connect bind
listen
accept
pthread_create
pthread_create pthread_create
pthread_join pthread_create
pthread_join pthread_join
pthread_join
1. TCP编程——函数接口
1.1 socket(首先监听套接字)
1. 定义
int socket(int domain, int type, int protocol);
2. 功能
创建一个用来进程通信的套接字,返回文件描述符
3. 参数
domain:AF_INET IPv4协议族
type:SOCK_STREAM 流式套接字 tcp传输协议
SOCK_DGRAM 数据报套接字 udp传输协议
SOCK_RAW 原始套接字
protocol:
默认为0
4. 返回值
成功返回套接字新文件描述符
失败返回-1
5. 注意
1.2 inet_addr
1. 定义
in_addr_t inet_addr(const char *cp);
2. 功能
将字符串IP地址转换为二进制IP地址
3. 参数
cp:字符串IP地址空间首地址
4. 返回值
成功返回二进制IP地址
5. 注意
1.3 htons
1. 定义
uint16_t htons(uint16_t hostshort);
2. 功能
将本地字节序(小端)转换为网络字节序(大端)
3. 参数
hostshort:本地端口号
4. 返回值
返回网络字节序端口号
uint16_t ntohs(uint16_t netshort);
功能:
将网络字节序(大端)转换为本地字节序(小端)
1.4 bind
1. 定义
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
2. 功能
将套接字与IP地址端口绑定在一起
3. 参数
sockfd:文件描述符
addr:结构体空间首地址
addrlen:信息的长度
4. 返回值
成功返回0
失败返回-1
5. 注意
1.5 connect
1. 定义
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
2. 功能
向接收端发送三次握手链接请求
3. 参数
sockfd:文件描述符
addr:接收方地址空间首地址
addrlen:接收方地址的大小
4. 返回值
成功返回0
失败返回-1
5. 注意
1.6 listen
1. 定义
int listen(int sockfd, int backlog);
2. 功能
监听链接请求
3. 参数
sockfd:文件描述符
backlog:允许最多等待链接的个数
4. 返回值
成功返回0
失败返回-1
5. 注意
1.7 accept(返回通信套接字)
1. 定义
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
2. 功能
处理等待队列中第一个链接
3. 参数
sockfd:文件描述符
addr:存放链接对方地址信息空间首地址
addrlen:想要接收地址大小变量空间首地址
4. 返回值
成功返回与发送端建立的新文件描述符(通信套接字)
失败返回-1
5. 注意
1.8 send (发的太块会阻塞)
1. 定义
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
2. 功能
发送数据
3. 参数
sockfd:文件描述符
buf:存放数据空间首地址
len:发送数据长度
flags:属性 默认为0
4. 返回值
成功返回发送字节数
失败返回-1
5. 注意
1.9 recv
1. 定义
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
2. 功能
接收数据
3. 参数
sockfd:文件描述符
buf:存放数据空间首地址
len:最多接收数据大小
flags:属性 默认为0
4. 返回值
成功返回实际接收字节数
失败返回-1
对方关闭套接字返回0
5. 注意
2. TCP示例程序
2.1 TCP单向通信
(1)头文件
#ifndef _HEAD_H_
#define _HEAD_H_
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#endif
(2)makefile
all:send recv
send:send.c
gcc $^ -o $@
recv:recv.c
gcc $^ -o $@
(3)recv.c
#include "head.h"
int main(int argc, char const *argv[])
{
int confd = 0;
int listfd = 0;
int ret_connect = 0;
int ret_bind = 0;
int ret_listen = 0;
struct sockaddr_in recvaddr;
struct sockaddr_in sendaddr;
socklen_t addrlen = sizeof(sendaddr);
ssize_t ret_send = 0;
ssize_t ret_recv = 0;
listfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == listfd)
{
perror("socket error!\n");
return -1;
}
bzero(&recvaddr,sizeof(recvaddr));
bzero(&sendaddr,sizeof(sendaddr));
recvaddr.sin_family = AF_INET;
recvaddr.sin_port = htons(50000);
recvaddr.sin_addr.s_addr = inet_addr("192.168.0.109");
ret_bind = bind(listfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
if (-1 == ret_bind)
{
perror("bind error!\n");
return -1;
}
ret_listen = listen(listfd, 3);
if (-1 == ret_listen)
{
perror("listen error!\n");
return -1;
}
confd = accept(listfd, (struct sockaddr *)&sendaddr, &addrlen);
if (-1 == confd)
{
perror("accept error!\n");
return -1;
}
while (1)
{
char buf[256] = {0};
ret_recv = recv(confd, buf, sizeof(buf), 0);
if (ret_recv <= 0)
{
break;
}
time_t tm;
time(&tm);
sprintf(buf, "%s %s", buf, ctime(&tm));
ret_send = send(confd, buf, strlen(buf), 0);
if (-1 == ret_send)
{
perror("send error!\n");
return -1;
}
}
close(confd);
close(listfd);
return 0;
}
(4)send.c
#include "head.h"
int main(int argc, char const *argv[])
{
int confd = 0;
int ret_connect = 0;
struct sockaddr_in recvaddr;
socklen_t addrlen = 0;
ssize_t ret_send = 0;
ssize_t ret_recv = 0;
confd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == confd)
{
perror("socket error!\n");
return -1;
}
bzero(&recvaddr, sizeof(recvaddr));
recvaddr.sin_family = AF_INET;
recvaddr.sin_port = htons(50000);
recvaddr.sin_addr.s_addr = inet_addr("192.168.0.109");
ret_connect = connect(confd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
if (-1 == ret_connect)
{
perror("connect error!\n");
return -1;
}
while (1)
{
char buf[256] = {"supercarrydoinb"};
ret_send = send(confd, buf, strlen(buf), 0);
if (-1 == ret_connect)
{
perror("send error!\n");
return -1;
}
ret_recv = recv(confd, buf, sizeof(buf), 0);
if (ret_recv <= 0)
{
break;
}
printf("%s", buf);
sleep(1);
}
close(confd);
return 0;
}
2.2 TCP实现多线程双机聊天(chat)——注意:!!线程编译gcc要加后缀 -lpthread
(1)头文件
#ifndef _HEAD_H_
#define _HEAD_H_
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#endif
(2)makefile
all:send recv
send:send.c
gcc $^ -o $@ -lpthread
recv:recv.c
gcc $^ -o $@ -lpthread
(3)recv.c
#include "head.h"
struct sockaddr_in sendaddr;
struct sockaddr_in recvaddr;
void *th1(void *arg)
{
int confd = *(int *)arg;
ssize_t ret_recv = 0;
while (1)
{
char buf[256] = {0};
ret_recv = recv(confd, buf, sizeof(buf), 0);
if (-1 == ret_recv)
{
perror("recv error!\n");
}
printf("from recv: %s\n", buf);
}
}
void *th2(void *arg)
{
int confd = *(int *)arg;
ssize_t ret_send = 0;
while (1)
{
printf("to recv:");
char buf[256] = {0};
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = '\0';
ret_send= send(confd, buf, strlen(buf), 0);
if (-1 == ret_send)
{
perror("send error!\n");
}
}
}
int main(int argc, char const *argv[])
{
int confd = 0;
int ret_connect = 0;
socklen_t addrlen = sizeof(sendaddr);
ssize_t ret_recv = 0;
ssize_t ret_send = 0;
pthread_t tid1;
pthread_t tid2;
char buf[256] = {0};
confd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == confd)
{
perror("socket error!\n");
return -1;
}
bzero(&recvaddr, sizeof(recvaddr));
bzero(&sendaddr, sizeof(sendaddr));
recvaddr.sin_family = AF_INET;
recvaddr.sin_port = htons(50000);
recvaddr.sin_addr.s_addr = inet_addr("192.168.0.109");
ret_connect = connect(confd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
if (-1 == ret_connect)
{
perror("connect error!\n");
return -1;
}
#if 0
ret_recv = recv(confd, buf, sizeof(buf), 0);
if (-1 == ret_recv)
{
perror("recv error!\n");
return -1;
}
#endif
pthread_create(&tid1, NULL, th1, &confd);
pthread_create(&tid2, NULL, th2, &confd);
pthread_join(tid1, NULL);
pthread_join(tid1, NULL);
return 0;
}
(4)send.c
#include "head.h"
struct sockaddr_in sendaddr;
struct sockaddr_in recvaddr;
void *th1(void *arg)
{
int confd = *(int *)arg;
ssize_t ret_recv = 0;
while (1)
{
char buf[256] = {0};
ret_recv = recv(confd, buf, sizeof(buf), 0);
if (-1 == ret_recv)
{
perror("recv error!\n");
}
printf("from send: %s\n", buf);
}
}
void *th2(void *arg)
{
int confd = *(int *)arg;
ssize_t ret_send = 0;
while (1)
{
printf("to send:");
char buf[256] = {0};
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = '\0';
ret_send = send(confd, buf, strlen(buf), 0);
if (-1 == ret_send)
{
perror("send error!\n");
}
}
}
int main(int argc, char const *argv[])
{
int listfd = 0;
int confd = 0;
int ret_bind = 0;
int ret_listen = 0;
socklen_t addrlen = sizeof(sendaddr);
ssize_t ret_recv = 0;
ssize_t ret_send = 0;
pthread_t tid1;
pthread_t tid2;
char buf[256] = {0};
listfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == listfd)
{
perror("socket error!\n");
return -1;
}
bzero(&recvaddr, sizeof(recvaddr));
bzero(&sendaddr, sizeof(sendaddr));
recvaddr.sin_family = AF_INET;
recvaddr.sin_port = htons(50000);
recvaddr.sin_addr.s_addr = inet_addr("192.168.0.109");
ret_bind = bind(listfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
if (-1 == ret_bind)
{
perror("bind error!\n");
return -1;
}
ret_listen = listen(listfd, 3);
if (-1 == ret_listen)
{
perror("listen error!\n");
return -1;
}
confd = accept(listfd, (struct sockaddr *)&sendaddr, &addrlen);
if (-1 == confd)
{
perror("accept error!\n");
return -1;
}
#if 0
ret_recv = recv(confd, buf, sizeof(buf), 0);
if (-1 == ret_recv)
{
perror("recv error!\n");
return -1;
}
#endif
pthread_create(&tid1, NULL, th1, &confd);
pthread_create(&tid2, NULL, th2, &confd);
pthread_join(tid1, NULL);
pthread_join(tid1, NULL);
return 0;
}
2.3 TCP实现文件的传输(结构体)
(1)头文件
#ifndef _HEAD_H_
#define _HEAD_H_
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#endif
(2)makefile
all:send recv
send:send.c
gcc $^ -o $@ -lpthread
recv:recv.c
gcc $^ -o $@ -lpthread
(3)recv.c
#include "head.h"
typedef struct
{
char buf[1024];
char filename[32];
int rd_ret;
int total;
}MSG;
int main(int argc, char const *argv[])
{
int ret_connect = 0;
int ret_stat = 0;
int confd = 0;
int openfd = 0;
ssize_t ret_recv = 0;
ssize_t ret_send = 0;
struct sockaddr_in recvaddr;
MSG msg;
struct stat st;
confd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == confd)
{
perror("socket error!\n");
return -1;
}
bzero(&recvaddr, sizeof(recvaddr));
bzero(&msg, sizeof(msg));
recvaddr.sin_family = AF_INET;
recvaddr.sin_port = htons(50000);
recvaddr.sin_addr.s_addr = inet_addr("192.168.0.109");
ret_connect = connect(confd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
if (-1 == ret_connect)
{
perror("connetc error!\n");
return -1;
}
openfd = open("./1.png", O_RDONLY);
if (-1 == openfd)
{
perror("open error!\n");
return -1;
}
strcpy(msg.filename, "2.png");
ret_stat = stat("./1.png", &st);
if (-1 == ret_stat)
{
perror("stat error!\n");
return -1;
}
msg.total = st.st_size;
while (1)
{
bzero(msg.buf, sizeof(msg.buf));
msg.rd_ret = read(openfd, msg.buf, sizeof(msg.buf));
send(confd, &msg, sizeof(msg), 0);
if (msg.rd_ret <= 0)
{
break;
}
char buf[256] = {0};
ret_recv = recv(confd, buf, sizeof(buf), 0);
if (ret_recv <= 0)
{
break;
}
usleep(1000 * 200);
}
close(confd);
close(openfd);
return 0;
}
(4)send.c
#include "head.h"
typedef struct
{
char buf[1024];
char filename[32];
int rd_ret;
int total;
}MSG;
int main(int argc, char const *argv[])
{
int listfd = 0;
int ret_bind = 0;
int ret_listen = 0;
int confd = 0;
ssize_t ret_recv = 0;
ssize_t ret_send = 0;
struct sockaddr_in recvaddr;
struct sockaddr_in sendaddr;
socklen_t addrlen = sizeof(sendaddr);
MSG msg;
listfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == listfd)
{
perror("socket error!\n");
return -1;
}
bzero(&recvaddr, sizeof(recvaddr));
bzero(&sendaddr, sizeof(sendaddr));
bzero(&msg, sizeof(msg));
recvaddr.sin_family = AF_INET;
recvaddr.sin_port = htons(50000);
recvaddr.sin_addr.s_addr = inet_addr("192.168.0.109");
ret_bind = bind(listfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
if (-1 == ret_bind)
{
perror("bind error!\n");
return -1;
}
ret_listen = listen(listfd, 3);
if (-1 == ret_listen)
{
perror("listen error!\n");
return -1;
}
confd = accept(listfd, (struct sockaddr *)&sendaddr, &addrlen);
if (-1 == confd)
{
perror("accept error!\n");
return -1;
}
int openfd = 0;
int flag = 0;
int total = 0;
int current_size = 0;
while (1)
{
ret_recv = recv(confd, &msg, sizeof(msg), 0);
if (ret_recv <= 0)
{
break;
}
if (0 == msg.rd_ret)
{
printf("file upload end\n");
return -1;
}
if (0 == flag)//拿到文件总大小
{
openfd = open(msg.filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (-1 == openfd)
{
perror("open error!\n");
return -1;
}
flag = 1;
total = msg.total;
}
write(openfd, msg.buf, msg.rd_ret);
current_size += msg.rd_ret;
printf("%d / %d\n", current_size, total);
char buf[256] = "go on";
send(confd, buf, strlen(buf), 0);
}
close(confd);
close(openfd);
close(listfd);
return 0;
}
3. TCP机制
1.序列号:发送字节内容在缓存区中的编号(要发送字节的编号)
2.确认号:收到字节内容的编号(只有ACK为1时才有确认号)
本次发送数据序列号为上次收到ack数据包的确认号
本次确认号为上次收到数据的序列号+实际收到数据长度
4. TCP特点
1.实现机制复杂
2.占用资源开销大
3.安全、可靠、可控
4.面向连接传输方式