tcp服务端:my_server.c

 #include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include "my_recv.h"  //自定义的头文件
 
#define SERV_PORT 4507 //服务器的端口
#define LISTENQ 12 //连接请求的队列的最大长度
#define INVALID_USERINFO 'n' //用户信息无效
#define VALID_USERINFO 'y'  //用户信息有效
#define USERNAME 0  //接收的是用户名
#define PASSWORD 1  //接收到的是空码
struct userinfo{  //保存用户名和空码的结构体
        char username[32];
        char password[32];
};
struct userinfo users[]={
        {"guangwen", "unix"},
        {"lusiyuang", "4508"},
        {"panlinbing", "clh"},
        {"kairong", "3"},
        {" "," "} //以只舍一个空格的字符串作为数组的结束标志
};
//查找用户名是否存在,存在返回该用户名的下标,不存在则返回-1,出错返回-2
int find_name(const char *name)
{
        int i;
        if(name==NULL){
          printf("in find_name,NULL pointer");
          return -2;
        }
        for(i=0;users[i].username[0] !=' ';i++){
          if(strcmp(users[i].username,name)==0){
                return i;
          }
        }
        return -1;
}
//发送数据
void send_data(int conn_fd,const char *string)
{
 if(send(conn_fd,string,strlen(string),0)<0)
         my_err("send",__LINE__);
}
int main()
{
        int sock_fd,conn_fd;
        int optval;
        int flag_recv=USERNAME;//标识接收到的用户名还是空码
        int ret;
        int name_num;
        pid_t pid;
        socklen_t cli_len;
        struct sockaddr_in cli_addr,serv_addr;
        char recv_buf[128];
//创建一个TCP套接字
        sock_fd=socket(AF_INET,SOCK_STREAM,0);
        if(sock_fd<0){
          my_err("socket",__LINE__);
        }
//设置该套接字使之可以重新绑定端口
        optval=1;
        if(setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,(void *)&optval,sizeof(int))<0)
        my_err("setsockopt",__LINE__);
//初始化服务器端地址结构
        memset(&serv_addr,0,sizeof(struct sockaddr_in));
        serv_addr.sin_family=AF_INET;
        serv_addr.sin_port=htons(SERV_PORT);
        serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//将套接字绑定到本地端口
        if(bind(sock_fd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr_in))<0)
          my_err("bind",__LINE__);
//将套接字转化为监听套接字,最大为LISTENQ
        if(listen(sock_fd,LISTENQ)<0)
          my_err("listen",__LINE__);
        cli_len=sizeof(struct sockaddr_in);
        while(1)
        {
        //通过accept接收客户端的连接请求,并返回连接套接字用于收发数据
  conn_fd=accept(sock_fd,(struct sockaddr *)&cli_addr,&cli_len);
                if(conn_fd<0)
                        my_err("accept",__LINE__);
                printf("accept a new clinet ,ip=%s:\n",inet_ntoa(cli_addr.sin_addr));
        //创建一个子进程处理刚刚接收的连接请求
#include<>
        if((pid=fork())==0){
                while(1){
                  if((ret=recv(conn_fd,recv_buf,sizeof(recv_buf),0))<0){
                        perror("recv");
                        exit(1);
                  }
                  recv_buf[ret-1] = '\0';//将数据结束标志‘\n’替换成字符串结束标>志
                  if(flag_recv==USERNAME){  //接收到的是用户名
                        name_num=find_name(recv_buf);
                        switch(name_num){
                                case -1:
                                        send_data(conn_fd,"n\n");
                                        break;
                                case -2:
                                        exit(1);
                                        break;
                                default:
                                        send_data(conn_fd,"y\n");
                                        flag_recv=PASSWORD;
                                        break;
                        }
                  }
                  else if(flag_recv==PASSWORD) { //接收到的是密码
                        if(strcmp(users[name_num].password,recv_buf)==0){
                                send_data(conn_fd,"y\n");
                                send_data(conn_fd,"welcome login my tcp server\n");
                                printf("%s login\n",users[name_num].username);
                                break;//跳出while循环
                    }else
                         send_data(conn_fd,"n\n");
                  }
                }
                close(sock_fd);
                close(conn_fd);
                exit(0);//结束子进程
        } else { //父进程关闭刚刚接收到的连接请求,执行accept等待其他连接请求
                        close(conn_fd);
                }
        }
        return 0;
}
TCP客户端:my_clience
                             
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include "my_recv.h"
#define INVALID_USERINFO 'n'  //用户信息无效
#define VALID_USERINFO 'y' //用户信息有效
//获取用户输入存入到buf,buf的长度为len,用户输入数据以'\n'为结束标志
int get_userinfo(char *buf,int len)
{
        int i;
        int c;
        if(buf==NULL)
        {
                return -1;
        }
        i=0;
        while(((c=getchar())!='\n')&&(c!=EOF)&&(i<len-2))
        {
                buf[i++]=c;
        }
        buf[i++]='\n';
        buf[i++]='\0';
        return 0;
}
//输入用户名,然后通过fd发送出去
void input_userinfo(int conn_fd,const char *string)
{
        char input_buf[32];
        char recv_buf[BUFSIZE];
        int flag_userinfo;
//输入用户信息直到正确为止
        do{
                printf("%s:",string);
                if(get_userinfo(input_buf,32)<0){
                        printf("error return from get_userinfo\n");
                        exit(1);
                }
                if(send(conn_fd,input_buf,strlen(input_buf),0)<0){
         my_err("send",__LINE__);
                }
        //从连接套接字上读取一次数据
                if(my_recv(conn_fd,recv_buf,sizeof(recv_buf))<0){
                        printf("data is too long\n");
                        exit(1);
                }
                if(recv_buf[0]==VALID_USERINFO){
                        flag_userinfo=VALID_USERINFO;
                } else {
                        printf("%s error,input again,",string);
                        flag_userinfo=INVALID_USERINFO;
                        }
        } while(flag_userinfo==INVALID_USERINFO);
}
int main(int argc,char **argv)
{
        int i;
        int ret;
        int conn_fd;
        int serv_port;
        struct sockaddr_in serv_addr;
        char recv_buf[BUFSIZE];
        //检查参数个数
        if(argc!=5){
                printf("Usage: [-p] [serv_port] [-a] [serv_address]\n");
                exit(1);
        }
//初始化服务器端地址结构
        memset(&serv_addr,0,sizeof(struct sockaddr_in));
        serv_addr.sin_family=AF_INET;
        //从命令行获取服务器端的端口与地址
        for(i=0;i<argc;i++)
        {
                if(strcmp("-p",argv[i])==0){
 serv_port=atoi(argv[i+1]);
                        if(serv_port<0||serv_port>65535){
                                printf("invalid serv_addr.sin_port\n");
                                exit(1);
                        } else {
                                        serv_addr.sin_port=htons(serv_port);
                                }
                        continue;
                }
        if(strcmp("-a",argv[i])==0){
                if(inet_aton(argv[i+1],&serv_addr.sin_addr)==0){
                        printf("invalid server ip address\n");
                        exit(1);
                }
                continue;
        }
        }
        //检测是否少输入了某一项参数
        if(serv_addr.sin_port==0||serv_addr.sin_addr.s_addr==0){
                printf("Usage:[-p] [serv_addr.sin_port] [-a] [serv_address]\n");
                exit(1);
        }
        //创建一个套接字
        conn_fd=socket(AF_INET,SOCK_STREAM,0);
        if(conn_fd<0)
          my_err("socket",__LINE__);
        //向服务器端发送连接请求
        if(connect(conn_fd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr))<0)
          my_err("connect",__LINE__);
        //输入用户名和密码
        input_userinfo(conn_fd,"username");
        input_userinfo(conn_fd,"password");
        //读取欢迎信息并打印出来
        if((ret=my_recv(conn_fd,recv_buf,sizeof(recv_buf)))<0){
                printf("data is too long\n");
                exit(1);
        }
        for(i=0;i<ret;i++){
                printf("%c",recv_buf[i]);
        }
        printf("\n");
close(conn_fd);
        return 0;
}