新增知识点
(1)fd_set的数据结构
select()机制中提供一fd_set的数据结构,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄(不管是socket句柄, 还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fe_set的内容,由此 来通知执行了select()的进程哪一socket或文件可读。(起到一个根据描述符通知要不要干某事情的作用)
fd_set set;
FD_ZERO(&set);/*将set清零使集合中不含任何fd*/
FD_SET(fd,&set); /*将fd加入set集合*/
FD_CLR(fd,&set); /*将fd从set集合中清除*/
FD_ISSET(fd,&set); /*测试fd是否在set集合中*/要是判断出其中有描述符则进行相应的处理
If(FD_ISSET(fd,&set))//进行判断,要是有这个事情,则执行if语句
select函数 系统提供了select函数实实现多路复用输入/输出模型。原型
#include <sys/time.h>
#include <unistd.h>
int select(int maxfd, fd_set *rdset, fd_set*wrset, fd_set *exset, struct timeval *timeout);
参数maxfd是需要监视的最大的文件描述符值+1;rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合。可写文件描述符的集合。及异常文件描述符的集合。struct timeval结构用于描述一段时间长度,如果在这个时间内, 需要监视的描述符没有事件发生则函数返回,返回值为0。
(2)键盘输入读取,数据清空
fgets(buf, MAXBUF,stdin);从输入中读取字符到buf
bzero(buf, MAXBUF);清空buf
memset(buf,0,sizeof(buf));清空buf
fflush(stdin);清空输入缓冲区
(3)运行服务器程序的时候出现端口被占用
步骤一:查看端口是否被占用 输入命令:
lsof–i : 《端口号》 要是改端口被那个程序占用或者绑定,这可以看到相关的信息,要是没有被使用,这没有任何信息。
键入 netstat –apu|grep 《端口号》也可以查看
步骤二: 步骤一可以知道,那些进程在使用这个端口,输入命令:ps –L,可以查看有那个进程在运行,找到相应的进程,ID
步骤三:将进程杀死。Kill -9 PID,步骤二中知道的进程。
test_server.c代码:
//
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#define PORT 3492
#define MAXBUF 1024
#define MAX_CONN_NM 5
#define Server_IP "127.0.0.1"
int main(void)
{
int sockfd, client_fd,sendbytes,len;
struct sockaddr_in server_addr,client_addr;
char buf[MAXBUF];
int sin_size, recvbytes;
fd_set rfds;
int retval, maxfd = -1;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
/*初始化服务器的地址结构*/
bzero(&server_addr, sizeof(server_addr));
/*使用internet协议族*/
server_addr.sin_family = AF_INET;
/*使用制定的端口*/
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(Server_IP);//INADDR_ANY;//
memset(server_addr.sin_zero,0,8);
int i = 1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i));
if (bind(sockfd, (struct sockaddr *) &server_addr, sizeof(struct sockaddr))
== -1) {
perror("bind");
exit(1);
}
printf("Blind success");
if (listen(sockfd, MAX_CONN_NM) == -1) {
perror("listen");
exit(1);
}
printf("Listening...................\n");
while(1)
{
if ((client_fd =accept(sockfd, (struct sockaddr *) &client_addr,&sin_size)) == -1)
{ //accept函数返回一个新的socket,这个socket(client_fd)用于同连接到的客户的通信
perror("accept");
exit(errno);
}
/* 开始处理每个新连接上的数据收发 */
printf (" Read to char\n");
/* 接收客户端的消息 */
while(1)
{
/* 把集合清空 */
FD_ZERO(&rfds);
/* 把标准输入句柄0加入到集合中 */
FD_SET(0, &rfds);
maxfd = 0;
/* 把当前连接句柄new_fd加入到集合中 */
FD_SET(client_fd, &rfds);
if (client_fd > maxfd)
maxfd = client_fd+1;
retval = select(maxfd, &rfds, (fd_set *)NULL, (fd_set *)NULL, (struct timeval*)NULL); //开始监听那些文件描述符出于可读状态
if (retval == -1)
{
printf("Chuxuancuowu%s", strerror(errno));
break;
}
else if (retval == 0)
{
continue;
}
else
{
if (FD_ISSET(0, &rfds))
{
memset(buf,0,sizeof(buf));
fgets(buf, MAXBUF, stdin);
if (!strncasecmp(buf, "quit", 4)) //在buf查找quit要是有一样的函数strncasecmp返回0;
{
printf("Qing qiu tui chu lianjie");
break;
}
sendbytes = send(client_fd, buf, strlen(buf) - 1, 0);
if (sendbytes < 0)
{
printf
("xiaoxi'%s'fasongshibai..cuowudaimashi%d,cuowuxiaoshishi'%s' ",
buf, errno, strerror(errno));
}
else
{
printf("Send Mecessage:%s ",buf);
}
}
/* 当前连接的socket上有消息(客户端)到来则接收对方发过来的消息并显示 */
if(FD_ISSET(client_fd, &rfds))
{
bzero(buf, MAXBUF );
len = recv(client_fd, buf, MAXBUF, 0);
if(len > 0)
{
printf("Receive message:%s\n", buf);
}
else
{
perror("Receive failed!!");
break;
}
}
}
}
close(client_fd);
close(sockfd);
return 0;
}
}
//
teat_client.c代码
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <resolv.h>
#include <netdb.h>
#define PORT 3492
#define MAXBUF 1024
#define MAX_CONN_NM 5
#define Server_IP "127.0.0.1"
int main(int argc, char **argv)
{
int sockfd, sendbytes,len;
struct hostent *host;
char buf[MAXBUF];
struct sockaddr_in serv_addr;
struct sockaddr_in dest;
fd_set rfds;
int retval, maxfd = -1;
if (argc < 1) {
printf
("Canshucuowu",
argv[0], argv[0]);
exit(0);
}
// if((host = gethostbyname(argv[1]) )== NULL)
// {
// perror("gethostbyname");
// exit(1);
// }
memset(buf,0,sizeof(buf));
//fgets(buf, MAXBUF, stdin); //sprintf(buf,"%s",argv[1]);
/* 创建一个 socket 用于 tcp 通信 */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket");
exit(1);
}
/* 初始化服务器端(对方)的地址和端口信息 */
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
serv_addr.sin_addr.s_addr = inet_addr(Server_IP); // serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
memset(serv_addr.sin_zero,0,8);
/* 连接服务器 */
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) != 0) {
perror("Connect ");
exit(1);
}
while(1)
{
/* 把集合清空 */
FD_ZERO(&rfds);
/* 把标准输入句柄0加入到集合中 */
FD_SET(0, &rfds);
maxfd = 0;
/* 把当前连接句柄sockfd加入到集合中 */
FD_SET(sockfd, &rfds);
if (sockfd > maxfd)
maxfd = sockfd+1;
retval = select(maxfd, &rfds, (fd_set *)NULL, (fd_set *)NULL, (struct timeval*)NULL); //开始监听那些文件描述符出于可读状态
if (retval == -1)
{
printf("Chuxuancuowu%s", strerror(errno));
break;
} else if (retval == 0)
{
continue;
}
else
{
if(FD_ISSET(sockfd, &rfds))
{
/* 连接的socket上有消息到来则接收对方发过来的消息并显示 */
memset(buf,0,sizeof(buf));
len = recv(sockfd, buf, MAXBUF, 0);
if(len > 0)
{
printf("Receive message: %s\n", buf);
}
else
{
perror("Receive failed!!");
break;
}
}
if(FD_ISSET(0, &rfds))
{
/* 用户按键了,则读取用户输入的内容发送出去 */
memset(buf,0,sizeof(buf));
fgets(buf, MAXBUF, stdin);
if (!strncasecmp(buf, "quit", 4)) //在buf查找quit要是有一样的函数strncasecmp返回0;
{
printf("Qing qiu tui chu lianjie");
break;
}
sendbytes = send(sockfd, buf, strlen(buf) - 1, 0);
if (sendbytes < 0)
{
printf
("xiaoxi'%s'fasongshibai..cuowudaimashi%d,cuowuxiaoshishi'%s' ",
buf, errno, strerror(errno));
} else
{
printf("Send Mecessage:%s ",buf);
}
}
}
}
/* 关闭连接 */
close(sockfd);
return 0;
}
在程序中已经制定了连接的iP地址,127.0.0.1,任何计算机可以通过该ip与本机进行连接。要修改IP只要在宏定义上修改即可。
编译上面两个文件,运行及可。
首先要运行服务器程序,然后运行客户端程序。接下来就可以随便发送了,键盘输入“quit”结束通话!