文章目录
TCP服务器的实现过程
1. 创建套接字:socket函数
int socket(int domain, int type, int protocol);
domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等(socket的类型有哪些?)。
protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
功能:创建一个套接字文件,然后以文件形式来操作通信
2. 绑定IP地址以及端口号:bind()
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数:
scokfd:socket函数创建的套接字
const struct sockaddr *addr:保存IP和端口号的结构体
socklen_t addrlen:结构体的长度功能:指定通信协议的套接字文件与IP以及端口绑定起来
返回值:成功返回0,失败返回-1
struct socketaddr_in
{
sa_family sin_family;//设置IPV4 IPV6
_be16 sin_port;//设置端口号
struct in_addr sin_addr;//设置IP
};
unit32_t htonl(unit32_t hostlong);//字节序转化的函数
unit16_t htons(unit16_t hostshort);
h代表host,n表示network,s代表16位短整型
3. 将套接字文件描述符,从主动变为被动(做监听准备)listen()
int listen(int sockfd, int backlog);
int sockfd:套接字文件描述
int backlog:指定队列的容量(小于30即可,一般是2,3)
功能:将套接字文件描述符,从主动变为被动(做监听准备)
返回值:成功返回0,失败返回-1
4. 等待客户端响应,accept()
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:被动监听客户端发起的三次握手请求,成功,即建立连接
返回值:成功返回一个通信描述符,专门用于与该连接成功的客户端通信,总之后续服务器与客户端的正式通信,使用的是accept
举例实现服务器:
#include"stdio.h"
#include"stdlib.h"
#include"string.h"
#include"unistd.h"
#include"sys/types.h"
#include"sys/socket.h"
#include"arpa/inet.h"
#define PORT 33333
int main()
{
int sockfd;
int cfd;
int c_len;
char buffer[1024];
struct sockaddr_in addr;
struct sockaddr_in c_addr;
sockfd = socket(AF_INET, SOCK_STREAM,0);
if(-1 == sockfd)
{
perror("socket create error!");
exit(1);
}
printf("socket success\n");
int opt = 1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
bzero(&addr,sizeof(struct sockaddr_in));
addr.sin_family =AF_INET;
addr.sin_port =htons(PORT);
addr.sin_addr.s_addr = inet_addr("192.168.12.13");
if(bind(sockfd,(struct sockaddr *)(&addr),sizeof(struct sockaddr_in)) <0)
{
perror("bind error!");
exit(1);
}
printf("bind success\n");
if(listen(sockfd,3)<0)
{
perror("listen error!");
exit(1);
}
printf("listen success\n");
while(1)
{
memset(buffer,0,sizeof(buffer));
bzero(&c_addr,sizeof(struct sockaddr_in));
c_len = sizeof(struct sockaddr_in);
printf("accept.......\n");
cfd = accept (sockfd,(struct sockaddr *)(&c_addr),&c_len);
if(cfd<0)
{
perror("accept error!");
exit(1);
}
printf("port =%d ip =%s\n",ntohs(c_addr.sin_port),inet_ntoa(c_addr.sin_addr));
//read(cfd,buffer,sizeof(buffer));
recv(cfd,buffer,sizeof(buffer),0);
printf("recv = %s\n",buffer);
usleep(2);
//write(cfd,buffer,strlen(buffer));
send(cfd,buffer,sizeof(buffer),0);
close(cfd);
}
return 0;
}
TCP客户端的实现过程
1. 用socket创建套接字文件,指定使用TCP协议
2. 调用connect主动向服务器发起三次握手,进行连接
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:向服务器主动发起连接
返回值:成功返回0,失败返回-1
3. 调用read(recv)和write(send)收发数据
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:向对方发送消息
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:接收对象发送的消息
sockfd:用于通信描述符
*buf:应用缓存
len:长度
flags:0
4. 调用close或者shutdown关闭连接
int shoutdown(int sockfd,int how)
sockfd:返回的文件描述符
how:如何断开连接
how:
SHUT_RD:只断开读连接
SHUT_WR:只断开写连接
SHUT_RDWR:读写连接都断开
功能:可以按照要求关闭连接,而且不管有多少个描述符指向同一连接,只要调用shutdown去操作了其中某个操作符,连接就会立即断开
返回值:成功返回0,失败返回-1
- 无法绑定的解决:
int opt =1;
setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt) );
举例实现客户端:
#include"stdio.h"
#include"stdlib.h"
#include"string.h"
#include"unistd.h"
#include"sys/types.h"
#include"sys/socket.h"
#include"arpa/inet.h"
#define PORT 33333
int main()
{
int sockfd;
struct sockaddr_in s_addr;
if((sockfd = socket(AF_INET,SOCK_STREAM,0))<0)
{
perror("socket error");
exit(1);
}
printf("client socket success!\n");
bzero(&s_addr,sizeof(struct sockaddr_in));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(PORT);
s_addr.sin_addr.s_addr=inet_addr("192.168.12.13");
if(connect(sockfd,(struct sockaddr *)(&s_addr),sizeof(struct sockaddr_in))<0)
{
perror("connect error!");
exit(1);
}
printf("connect success!\n");
write(sockfd,"hello world",12);
char buffer[1024];
read(sockfd,buffer,sizeof(buffer));
printf("recv server =%s\n",buffer);
return 0;
}
闲的无聊做了一丢丢优化:
- 实现客户端循环读入,且空格不影响输入
- 实现服务器持续接收,不会死循环读
server.c
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "arpa/inet.h"
#define PORT 33333
int main()
{
int sockfd;
int cfd;
int c_len;
char buffer[1024];
struct sockaddr_in addr;
struct sockaddr_in c_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
perror("socket create error!");
exit(1);
}
printf("socket success\n");
int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&addr, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = inet_addr("192.168.128.128");
if (bind(sockfd, (struct sockaddr *)(&addr), sizeof(struct sockaddr_in)) < 0)
{
perror("bind error!");
exit(1);
}
printf("bind success\n");
if (listen(sockfd, 3) < 0)
{
perror("listen error!");
exit(1);
}
printf("listen success\n");
while (1)
{
memset(buffer, 0, sizeof(buffer));
bzero(&c_addr, sizeof(struct sockaddr_in));
c_len = sizeof(struct sockaddr_in);
printf("accept.......\n");
cfd = accept(sockfd, (struct sockaddr *)(&c_addr), &c_len);
if (cfd < 0)
{
perror("accept error!");
exit(1);
}
while (1)
{
while (1)
{
printf("port =%d ip =%s\n", ntohs(c_addr.sin_port), inet_ntoa(c_addr.sin_addr));
recv(cfd, buffer, sizeof(buffer), 0);
printf("recv = %s\n", buffer);
usleep(2);
send(cfd, buffer, sizeof(buffer), 0);
break;
}
}
close(cfd);
}
return 0;
}
client.cpp
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "arpa/inet.h"
#include <iostream>
using namespace std;
#define PORT 33333
int main()
{
int sockfd;
struct sockaddr_in s_addr;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket error");
exit(1);
}
cout << "client socket success!" << endl;
bzero(&s_addr, sizeof(struct sockaddr_in));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(PORT);
s_addr.sin_addr.s_addr = inet_addr("192.168.182.128"); // ifconfig查一下
if (connect(sockfd, (struct sockaddr *)(&s_addr), sizeof(struct sockaddr_in)) < 0)
{
perror("connect error!");
exit(1);
}
cout << "connect success!" << endl;
while (1)
{
char str[1024];
cin.getline(str,1024);
write(sockfd, str, sizeof(str));
char buffer[1024];
read(sockfd, buffer, sizeof(buffer));
cout << "recv server = " << buffer << endl;
}
return 0;
}
运行效果:
server:
client
这里还是明确:里面的ip地址,要查一下自己的,然后换成自己的