1 前言
在进行网络socket编程之前,首先要明白计算机通信的原理。网络socket编程是建立在客户端和服务端之间的相互通信,这就必须要了解到计算机网络的知识。我所写的内容是实现简单的本机之间的相互通信。还可以上升到局域网之间不同pc的通信,甚至是不在同一局域网内的pc之间的相互通信。
2概念梳理
2.1 UNIX下文件I/O
写客户端和服务端程序时,你需要利用C语言的基本语法和UNIX下相关系统调用以及库函数。包括socket,connect,bind,listen,write,read的使用等等。
2.2 计算机网络
实现客户端和服务端通信,必须要弄清楚通信原理,包括TCP/UDP协议,IPV4/IPV6的理解等。对于本例客户端和服务端,他们分别占用着同一ip下不同的端口,所以在进行通信时,必须要弄清楚目的ip目的端口以及源ip源端口。我们需要指定连接服务端的端口,但是客户端的端口不用去指定,因为客户端可以有很多个,可以让linux操作系统内核随机分配,这样会大大减少内存的压力。
2.3 linux基本操作
执行操作时,我们必须要了解在linux命令行下的基本操作才能对我们的pc进行操控,比如gcc的调试和vim的基本使用,这都是非常有必要的。
3代码及运行结果
3.1 服务端程序
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#define MSG "hello , I am accept\n"
void useage(char *progname)
{
printf("%s useage:\n",progname);
printf("-p(--port):specify server listen port\n");
printf("-h(--help):print this help information\n");
return;
}
int main(int argc, char **argv)
{
int sockfd = -1;
int clifd = -1;
int rv = -1;
struct sockaddr_in cliaddr;
struct sockaddr_in servaddr;
socklen_t len=1;
int port=0;
char buf[1024];
struct option opts[] = {
{"port",required_argument,NULL,'p'},
{"help",no_argument,NULL,'h'},
{NULL,0,NULL,0}
};
int opt;
int idx;
while((opt = getopt_long(argc,argv,"p:h",opts,&idx)) != -1)
{
switch(opt)
{
case 'p':
port = atoi(optarg);
break;
case 'h':
useage(argv[0]);
return -1;
}
}
if(!port)
{
useage(argv[0]);
return 0;
}
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
printf("Create socket failure:%s\n",strerror(errno));
return -1;
}
printf("Create socket[%d] successfully\n",sockfd);
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(port);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
rv = bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(rv<0)
{
printf("socket[%d] bind on port[%d] failure:%s\n",sockfd,
port,strerror(errno));
return -2;
}
listen(sockfd,13);
// inet_aton(servip,&servaddr.sin_addr);
printf("Connect client successfully:\n");
while(1)
{
clifd = accept(sockfd,(struct sockaddr *)&cliaddr,&len);
if(clifd<0)
{
printf("Accept new client faliure:%s\n",strerror(errno));
continue;
}
printf("Accept new client[%s:%d] successfully!\n",
inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
memset(buf,0,sizeof(buf));
rv = read(clifd,buf,sizeof(buf));
if(rv < 0)
{
printf("read failure:%s\n",strerror(errno));
close(clifd);
continue;
}
else if(rv == 0)
{
printf("socket[%d] get disconnect\n",clifd);
close(clifd);
continue;
}
else if(rv > 0)
{
printf("read %dbyte from client:%s\n",rv,buf);
}
rv = write(clifd,MSG,sizeof(MSG));
if(rv < 0)
{
printf("write failure:%s\n",strerror(errno));
close(clifd);
continue;
}
printf("close client socket[%d]\n",clifd);
close(clifd);
}
close(sockfd);
}
3.1客户端程序(3种写法)
3.1.1 第一种方法---在程序内指定IP和端口
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MSG_STR "Hello i am zhangkang\n"//添加宏定义,方便write函数中strlen的使用
int main (int argc,char *argv[])
{
int sockfd=-1;
int rv=-1;
struct sockaddr_in servaddr;//ipv4 结构体的定义;
char *servip="127.0.0.1";//与本机进行通信;
int port=6666;//指定服务器端口
char buf[1024];//指定读区
sockfd=socket(AF_INET,SOCK_STREAM,0);
if (socket<0)
{
printf("Creat socker faliure:%s\n",strerror(errno));
return -1;
}
printf("Creat socket[%d] successful!\n",sockfd);
memset(&servaddr,0,sizeof(servaddr));//将定义ipv4的结构体清空;
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(port);
inet_aton(servip,&servaddr.sin_addr);//写入ipv4结构体中的一些数据;
rv=connect(sockfd(struct sockaddr *)&servaddr,sizeof(servaddr));
if(rv<0)
{
printf("Connect to server[%s:%d] failure:%s\n",servip,port,strerror(errno));
return -2;
}
printf("Connect to server[%s:%d] successful!",servip,port);
while(1)
{
rv=write(sockfd,MSG_STR,strlen(MSG_STR));
if(rv<0)
{
printf("Write to server by sockfd[%d] failure:%s\n",sockfd,strerror(errno));
break;
}
memset(buf,0,sizeof(buf));//将buf清空;
rv=read(sockfd,buf,sizeof(buf));
if(rv<0)
{
printf("Read data from server by sockfd[%d] failure:%s\n",sockfd,strerror(errno));
break;
}
else if(rv==0)
{
printf("Socket[%d] get disconnected\n",sockfd);
break;
}
else if(rv>0)
{
printf("Read %d bytes data from Server:%s\n",rv,buf);
break;
}
}
close(sockfd);
3.1.1.1 第一种方法结果图
客户端:
服务端:
3.1.2 第二种方法--用arg传递IP和端口
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#define MSG_STR "Hello zhangkang\n"//添加宏定义,方便write函数中strlen的使用
int main (int argc,char *argv[])
{
int sockfd=-1;
int rv=-1;
struct sockaddr_in servaddr;//ipv4 结构体的定义;
char *servip=NULL;
int port=0;
char buf[1024];//指定读区
if(argc<3)
{
printf("program usage: %s [ServerIP] [ServerPORT]\n",argv[0]);
return -1;
}
servip=argv[1];//第二个参数为目的ip;
port=atoi(argv[2]);//第三个参数为目的端口;
sockfd=socket(AF_INET,SOCK_STREAM,0);
if (socket<0)
{
printf("Creat socker faliure:%s\n",strerror(errno));
return -2;
}
printf("Creat socket[%d] successful!\n",sockfd);
memset(&servaddr,0,sizeof(servaddr));//将定义ipv4的结构体清空;
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(port);
inet_aton(servip,&servaddr.sin_addr);//写入ipv4结构体中的一些数据;
rv=connect(sockfd(struct sockaddr *)&servaddr,sizeof(servaddr));
if(rv<0)
{
printf("Connect to server[%s:%d] failure:%s\n",servip,port,strerror(errno));
return -3;
}
printf("Connect to server[%s:%d] successful!",servip,port);
while(1)
{
rv=write(sockfd,MSG_STR,strlen(MSG_STR));
if(rv<0)
{
printf("Write to server by sockfd[%d] failure:%s\n",sockfd,strerror(errno));
break;
}
memset(buf,0,sizeof(buf));//将buf清空;
rv=read(sockfd,buf,sizeof(buf));
if(rv<0)
{
printf("Read data from server by sockfd[%d] failure:%s\n",sockfd,strerror(errno));
break;
}
else if(rv==0)
{
printf("Socket[%d] get disconnected\n",sockfd);
break;
}
else if(rv>0)
{
printf("Read %d bytes data from Server:%s\n",rv,buf);
break;
}
}
close(sockfd);
}
3.1.2.1第二种方法结果图
客户端:
服务端:
3.1.3第三种方法--利用getopt_long的选项传递IP和端口
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <getopt.h>
#define MSG_STR "Hello i am zhangkang\n"//添加宏定义,方便write函数中strlen的使用
void print_useage(char *progname)
{
printf("%s print_useage:\n",progname)
printf("-i(--ipaddr):specify server IP address\n");
printf("-p(--port):specify server port\n");
printf("-h(--help):print this help information\n");
return;
}
int main (int argc,char *argv[])
{
int sockfd=-1;
int rv=-1;
struct sockaddr_in servaddr;//ipv4 结构体的定义;
char *servip=NULL;
int port=0;
char buf[1024];//指定读区
int opt;
int idx;
struct option opts[] = {
{"ipaddr",required_argument,NULL,'i'},
{"port",required_argument,NULL,'p'},
{"help",no_argument,NULL,'h'},
{NULL,0,NULL,0}
};
while((opt = getopt_long(argc,argv,"i:a:p:h",opts,&idx)) != -1)
{
switch(opt)
{
case 'i':
servip = optarg;
break;
case 'p':
port = atoi(optarg);
break;
case 'h':
print_useage(argv[0]);
return 0;
case 'a':
h_alias = optarg;
break;
}
}
sockfd=socket(AF_INET,SOCK_STREAM,0);
if (socket<0)
{
printf("Creat socker faliure:%s\n",strerror(errno));
return -1;
}
printf("Creat socket[%d] successful!\n",sockfd);
memset(&servaddr,0,sizeof(servaddr));//将定义ipv4的结构体清空;
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(port);
inet_aton(servip,&servaddr.sin_addr);//写入ipv4结构体中的一些数据;
rv=connect(sockfd(struct sockaddr *)&servaddr,sizeof(servaddr));
if(rv<0)
{
printf("Connect to server[%s:%d] failure:%s\n",servip,port,strerror(errno));
return -2;
}
printf("Connect to server[%s:%d] successful!",servip,port);
while(1)
{
rv=write(sockfd,MSG_STR,strlen(MSG_STR));
if(rv<0)
{
printf("Write to server by sockfd[%d] failure:%s\n",sockfd,strerror(errno));
break;
}
memset(buf,0,sizeof(buf));//将buf清空;
rv=read(sockfd,buf,sizeof(buf));
if(rv<0)
{
printf("Read data from server by sockfd[%d] failure:%s\n",sockfd,strerror(errno));
break;
}
else if(rv==0)
{
printf("Socket[%d] get disconnected\n",sockfd);
break;
}
else if(rv>0)
{
printf("Read %d bytes data from Server:%s\n",rv,buf);
break;
}
}
close(sockfd);
}
3.1.3.1 第三种方法结果图
客户端:
服务端: