网络编程:使用tcp协议创建简单cs架构
这周看了网络编程的书,写了一个简单的cs架构,以下是我写的代码
//客户端
const int MAXLINE = 1024;
const int S_PORT = 4000;
const int BACKLOG = 20;
const char * IP = "39.100.115.xxx";
int main(int argc, char*argv[])
{
struct sockaddr_in svr_addr;
int res = 0;
bzero(&svr_addr, sizeof(svr_addr));
//打开网络通讯端口
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
svr_addr.sin_family = AF_INET;
svr_addr.sin_port = htons(S_PORT);
svr_addr.sin_addr.s_addr = inet_addr("192.168.21.136");
// 连接服务器
res = connect(socketfd, (struct sockaddr*)&svr_addr, sizeof(svr_addr));
if(res == -1) {
printf("fail\n");
return -1;
}
printf("success\n");
const char * msg = "Hello";
int wres = write(socketfd, msg, sizeof(msg));
if(wres == -1){
printf("wrete fail: %d\n", wres);
}
return 0;
}
//服务器
const int MAXLINE = 1024;
const int S_PORT = 4000;
const int BACKLOG = 20;
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr;
char buff[MAXLINE];
time_t ticks;
int listenfd, connfd;
//打开网络通讯端口
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
{
printf("listen fail: %d\n", listenfd);
return -1;
}
printf("listen: %d\n", listenfd);
//设置internet协议,不绑定本地IP,设置本地端口
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(S_PORT);
//将litenfd和servaddr绑定在一起,使listenfd作用于网络的文件描述符监听servaddr所描述的地址和端口
int binres = bind(listenfd, (struct sockaddr*)(&servaddr), sizeof(servaddr));
if(binres == -1)
{
printf("fail bind:%d\n", binres);
return -1;
}
printf("bind success:%d\n", binres);
//声明lintenfd处于监听状态,允许做多backlog个客户链接
int res = listen(listenfd, BACKLOG);
if(res == -1)
{
printf("listen fail\n");
return -1;
}
printf("linten success%d\n", res);
while(1)
{
printf("wait\n");
struct sockaddr cliaddr;
memset(&cliaddr, 0, sizeof(cliaddr));
int size_cli = sizeof(cliaddr);
//阻塞等待客户端连接
connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &size_cli);
if(connfd == -1)
{
printf("fail\n");
return -1;
}
printf("connect\n");
read(connfd, buff, 1024);
printf("%s\n", buff);
close(connfd);
}
return 0;
}
在上面的代码中,服务器和客户端都使用了socket这个接口,这个接口的作用是打开一个网络通讯端口,在linux和window下都要使用这个接口进行网络通讯,唯一不同的是,返回值不同(window下为SOCKET机构,结构内容这里就不探讨了),socket的函数原型是这样,
int socket(int domain, int type, int protocol);
socket()打开一个网络通讯端口,失败返回-1,成功的话,返回文件描述符,程序
可以像读写文件一样用read/write直接读写数据。(这也源于unix/linux的基本
哲学,一切皆文件,包括用到的鼠标,键盘等,都可以视为文件)
domain 表示地址族,也就是IP地址类型,AF_INET表示IPv4地址,AF_INET6
表示IPv6地址
type 表示数据传输方式,对于TCP协议,SOCK_STREAM表示面向流的传输协议。
对于UDP协议,SOCK_DGRAM表示面向数据报的传输协议
protocol表示传输协议,常用的有IPPROTO_TCP和IPPTOTO_UDP,分别表示TCP和
UDP协议吗,一般情况下protocol指定为0就可以了。
int bind(int socketfd, const sockaddr *myaddr, socklen_t addrlen);
成功返回0,失败返回-1
socketfd是使用socket()打开的网络通讯端口,myaddr是数据结构,实际上
可以指定多种协议的结构,而他们的长度不同,所以需要第三个参数addrlen来指
定结构体长度,来判断就够类型
int listen(int sockfd, int backlog);
成功返回0,失败返回-1
linten声明服务器处于监听状态,并且允许最多有backlog个客户端处于监听
int accept(int sockfd, struct sockaddr *clienaddr, socklen_t *addrlen);
三次握手后服务器调用accept接受连接,如果服务器调用accept时还没有客户端
请求连接,就阻塞等待客户端连接。
客户端
int connect(int sockfd,const struct sockaddr *serveraddr, socklen_t addrlen);
connect成功返回0,出错返回-1
connect与bind的参数一致,区别在于bind的参数是自己的地址,connect的参数是
对方的地址
客户端不是不允许调用bind,只是没有必要调用固定端口号,服务器也不是必须
调用bind,只是不调用的话没法固定端口号,每次启动服务器的端口号都不固定
客户端连接会很麻烦
这是这两天学习的一些总结,还有,以上是基于linux系统来进行测试的。服务器搭建在阿里云服务器,本地客户端连接服务器的公网IP,测试可以通信。
上次学习这个知识点还是16年,这几年的工作内容也没有接触到网络相关,一直是使用Qt。Qt这套框架很强大。但是工作中用到最多的是GUI和Core的部分,数据库没有用的,网络也基本上没有接触,甚至多线程都很少,有一次我用了多线程,后来还被砍掉。。。
还是很迷茫,不怎么去学习网络