Linux下的socket通信
最近再学多线程的一些知识,刚好项目需求需要一个多线程的服务器,就现学现卖搭了一个服务器(数据库搭载mysql , 贴出一个登录模块 ,写的不好手下留情,刚接触不久)
1、Linux搭载mysql,有很多大佬的博客都有详细教程,这里就不搬运了。
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <mysql/mysql.h>
#include <mysql/errmsg.h>
#include <mysql/mysqld_error.h>
#include "Function.h"
void* thread_routine(void* arg);
int clnt_num = 0;
int clnt_socks[CLNT_MAX];
pthread_mutex_t mutex;
pthread_t thread_id;
MYSQL mysql;
int main(int argc , char*argv[]){
int serv_sock , clnt_sock , option , optlen;
struct sockaddr_in serv_addr , clnt_addr;
socklen_t clnt_addr_sz;
MYSQL mysql;
if(argc != 2)
{
printf("Usage : %s IP Port", argv[0]);
exit(0);
}
pthread_mutex_init(&mutex , NULL);
serv_sock = socket(PF_INET , SOCK_STREAM , 0);
if(serv_sock == -1)
error_handler("socket() filed");
//** 地址再分配 **/
optlen = sizeof(option);
option = 1;
setsockopt(serv_sock , SOL_SOCKET , SO_REUSEADDR , (void*)&option , optlen);
memset(&serv_addr , 0 , sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock , (struct sockaddr*)&serv_addr , sizeof(serv_addr)) == -1)
error_handler("bind() error");
if(listen(serv_sock , 5) == -1)
error_handler("connect() error");
while(1){
clnt_addr_sz = sizeof(clnt_addr);
clnt_sock = accept(serv_sock , (struct sockaddr*)&clnt_addr , &clnt_addr_sz);
if(clnt_sock == -1)
continue;
pthread_mutex_lock(&mutex);
clnt_socks[clnt_num++] = clnt_sock;
pthread_mutex_unlock(&mutex);
pthread_create(&thread_id , NULL , thread_routine , (void*)&clnt_sock);
pthread_detach(thread_id);
printf("New client have connected and its IP is %s \n" , inet_ntoa(clnt_addr.sin_addr));
}
close(serv_sock);
return 0;
}
void* thread_routine(void *arg) {
int clnt_sock = *((int *)arg);
MYSQL_RES *my_res;
MYSQL_ROW my_row;
while((str_len = read(clnt_sock , message , sizeof(message))) != 0) {
printf("%d\n",str_len);
if(str_len != -1){
printf("message from client:%s\n" , message);
mysql_init(&mysql);
if(mysql_real_connect(&mysql , "localhost" , "root" , "12345678" , "test" , 0 , NULL , 0))
printf("Connected database success\n");
else{
printf("Connected database failed \t error_message:%s" , mysql_error(&mysql));
mysql_close(&mysql);
continue;
}
strcpy(msg_head, strtok(message, "#")); //执行此函数后原有字符串改变
if (strcmp(msg_head, "01") == 0) { //消息头部01代表注册
//如果账号已经注册,就不用在注册提示用户已注册
strcpy(msg_phone, strtok(NULL, "#"));
strcpy(msg_psd, strtok(NULL, "#"));
strcpy(msg_username, strtok(NULL, "#"));
printf("%s , %s , %s\n" , msg_phone , msg_psd , msg_username);
sprintf(sql, "select * from user where phone = %s", msg_phone);
if (mysql_query(&mysql, sql) == 0){
my_res = mysql_store_result(&mysql);
if (my_res != NULL){ //查询表中无注册记录 , 需注册账号
if(mysql_num_rows(my_res)){
printf("the user table have the register info: %lu\n" , (unsigned long)mysql_num_rows(my_res));
write(clnt_sock , messagePrint(HAVE_REGISTER_INFO) , strlen(messagePrint(HAVE_REGISTER_INFO)));
}else{
sprintf(sql, "insert into user(user_name , phone , password) values ('%s' ,'%s' ,'%s')", msg_username, msg_phone, msg_psd);
if (mysql_query(&mysql, sql) != 0){
fprintf(stderr, "mysql_query failed:\n\tcode:%d\n\treason:%s\n",mysql_errno(&mysql), mysql_error(&mysql));
write(clnt_sock , messagePrint(SQL_SENTENCE_ERROR) , strlen(messagePrint(SQL_SENTENCE_ERROR)));
}else{
if(mysql_affected_rows(&mysql) != 0){
printf("Insert success,affect row are %lu\n", mysql_affected_rows(&mysql));
write(clnt_sock , messagePrint(REGISTER_OK) , strlen(messagePrint(REGISTER_OK)));
}else{
printf("Insert failed,affect row are %lu\n", mysql_affected_rows(&mysql));
write(clnt_sock , messagePrint(REGISTER_ERROR) , strlen(messagePrint(REGISTER_ERROR)));
}
}
}
}else //已经有注册记录 , 返回注册提示信息
write(clnt_sock , messagePrint(MESSAGE_END) , strlen(messagePrint(MESSAGE_END)));
}else{
printf("error here\n");
fprintf(stderr, "mysql_query failed:\n\tcode:%d\n\treason:%s\n", mysql_errno(&mysql), mysql_error(&mysql));
write(clnt_sock , messagePrint(SQL_SENTENCE_ERROR) , strlen(messagePrint(SQL_SENTENCE_ERROR)));
}
}
}
else{
printf("The Client's data buffer have been clear\n");
}
}
pthread_mutex_lock(&mutex);
for(int i = 0; i < clnt_num ; i++){ //移除断开连接的客户端
if(clnt_socks[i] == clnt_sock){
while(i++ < clnt_num - 1)
clnt_socks[i] = clnt_socks[i+1];
printf("Disconnected Client's SocketFd: %d \n" , clnt_sock);
mysql_free_result(my_res);
mysql_close(&mysql);
break;
}
}
clnt_num--;
pthread_mutex_unlock(&mutex);
close(clnt_sock);
return NULL;
}
2、头文件写函数,初步使用,还在做 ,后期调整
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CLNT_MAX 50
#define MAX_SIZE 1024 * 1024
#define ENUM_RETURN_MESSAGE(x) if(msg == x) return #x;
typedef enum
{
SQL_SENTENCE_ERROR,
REGISTER_OK,
HAVE_REGISTER_INFO,
REGISTER_ERROR,
LOGIN_OK,
USER_NOT_EXISTED,
USER_OR_PASSWORD_ERROR,
BIKE_ID_NOT_EXISTED,
MESSAGE_END,
}RETURN_MESSAGE;
char* messagePrint(RETURN_MESSAGE msg)
{
ENUM_RETURN_MESSAGE(SQL_SENTENCE_ERROR);
ENUM_RETURN_MESSAGE(REGISTER_OK);
ENUM_RETURN_MESSAGE(HAVE_REGISTER_INFO);
ENUM_RETURN_MESSAGE(REGISTER_ERROR);
ENUM_RETURN_MESSAGE(LOGIN_OK);
ENUM_RETURN_MESSAGE(USER_NOT_EXISTED);
ENUM_RETURN_MESSAGE(USER_OR_PASSWORD_ERROR);
ENUM_RETURN_MESSAGE(BIKE_ID_NOT_EXISTED);
return "Unknown_Error";
}
char message[MAX_SIZE];
char msg_head[5]; //客户端消息头部
char msg_phone[12]; //客户端消息—手机号
char msg_psd[9]; //客户端消息--密码
char msg_username[15]; //客户端消息---用户名
char bike_id[10]; //客户端消息------车辆编号
int str_len = 0;
char sql[100];
void error_handler(char* msg)
{
fputs(msg , stderr);
fputs("\n" , stderr);
exit(1);
}