- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在编写一个 pthread 服务器,它接收来自客户端的请求并将它们发回一堆 .ppm 文件。一切似乎都很顺利,但有时当我只连接了 1 个客户端时,当我试图从文件描述符(对于文件)中读取时,它说文件描述符错误。这没有意义,因为我的 int fd 不是 -1,而且该文件肯定存在。其他时候,我会收到“非套接字上的套接字操作”错误。这很奇怪,因为其他时候,它不会给我这个错误并且一切正常。
当尝试连接多个客户端时,出于某种原因,它只会正确发送给一个客户端,然后另一个客户端收到错误的文件描述符或“非套接字”错误,即使两个线程都在处理相同的消息并执行相同的套路。任何人都知道为什么?这是给我该错误的代码:
while(mqueue.head != mqueue.tail && count < dis_m){
printf("Sending to client %s: %s\n", pointer->id, pointer->message);
int fd;
fd = open(pointer->message, O_RDONLY);
char buf[58368];
int bytesRead;
printf("This is fd %d\n", fd);
bytesRead=read(fd,buf,58368);
send(pointer->socket,buf,bytesRead,0);
perror("Error:\n");
fflush(stdout);
close(fd);
mqueue.mcount--;
mqueue.head = mqueue.head->next;
free(pointer->message);
free(pointer);
pointer = mqueue.head;
count++;
}
printf("Sending %s\n", pointer->message);
int fd;
fd = open(pointer->message, O_RDONLY);
printf("This is fd %d\n", fd);
printf("I am hhere2\n");
char buf[58368];
int bytesRead;
bytesRead=read(fd,buf,58368);
send(pointer->socket,buf,bytesRead,0);
perror("Error:\n");
close(fd);
mqueue.mcount--;
if(mqueue.head != mqueue.tail){
mqueue.head = mqueue.head->next;
}
else{
mqueue.head->next = malloc(sizeof(struct message));
mqueue.head = mqueue.head->next;
mqueue.head->next = malloc(sizeof(struct message));
mqueue.tail = mqueue.head->next;
mqueue.head->message = NULL;
}
free(pointer->message);
free(pointer);
pthread_mutex_unlock(&numm);
pthread_mutex_unlock(&circ);
pthread_mutex_unlock(&slots);
两个线程的消息是相同的,格式为 ./path/imageXX.ppm,其中 XX 是应该发送给客户端的编号。每个图像的文件大小为 58368 字节。
有时,此代码会在读取时挂起并停止执行。我也不知道这会是什么,因为文件描述符返回时是有效的。
提前致谢。
编辑:
这是一些示例输出:
Sending to client a: ./support/images/sw90.ppm
This is fd 4
Error:
: Socket operation on non-socket
Sending to client a: ./support/images/sw91.ppm
This is fd 4
Error:
: Socket operation on non-socket
Sending ./support/images/sw92.ppm
This is fd 4
I am hhere2
Error:
: Socket operation on non-socket
My dispatcher has defeated evil
具有 2 个客户端的示例(首先为客户端 b 提供服务) 发送给客户端 b:./support/images/sw87.ppm 这是fd 6 错误: : 成功 发送给客户端 b:./support/images/sw88.ppm 这是fd 6 错误: : 成功 发送给客户端 b:./support/images/sw89.ppm 这是fd 6 错误: : 成功
This is fd 6
Error:
: Bad file descriptor
Sending to client a: ./support/images/sw85.ppm
This is fd 6
Error:
如您所见,在这种情况下,谁先得到服务就可以打开文件,但第二个人不能。
编辑2:
完整代码。抱歉,它很长而且格式很糟糕。
#include <netinet/in.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "ring.h"
/*
Version 1
Here is what is implemented so far:
The threads are created from the arguments specified (number of threads that is)
The server will lock and update variables based on how many clients are in the system and such.
The socket that is opened when a new client connects, must be passed to the threads. To do this, we need some sort of global array. I did
this by specifying an int client and main_pool_busy, and two pointers poolsockets and nonpoolsockets.
My thinking on this was that when a new client enters the system, the server thread increments the variable client. When a thread is finished with this client (after it sends it the data), the thread will decrement client and close the socket. HTTP servers act this way sometimes (they terminate the socket as soon as one transmission is sent). *Note down at bottom
After the server portion increments the client counter, we must open up a new socket (denoted by new_sd) and get this value to the appropriate thread.
To do this, I created global array poolsockets, which will hold all the socket descriptors for our pooled threads. The server portion gets the new socket descriptor, and places the value in the first spot of the array that has a 0. We only place a value in this array IF:
1. The variable main_pool_busy < worknum (If we have more clients in the system than in our pool, it doesn't mean we should always create a new thread.
At the end of this, the server signals on the condition variable clientin that a new client has arrived.
In our pooled thread, we then must walk this array and check the array until we hit our first non-zero value. This is the socket we will give to that thread. The thread then changes the array to have a zero here.
What if our all threads in our pool our busy?
If this is the case, then we will know it because our threads in this pool will increment main_pool_busy by one when they are working on a request and decrement it when they are done. If main_pool_busy >= worknum, then we must dynamically create a new thread. Then, we must realloc the size of our
nonpoolsockets array by 1 int. We then add the new socket descriptor to our pool.
Here's what we need to figure out:
NOTE* Each worker should generate 100 messages which specify the worker thread ID, client socket descriptor and a copy of the client message. Additionally, each message should include a message number, starting from 0 and incrementing for each subsequent message sent to the same client.
I don't know how to keep track of how many messages were to the same client. Maybe we shouldn't close the socket descriptor, but rather keep an array of structs for each socket that includes how many messages they have been sent. Then, the server adds the struct, the threads remove it, then the threads add it back once they've serviced one request (unless the count is 100).
-------------------------------------------------------------
CHANGES
Version 1
----------
NONE: this is the first version.
*/
#define MAXSLOTS 30
#define dis_m 15 //problems with dis_m ==1
//Function prototypes
void inc_clients();
void init_mutex_stuff(pthread_t*, pthread_t*);
void *threadpool(void *);
void server(int);
void add_to_socket_pool(int);
void inc_busy();
void dec_busy();
void *dispatcher();
void create_message(long, int, int, char *, char *);
void init_ring();
void add_to_ring(char *, char *, int, int, int);
int socket_from_string(char *);
void add_to_head(char *);
void add_to_tail(char *);
struct message * reorder(struct message *, struct message *, int);
int get_threadid(char *);
void delete_socket_messages(int);
struct message * merge(struct message *, struct message *, int);
int get_request(char *, char *, char*);
/////////////////////
//Global mutexes and condition variables
pthread_mutex_t startservice;
pthread_mutex_t numclients;
pthread_mutex_t pool_sockets;
pthread_mutex_t nonpool_sockets;
pthread_mutex_t m_pool_busy;
pthread_mutex_t slots;
pthread_mutex_t numm;
pthread_mutex_t circ;
pthread_cond_t clientin;
pthread_cond_t m;
///////////////////////////////////////
//Global variables
int clients;
int main_pool_busy;
int * poolsockets, nonpoolsockets;
int worknum;
struct ring mqueue;
///////////////////////////////////////
int main(int argc, char ** argv){
//error handling if not enough arguments to program
if(argc != 3){
printf("Not enough arguments to server: ./server portnum NumThreadsinPool\n");
_exit(-1);
}
//Convert arguments from strings to integer values
int port = atoi(argv[1]);
worknum = atoi(argv[2]);
//Start server portion
server(port);
}
///////////////////////////////////////////////////////////////////////////////////////////////
//The listen server thread/////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
void server(int port){
int sd, new_sd;
struct sockaddr_in name, cli_name;
int sock_opt_val = 1;
int cli_len;
pthread_t threads[worknum]; //create our pthread id array
pthread_t dis[1]; //create our dispatcher array (necessary to create thread)
init_mutex_stuff(threads, dis); //initialize mutexes and stuff
//Server setup ///////////////////////////////////////////////////////
if ((sd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
perror("(servConn): socket() error");
_exit (-1);
}
if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char *) &sock_opt_val,
sizeof(sock_opt_val)) < 0) {
perror ("(servConn): Failed to set SO_REUSEADDR on INET socket");
_exit (-1);
}
name.sin_family = AF_INET;
name.sin_port = htons (port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind (sd, (struct sockaddr *)&name, sizeof(name)) < 0) {
perror ("(servConn): bind() error");
_exit (-1);
}
listen (sd, 5);
//End of server Setup //////////////////////////////////////////////////
for (;;) {
cli_len = sizeof (cli_name);
new_sd = accept (sd, (struct sockaddr *) &cli_name, &cli_len);
printf ("Assigning new socket descriptor: %d\n", new_sd);
inc_clients(); //New client has come in, increment clients
add_to_socket_pool(new_sd); //Add client to the pool of sockets
if (new_sd < 0) {
perror ("(servConn): accept() error");
_exit (-1);
}
}
pthread_exit(NULL); //Quit
}
//Adds the new socket to the array designated for pthreads in the pool
void add_to_socket_pool(int socket){
pthread_mutex_lock(&m_pool_busy); //Lock so that we can check main_pool_busy
int i;
//If not all our main pool is busy, then allocate to one of them
if(main_pool_busy < worknum){
pthread_mutex_unlock(&m_pool_busy); //unlock busy, we no longer need to hold it
pthread_mutex_lock(&pool_sockets); //Lock the socket pool array so that we can edit it without worry
for(i = 0; i < worknum; i++){ //Find a poolsocket that is -1; then we should put the real socket there. This value will be changed back to -1 when the thread grabs the sockfd
if(poolsockets[i] == -1){
poolsockets[i] = socket;
pthread_mutex_unlock(&pool_sockets); //unlock our pool array, we don't need it anymore
inc_busy(); //Incrememnt busy (locks the mutex itself)
pthread_cond_signal(&clientin); //Signal first thread waiting on a client that a client needs to be serviced
break;
}
}
}
else{ //Dynamic thread creation goes here
pthread_mutex_unlock(&m_pool_busy);
}
}
//Increments the client number. If client number goes over worknum, we must dynamically create new pthreads
void inc_clients(){
pthread_mutex_lock(&numclients);
clients++;
pthread_mutex_unlock(&numclients);
}
//Increments busy
void inc_busy(){
pthread_mutex_lock(&m_pool_busy);
main_pool_busy++;
pthread_mutex_unlock(&m_pool_busy);
}
//Initialize all of our mutexes at the beginning and create our pthreads
void init_mutex_stuff(pthread_t * threads, pthread_t * dis){
pthread_mutex_init(&startservice, NULL);
pthread_mutex_init(&numclients, NULL);
pthread_mutex_init(&pool_sockets, NULL);
pthread_mutex_init(&nonpool_sockets, NULL);
pthread_mutex_init(&m_pool_busy, NULL);
pthread_mutex_init(&circ, NULL);
pthread_cond_init (&clientin, NULL);
main_pool_busy = 0;
poolsockets = malloc(sizeof(int)*worknum);
int threadreturn; //error checking variables
long i = 0; //Loop and create pthreads
for(i; i < worknum; i++){
threadreturn = pthread_create(&threads[i], NULL, threadpool, (void *) i);
poolsockets[i] = -1;
if(threadreturn){
perror("Thread pool created unsuccessfully");
_exit(-1);
}
}
pthread_create(&dis[0], NULL, dispatcher, NULL);
}
//////////////////////////////////////////////////////////////////////////////////////////
/////////Main pool routines
/////////////////////////////////////////////////////////////////////////////////////////
void dec_busy(){
pthread_mutex_lock(&m_pool_busy);
main_pool_busy--;
pthread_mutex_unlock(&m_pool_busy);
}
void dec_clients(){
pthread_mutex_lock(&numclients);
clients--;
pthread_mutex_unlock(&numclients);
}
//This is what our threadpool pthreads will be running.
void *threadpool(void * threadid){
long id = (long) threadid; //Id of this thread
int i;
int socket;
int counter = 0;
//Try and gain access to the next client that comes in and wait until server signals that a client as arrived
while(1){
pthread_mutex_lock(&startservice); //lock start service (required for cond wait)
pthread_cond_wait(&clientin, &startservice); //wait for signal from server that client exists
pthread_mutex_unlock(&startservice); //unlock mutex.
pthread_mutex_lock(&pool_sockets); //Lock the pool socket so we can get the socket fd unhindered/interrupted
for(i = 0; i < worknum; i++){
if(poolsockets[i] != -1){
socket = poolsockets[i];
poolsockets[i] = -1;
pthread_mutex_unlock(&pool_sockets);
}
}
printf("Thread #%d is past getting the socket\n", id);
int incoming = 1;
while(counter < 100 && incoming != 0){
char buffer[512];
bzero(buffer,512);
int startcounter = 0;
incoming = read(socket, buffer, 512);
if(buffer[0] != 0){
//client ID:priority:request:arguments
char id[100];
long prior;
char request[100];
char arg1[100];
char message[100];
char arg2[100];
char * point;
point = strtok(buffer, ":");
strcpy(id, point);
point = strtok(NULL, ":");
prior = atoi(point);
point = strtok(NULL, ":");
strcpy(request, point);
point = strtok(NULL, ":");
strcpy(arg1, point);
point = strtok(NULL, ":");
if(point != NULL){
strcpy(arg2, point);
}
int fd;
if(strcmp(request, "start_movie") == 0){
int count = 1;
while(count <= 100){
char temp[10];
snprintf(temp, 50, "%d\0", count);
strcpy(message, "./support/images/");
strcat(message, arg1);
strcat(message, temp);
strcat(message, ".ppm");
printf("This is message %s to %s\n", message, id);
count++;
add_to_ring(message, id, prior, counter, socket); //Adds our created message to the ring
counter++;
}
printf("I'm out of the loop\n");
}
else if(strcmp(request, "seek_movie") == 0){
int count = atoi(arg2);
while(count <= 100){
char temp[10];
snprintf(temp, 10, "%d\0", count);
strcpy(message, "./support/images/");
strcat(message, arg1);
strcat(message, temp);
strcat(message, ".ppm");
printf("This is message %s\n", message);
count++;
}
}
//create_message(id, socket, counter, buffer, message); //Creates our message from the input from the client. Stores it in buffer
}
else{
delete_socket_messages(socket);
break;
}
}
counter = 0;
close(socket);//Zero out counter again
}
dec_clients(); //client serviced, decrement clients
dec_busy(); //thread finished, decrement busy
}
//Creates a message
void create_message(long threadid, int socket, int counter, char * buffer, char * message){
snprintf(message, strlen(buffer)+15, "%d:%d:%d:%s", threadid, socket, counter, buffer);
}
//Gets the socket from the message string (maybe I should just pass in the socket to another method)
int socket_from_string(char * message){
char * substr1 = strstr(message, ":");
char * substr2 = substr1;
substr2++;
int occurance = strcspn(substr2, ":");
char sock[10];
strncpy(sock, substr2, occurance);
return atoi(sock);
}
//Adds message to our ring buffer's head
void add_to_head(char * message){
printf("Adding to head of ring\n");
mqueue.head->message = malloc(strlen(message)+1); //Allocate space for message
strcpy(mqueue.head->message, message); //copy bytes into allocated space
}
//Adds our message to our ring buffer's tail
void add_to_tail(char * message){
printf("Adding to tail of ring\n");
mqueue.tail->message = malloc(strlen(message)+1); //allocate space for message
strcpy(mqueue.tail->message, message); //copy bytes into allocated space
mqueue.tail->next = malloc(sizeof(struct message)); //allocate space for the next message struct
}
//Adds a message to our ring
void add_to_ring(char * message, char * id, int prior, int mnum, int socket){
//printf("This is message %s:" , message);
pthread_mutex_lock(&circ); //Lock the ring buffer
pthread_mutex_lock(&numm); //Lock the message count (will need this to make sure we can't fill the buffer over the max slots)
if(mqueue.head->message == NULL){
add_to_head(message); //Adds it to head
mqueue.head->socket = socket; //Set message socket
mqueue.head->priority = prior; //Set its priority (thread id)
mqueue.head->mnum = mnum; //Set its message number (used for sorting)
mqueue.head->id = malloc(sizeof(id));
strcpy(mqueue.head->id, id);
}
else if(mqueue.tail->message == NULL){ //This is the problem for dis_m 1 I'm pretty sure
add_to_tail(message);
mqueue.tail->socket = socket;
mqueue.tail->priority = prior;
mqueue.tail->mnum = mnum;
mqueue.tail->id = malloc(sizeof(id));
strcpy(mqueue.tail->id, id);
}
else{
mqueue.tail->next = malloc(sizeof(struct message));
mqueue.tail = mqueue.tail->next;
add_to_tail(message);
mqueue.tail->socket = socket;
mqueue.tail->priority = prior;
mqueue.tail->mnum = mnum;
mqueue.tail->id = malloc(sizeof(id));
strcpy(mqueue.tail->id, id);
}
mqueue.mcount++;
pthread_mutex_unlock(&circ);
if(mqueue.mcount >= dis_m){
pthread_mutex_unlock(&numm);
pthread_cond_signal(&m);
}
else{
pthread_mutex_unlock(&numm);
}
printf("out of add to ring\n");
fflush(stdout);
}
//////////////////////////////////
//Dispatcher routines
/////////////////////////////////
void *dispatcher(){
init_ring();
while(1){
pthread_mutex_lock(&slots);
pthread_cond_wait(&m, &slots);
pthread_mutex_lock(&numm);
pthread_mutex_lock(&circ);
printf("Dispatcher to the rescue!\n");
mqueue.head = reorder(mqueue.head, mqueue.tail, mqueue.mcount);
//printf("This is the head %s\n", mqueue.head->message);
//printf("This is the tail %s\n", mqueue.head->message);
fflush(stdout);
struct message * pointer = mqueue.head;
int count = 0;
while(mqueue.head != mqueue.tail && count < dis_m){
printf("Sending to client %s: %s\n", pointer->id, pointer->message);
int fd;
fd = open(pointer->message, O_RDONLY);
char buf[58368];
int bytesRead;
printf("This is fd %d\n", fd);
bytesRead=read(fd,buf,58368);
send(pointer->socket,buf,bytesRead,0);
perror("Error:\n");
fflush(stdout);
close(fd);
mqueue.mcount--;
mqueue.head = mqueue.head->next;
free(pointer->message);
free(pointer);
pointer = mqueue.head;
count++;
}
printf("Sending %s\n", pointer->message);
int fd;
fd = open(pointer->message, O_RDONLY);
printf("This is fd %d\n", fd);
printf("I am hhere2\n");
char buf[58368];
int bytesRead;
bytesRead=read(fd,buf,58368);
send(pointer->socket,buf,bytesRead,0);
perror("Error:\n");
close(fd);
mqueue.mcount--;
if(mqueue.head != mqueue.tail){
mqueue.head = mqueue.head->next;
}
else{
mqueue.head->next = malloc(sizeof(struct message));
mqueue.head = mqueue.head->next;
mqueue.head->next = malloc(sizeof(struct message));
mqueue.tail = mqueue.head->next;
mqueue.head->message = NULL;
}
free(pointer->message);
free(pointer);
pthread_mutex_unlock(&numm);
pthread_mutex_unlock(&circ);
pthread_mutex_unlock(&slots);
printf("My dispatcher has defeated evil\n");
}
}
void init_ring(){
mqueue.head = malloc(sizeof(struct message));
mqueue.head->next = malloc(sizeof(struct message));
mqueue.tail = mqueue.head->next;
mqueue.mcount = 0;
}
struct message * reorder(struct message * begin, struct message * end, int num){
//printf("I am reordering for size %d\n", num);
fflush(stdout);
int i;
if(num == 1){
//printf("Begin: %s\n", begin->message);
begin->next = NULL;
return begin;
}
else{
struct message * left = begin;
struct message * right;
int middle = num/2;
for(i = 1; i < middle; i++){
left = left->next;
}
right = left -> next;
left -> next = NULL;
//printf("Begin: %s\nLeft: %s\nright: %s\nend:%s\n", begin->message, left->message, right->message, end->message);
left = reorder(begin, left, middle);
if(num%2 != 0){
right = reorder(right, end, middle+1);
}
else{
right = reorder(right, end, middle);
}
return merge(left, right, num);
}
}
struct message * merge(struct message * left, struct message * right, int num){
//printf("I am merginging! left: %s %d, right: %s %dnum: %d\n", left->message,left->priority, right->message, right->priority, num);
struct message * start, * point;
int lenL= 0;
int lenR = 0;
int flagL = 0;
int flagR = 0;
int count = 0;
int middle1 = num/2;
int middle2;
if(num%2 != 0){
middle2 = middle1+1;
}
else{
middle2 = middle1;
}
while(lenL < middle1 && lenR < middle2){
count++;
//printf("In here for count %d\n", count);
if(lenL == 0 && lenR == 0){
if(left->priority < right->priority){
start = left; //Set the start point
point = left; //set our enum;
left = left->next; //move the left pointer
point->next = NULL; //Set the next node to NULL
lenL++;
}
else if(left->priority > right->priority){
start = right;
point = right;
right = right->next;
point->next = NULL;
lenR++;
}
else{
if(left->mnum < right->mnum){
////printf("This is where we are\n");
start = left; //Set the start point
point = left; //set our enum;
left = left->next; //move the left pointer
point->next = NULL; //Set the next node to NULL
lenL++;
}
else{
start = right;
point = right;
right = right->next;
point->next = NULL;
lenR++;
}
}
}
else{
if(left->priority < right->priority){
point->next = left;
left = left->next; //move the left pointer
point = point->next;
point->next = NULL; //Set the next node to NULL
lenL++;
}
else if(left->priority > right->priority){
point->next = right;
right = right->next;
point = point->next;
point->next = NULL;
lenR++;
}
else{
if(left->mnum < right->mnum){
point->next = left; //set our enum;
left = left->next;
point = point->next;//move the left pointer
point->next = NULL; //Set the next node to NULL
lenL++;
}
else{
point->next = right;
right = right->next;
point = point->next;
point->next = NULL;
lenR++;
}
}
}
if(lenL == middle1){
flagL = 1;
break;
}
if(lenR == middle2){
flagR = 1;
break;
}
}
if(flagL == 1){
point->next = right;
point = point->next;
for(lenR; lenR< middle2-1; lenR++){
point = point->next;
}
point->next = NULL;
mqueue.tail = point;
}
else{
point->next = left;
point = point->next;
for(lenL; lenL< middle1-1; lenL++){
point = point->next;
}
point->next = NULL;
mqueue.tail = point;
}
//printf("This is the start %s\n", start->message);
//printf("This is mqueue.tail %s\n", mqueue.tail->message);
return start;
}
void delete_socket_messages(int a){
}
最佳答案
不要调用 perror()
除非您刚刚收到错误指示。例如:
int fd;
fd = open(pointer->message, O_RDONLY);
你应该检查fd
在你使用它之前;打印它只是一个临时替代品。
char buf[58368];
int bytesRead;
printf("This is fd %d\n", fd);
bytesRead=read(fd,buf,58368);
至少您已经从 read()
中获取了值, 但您应该在使用前检查它。
send(pointer->socket,buf,bytesRead,0);
您需要检查是否send()
成功与否。您可能还想打印 pointer->socket
的值,只是为了看看它的值(value)是多少。它应该是一个文件描述符(小的正整数)。
perror("Error:\n");
你不想调用perror()
除非你知道 send()
失败的。 errno
系统库永远不会将变量设置为 0;它可以包含任何值。如果必须调用它,请设置 errno = 0;
在你打电话之前 send()
.
但是,您可能会在 errno
中遇到错误即使没有出错。例如,在 Solaris 上,您可以获得 ENOTTY
在errno
成功写入文件后(因为文件不是 tty
,如果文件描述符引用 tty
的操作没有成功,但整个操作成功)。
您可能还会注意到:
//Function prototypes
void inc_clients();
不是函数原型(prototype);它只是一个函数声明,有一个名为 inc_clients()
的函数它不返回任何值并采用未指定(但固定)数量的未知和未指定类型的参数。它不能是可变参数函数;你必须在这些范围内有一个合适的原型(prototype)(因此,出于所有实际目的,如果你使用 <stdio.h>
或 printf()
或他们的亲戚,你必须包括 scanf()
)。
//Function prototypes
void inc_clients(void);
这是一个原型(prototype);这表示该函数不接受任何参数且不返回任何值(因此必须严格调用它以防止其引起的副作用)。
请注意,C++ 在这一点上与 C 不同;您的原始声明没有 (void)
等同于带(void)
的声明.
关于c - 非套接字或错误文件描述符上的套接字操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13538243/
我使用下拉菜单提供一些不同的链接,但我希望这些链接在同一选项卡中打开,而不是在新选项卡中打开。这是我找到的代码,但我对 Javascript 非常缺乏知识 var urlmenu = docume
我对 javascript 不太了解。但我需要一个垂直菜单上的下拉菜单,它是纯 JavaScript,所以我从 W3 复制/粘贴脚本:https://www.w3schools.com/howto/t
我已经坐了 4 个小时,试图让我的导航显示下 zipper 接垂直,但它继续水平显示它们。我无法弄清楚为什么会发生这种情况或如何解决它。 如果有人能告诉我我做错了什么,我将不胜感激。我有一个潜移默化的
我正在尝试创建选项卡式 Accordion 样式下拉菜单。我使用 jQuery 有一段时间了,但无法使事件状态达到 100%。 我很确定这是我搞砸的 JS。 $('.service-button').
对于那些从未访问过 Dropbox 的人,这里是链接 https://www.dropbox.com/ 查看“登录”的下拉菜单链接。我如何创建这样的下 zipper 接? 最佳答案 这是 fiddle
我正在制作一个 Liferay 主题,但我在尝试设计导航菜单的样式时遇到了很多麻烦。我已经为那些没有像这样下拉的人改变了导航链接上的经典主题悬停功能: .aui #navigation .nav li
如果您将鼠标悬停在 li 上,则会出现一个下拉菜单。如果您将指针向下移至悬停时出现的 ul,我希望链接仍然带有下划线,直到您将箭头从 ul 或链接移开。这样你就知道当菜单下拉时你悬停在哪个菜单上。 知
我有一个带有多个下拉菜单的导航栏。因此,当我单击第一个链接时,它会打开下拉菜单,但是当我单击第二个链接时,第一个下拉菜单不会关闭。 (所以如果用户点击第二个链接我想关闭下拉菜单) // main.js
我正在尝试制作一个导航下拉菜单(使用 Bootstrap 3),其中链接文本在同一行上有多个不同的对齐方式。 在下面的代码中,下拉列表 A 中的链接在 HTML 中有空格字符来对齐它们,但是空白被忽略
我希望有人能帮我解决这个 Bootstrap 问题,因为我很困惑。 有人要求我在底部垂直对齐图像和其中包含图像的链接。 我面临的问题是他们还希望链接在链接/图像组合上具有 pull-right,这会杀
我正在构建一个 Rails 应用程序,并希望指向我的类的每个实例的“显示”页面的链接显示在“索引”页面的下拉列表中。我目前正在使用带有 options_from_collection_for_sele
我有以下 Bootstrap3 导航菜单 ( fiddle here )。我想设置“突出显示”项及其子链接与下拉列表 1 和 2 链接不同的链接文本(和悬停)的样式。我还希望能够以不同于 Highli
我对导航栏中的下拉菜单有疑问。对于普通的导航链接(无下拉菜单),我将菜单文本放在 H3 中,但是当我尝试对下 zipper 接执行相同操作时,箭头不在标题旁边,而是在标题下方。我决定用 span 替换
我是一名优秀的程序员,十分优秀!