服务器
1、建立连接
socket(),分配文件描述符,即监听套接字
bind(),将套接字与本地IP地址和端口绑定
listen(),监听特定端口,可设置监听连接最大个数
accept(),阻塞等待客户端连接
2、数据收发
read()/recv()阻塞等待客户端发送数据,收到数据后从read()/recv()返回数据和数据数量
write()/send()将处理结果发送给客户端,然后继续调用read()/recv()等待客户端请求
3、关闭连接
当read()/recv()返回0的时候,说明客户端发来FIN数据包,即关闭连接,调用close()关闭连接套接字和监听套接字
当客户端调用close()关闭连接套接字时也会受到客户端发开的断开连接请求
客户端
1、建立连接
socket(),分配文件描述符
connect(),向服务器发送建立连接请求
2、数据收发
write()/send(),将数据发送给服务器
read()/recv(),阻塞等待服务器应答回复数据
3、关闭连接
当没有数据发送的时候,调用close()关闭连接套接字,即关闭连接,向服务器发送FIN数据报
或者write()/send()发送0字节给服务器表示断开连接
TCP通信过程
图片转载自:https://blog.csdn.net/upupday19/article/details/78916142
示例代码
头文件:std.h
#ifndef _STD_
#define _STD_
// 头文件 //
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <grp.h>
#include <string.h>
#include <time.h>
#include <string.h>
#include <errno.h>
//tcp//
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
//错误提示//
#define err_exit(function) \
do { \
fprintf(stderr, "in %s at %s %d:\n%s : %s\n", __FUNCTION__, __FILE__, __LINE__ - 1, function, strerror(errno)); \
exit(EXIT_FAILURE); \
} while(0)
#endif
服务器代码:test_tcp_server.c
//服务器端//
#include "std.h"
int main(void)
{
//定义服务器监听套接字和连接套接字
int listenfd = -1;
int connfd = -1;
struct sockaddr_in server, client;//定义服务器对应的套接字地址
char buf[BUFSIZ];
int ret=-1;
//初始化套接字地址结构体
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;//IPv4
server.sin_port = htons(888);//设置监听端口
//server.sin_addr.s_addr = inet_addr("192.168.1.50");//谁的地址?
//server.sin_addr.s_addr = inet_addr("0");//?
//server.sin_addr.s_addr = INADDR_ANY;//?
server.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY接收任意IP的连接请求
//创建套接字
if (0 > (listenfd = socket(AF_INET, SOCK_STREAM, 0)))
err_exit("socket");
//绑定套接字和本地IP地址和端口
if (0 > bind(listenfd, (struct sockaddr *)&server, sizeof(server)))
err_exit("bind");
//设置listen_fd为监听描述符
listen(listenfd, 1024);//1024为最大连接个数
printf("listen...\n");
//accept阻塞等待客户端请求
memset(&client, 0, sizeof(client));
socklen_t len = sizeof(client);
if (0 > (connfd = accept(listenfd, (struct sockaddr *)&client, &len)))
err_exit("accept");
printf("client's ip is: %s, port is: %d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
fflush(stdout);
while(1)
{
//读取客户端发来的信息
memset(buf, 0, sizeof(buf));
if (0 > (ret = recv(connfd, buf, BUFSIZ, 0)))
err_exit("recv");
else if (0 == ret)//客户端关闭连接请求断开或发送0字节数据会走这里
{
printf("client quit!\n");
//关闭连接套接字
close(connfd);
//关闭监听套接字
close(listenfd);
exit(0);
}
buf[ret] = '\0';
printf("recv client: %s\n", buf);
//向客户端发送信息
send(connfd, "ok", sizeof("ok"), 0);
}
}
客户端代码:test_tcp_client.c
//客户端//
#include "std.h"
int main(void)
{
int connfd = -1;//定义客户端套接字
struct sockaddr_in server;//定义服务器的套接字地址
char buf[BUFSIZ];//stdlib.h定义BUFSIZ=8192
int ret=-1;
//初始化服务器套接字地址
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;//IPv4
server.sin_port = htons(888);//服务器端口
server.sin_addr.s_addr = inet_addr("192.168.1.50");//服务器IP地址
//创建套接字
if (0 > (connfd = socket(AF_INET, SOCK_STREAM, 0)))
err_exit("socket");
//发送连接请求
if (0 > connect(connfd, (struct sockaddr *)&server, sizeof(server)))
err_exit("connect");
printf("connect success!\n");
//输入并向服务器发送字符串
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
send(connfd, buf, strlen(buf)-1, 0);//-1目的是去除\n字符
//从服务器接收数据
if (0 > (ret = recv(connfd, buf, BUFSIZ, 0)))
err_exit("recv");
else if (0 == ret) {
printf("server quit!\n");
close(connfd);
}
buf[ret] = '\0';
printf("recv server: %s\n", buf);
//memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
send(connfd, buf, strlen(buf)-1, 0);
close(connfd);
exit(0);
}
扩展
select,poll,epoll实现机制区别:https://www.cnblogs.com/aspirant/p/9166944.html
问题
端口号非法,需要修改端口号,这里修为2048
[yubo.wang@localhost socket]$ ./server
in main at test_tcp_server.c 27:
bind : Permission denied